Backbone.js: Run and display api results on keydown event - javascript

I am programming with Backbone.js I am trying to run an API request when the user types in a query in the search box. However nothing happens when I type in a query.
Here is my JAVASCRIPT:
$(function(){
var SearchList = Backbone.Collection.extend({
url: "https://api.nutritionix.com/v1_1/search/taco?results=0%3A20&cal_min=0&cal_max=50000&fields=item_name%2Cbrand_name%2Citem_id%2Cbrand_id&appId=26952a04&appKey=78e2b31849de080049d26dc6cf4f338c",
initialize: function(){
this.bind("reset", function(model, options){
console.log("Inside event");
console.log(model);
});
},
//** 1. Function "parse" is a Backbone function to parse the response properly
parse:function(response){
//** return the array inside response, when returning the array
//** we left to Backone populate this collection
return response.hits;
}
});
// The main view of the application
var App = Backbone.View.extend({
initialize: function () {
this.model = new SearchList();
this.list = $('#listing');
},
el: 'document',
events: {
"keydown" : "prepCollection"
},
prepCollection: function(){
var name = $('input').val();
var newUrl = "https://api.nutritionix.com/v1_1/search/" + name + "?results=0%3A20&cal_min=0&cal_max=50000&fields=item_name%2Cbrand_name%2Citem_id%2Cbrand_id&appId=26952a04&appKey=78e2b31849de080049d26dc6cf4f338c";
this.model.set("url", newUrl);
this.model.fetch({
success: function (response, xhr) {
console.log("Inside success");
console.log(response.toJSON());
},
ERROR: function (errorResponse) {
console.log(errorResponse)
}
});
this.listenTo(this.model, 'sync', this.render);
},
render: function(){
var terms = this.model;
terms.each(function (term) {
this.list.append("<li>" + term.get('field')["brand_name"] + "</li>")
}, this);
}
});
var app = new App();
});
Here is my HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Interactive Food Guide</h1>
<div>
<input type="text" id="searchBox"> <br/><br/>
</div>
<ul id="listing"></ul>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Backbone and Underscore -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.1/backbone-min.js"></script>
<!-- apps functionality -->
<script src="js/app.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>

url is a property of the model, not an attribute. Think of attributes as things you might persist to the server.
Change:
this.model.set("url", newUrl);
To:
this.model.url = newUrl;
Note that url can also be a function which returns a string, and that there is a default function already there which works for some more typical REST cases: http://backbonejs.org/#Model-url
Also JS variables and object keys are case-sensitive, so your key should be error nor ERROR.
After running the project I noticed a couple of other things wrong:
el: 'document' - This is the whole document including the head, Backbone works with the body or things within it. Fix this by changing it to el: 'body'
You were trying to access the field attribute - it is actually called fields and you can access it like so term.get('fields').brand_name
Other bonus fixes: Clear the list before appending new results, _.throttle prepCollection so that if letters are typed fast then it will only do 2 searches (one at the beginning and one at the end of the input). Change to _.debounce to only do one search at the end of the input.
Fiddle: http://jsfiddle.net/ferahl/2nLezvmg/1/

Related

Knockout bindings are not updating

I'm playing around with an app that gets playlistItem data from the youtube api and lists all titles in the playlist on a page using knockout.js. I can get the list to load just fine, but I'm running into some issues while trying to add some extra functionality to the app.
Things I'm trying to add are a count of the total number of titles in the playlist and an input field that lets the user load another playlist. I currently can get neither to work.
Here's the app.js file:
// global variables
var ytResults=[];
var pageToken;
//var clipCount=ko.observable('');
var clipsDone=0;
var ytPlaylistID='PLpmQJ2D10iJx_GEYNZwAON38cluj0dNj4';
// function to create clip objects
var clip=function(data){
this.clipTitle=ko.observable(data.title);
};
function Model(){
var self=this;
//Create an observable array to store a list of clip objects
self.clipList=ko.observableArray([]);
self.clipCount=ko.observable('');
}
var model =new Model();
function ViewModel(){
var self = this;
self.errorMessage=ko.observable('');
self.loadPlaylist=ko.observable('');
errorHandling=function(){
self.errorMessage("Can't load the list and app");
};
fillList=function(){
for(i=0;i<ytResults.length;i++){
var clipData={
title: ytResults[i]
};
model.clipList.push(new clip(clipData));
};
};
var ytConnector=(function(){
var searchYtRequest=function(requestPayload, callback){
$.ajax({
url: requestPayload.url,
type: requestPayload.method,
}).done(function(data){
//console.log(data);
model.clipCount=data.pageInfo.totalResults;
ytResults.length=0;
for(i=0;i<data.items.length;i++){
ytResults=ytResults.concat(data.items[i].snippet.title);
};
fillList();
pageToken=data.nextPageToken;
clipsDone=clipsDone+50;
if(clipsDone<model.clipCount){
ytConnector.fetchDataFromYt();
};
}).fail(function(jqxhr, textStatus, error) {
// Let empty results set indicate problem with load.
// If there is no callback - there are no UI dependencies
self.errorMessage("Failed to load: " + textStatus + ", " + error);
}).always(function() {
typeof callback === 'function' && callback(ytResults);
});
};
// get playlist data from youtube
function fetchDataFromYt(){
if(pageToken!=null){
thisUrl='https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId='+ytPlaylistID+'&key=AIzaSyBFrCeUpPitoT6eOk_mq6Uza6etWtAH0oQ&pageToken='+pageToken;
}else{
thisUrl='https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId='+ytPlaylistID+'&key=AIzaSyBFrCeUpPitoT6eOk_mq6Uza6etWtAH0oQ';
};
var requestData = {
url: thisUrl,
method: 'GET',
};
searchYtRequest(requestData);
}return{
fetchDataFromYt: fetchDataFromYt,
};
})();
ytConnector.fetchDataFromYt();
thisPlaylist=function(){
ytPlaylistID=self.loadPlaylist();
console.log('playlist ID: '+ytPlaylistID);
};
}
ko.applyBindings(new ViewModel());
// PLpmQJ2D10iJzd1SPy7FlaaBFt07fhLSL3
Here's the html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Telex|Aldrich" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<title>Youtube playlist</title>
</head>
<body>
<div class="container">
<div class="errorMsg" data-bind="text: errorMessage, visible: errorMessage">
</div>
<div class="form-holder">
<form data-bind="submit: thisPlaylist">
playlist ID: <input type="text" size="50" data-bind="value: loadPlaylist, valueUpdate: 'afterkeydown'">
<button type="submit" data-bind="enable: loadPlaylist().length>0">submit</button>
</form>
</div>
<div class="info-holder">
clips in playlist:<span data-bind="text: model.clipCount"></span>
</div>
<ul class="listDiv" data-bind="foreach: model.clipList">
<li data-bind="text: clipTitle">
</ul>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="js/lib/knockout-3.2.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js"></script>
<script src="js/oauth-1.0a.js"></script>
<script src="js/app.js"></script>
</body>
model.clipCound is an observable object, the way you are setting the value is incorrect.
model.clipCount=data.pageInfo.totalResults;
You need to change to this
model.clipCount(data.pageInfo.totalResults);

How to load different pages in O365 mail app based on regex

I am creating an O365 app and I have 2 .aspx files, when the user clicks on the O365 mail app, I want each of these pages to be loaded based on the subject of the mail.
Scenario 1: Mail subject contains '#'
result: load page1
Scenario 2: Mail subject does not contain '#'
result: load page2
I have tried having an intermediate .js file where I have written the logic,
but when I do window.location = "path_to_aspx_file",
only the html is loaded but the js files do not run.
My current implementation:
I have LandingLogic.js
(function () {
"use strict";
//The Office initialize function must be run each time a new page is loaded
Office.initialize = function (reason) {
$(document).ready(function () {
var item = Office.cast.item.toItemRead(Office.context.mailbox.item);
var sub = item.subject;
if (sub.indexOf("some text") > -1) {
window.location = "http://localhost:51776/File1.aspx";
}
else {
window.location = "http://localhost:51776/File2.aspx";
}
});
};
})();
After a bit of fumbling around.
I am able to navigate to each of these files now, but I am not sure how to access the mail subject from File1.aspx and File2.aspx.
Did you initialize the Office context before you using Office JavaScript API to get the subject? To redirect the HTML page easily, we can include the JavaScript like below:
Home.js:
/// <reference path="../App.js" />
(function () {
"use strict";
// The Office initialize function must be run each time a new page is loaded
Office.initialize = function (reason) {
$(document).ready(function () {
app.initialize();
RedirectHTMLPage();
});
};
function RedirectHTMLPage() {
var subject = Office.context.mailbox.item.subject;
if (subject.indexOf("#") != -1) {
window.location.href = "https://localhost:44300/page1.aspx";
} else {
window.location.href = "https://localhost:44300/page2.aspx";
}
}
})();
The HTML page for redirecting:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<title></title>
<script src="../../Scripts/jquery-1.9.1.js" type="text/javascript"></script>
<link href="../../Content/Office.css" rel="stylesheet" type="text/css" />
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
<!-- To enable offline debugging using a local reference to Office.js, use: -->
<!-- <script src="../../Scripts/Office/MicrosoftAjax.js" type="text/javascript"></script> -->
<!-- <script src="../../Scripts/Office/1/office.js" type="text/javascript"></script> -->
<link href="../App.css" rel="stylesheet" type="text/css" />
<script src="../App.js" type="text/javascript"></script>
<link href="Home.css" rel="stylesheet" type="text/css" />
<script src="Home.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
I have tried having an intermediate .js file where I have written the logic, but when I do window.load = "path_to_aspx_file", only the html is loaded but the js files do not run.
Would you mind sharing the detail you using the “window.load”?
Fei Xue answer is correct . if you want to get subject from file2.aspx , add office.js reference and access subject same as file1.aspx inside the Office.initialize event
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>

How to bind dynamic content to angular-toastr?

I'm using angular-toastr, and I want to have a dynamic content inside toastr, let's say, a counter, how can I update it with no other instance.
Here is my angular script:
// Code goes here
angular.module("myApp", ['toastr'])
.controller("myCtrl", myCtrl);
myCtrl.$inject = ["toastr"];
function myCtrl(toastr){
var vm = this;
vm.cont = 0;
vm.start = function(){
//I need create only one toastr with vm.cont update for each increment
toastr.info(vm.cont + " en espera", 'Transferencias y pagos', {
allowHtml: true,
extendedTimeOut: 0,
tapToDismiss: true,
timeOut: 0,
onHidden: vm.listWaitView
});
};
vm.increment = function(){
vm.cont++;
vm.start(); //function that trigger the toastr
};
}
My view:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script data-require="angular.js#1.4.8" data-semver="1.4.8" src="https://code.angularjs.org/1.4.8/angular.js"></script>
<link data-require="angular-toastr#1.3.1" data-semver="1.3.1" rel="stylesheet" href="https://rawgit.com/Foxandxss/angular-toastr/1.3.1/dist/angular-toastr.css" />
<script data-require="angular-toastr#1.3.1" data-semver="1.3.1" src="https://rawgit.com/Foxandxss/angular-toastr/1.3.1/dist/angular-toastr.tpls.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="myCtrl as ctrl">
<h1>Counter</h1>
<h2>{{ctrl.cont}}</h2>
<button ng-click="ctrl.increment();">Increment</button>
</body>
</html>
For your convenience I made a simple script that I uploaded in plunkr:
Example:
https://plnkr.co/edit/w7WbfwyYqkqxQWsAPCZz?p=preview
Are you looking for clearing the existing toasts before creating new one. If yes, then try the below
toastr.clear();
toastr.info(vm.cont + " en espera", 'Transferencias y pagos', {
...
Here is the plunker.
https://plnkr.co/edit/2fnYT6Oi7qzUnhW0KgRo?p=info
You can also set new messages without removing the toast and adding a new one as proposed in the accepted answer.
When creating a toastr message you get an ActiveToast instance returned. On this active toast you can access a ToastRef instance throught the toastrRef property and from there you can get access a property componentInstance (by default an instance of Toast if you do not use your own custom component). On this Toast instance you can set a new message directly as follows:
const toast: ActiveToast = this.toastr.success('Something just happened');
const toastComponent: Toast = toast.toastRef.componentInstance;
setTimeout(
() => toastComponent.message = 'Toast message has changed', 3000
);
This solution was mentioned by MikeAlexMartinez in the following GitHub issue inside the ngx-toastr repository:
Hope it will be useful for others ending up here.

Trapping user input and using as search of Flickr API, need to get button click working

I'm trying to get the user to input a search term, and then use that term as a tag to search Flickr's API. I managed to get the basic search working, but something has gone wrong between that and adding the button click event. Can someone put me right? I feel like it's something little that I'm not seeing, I'm pretty new to jqueryand json.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<script>
$("button").click(function() {
var searchTerm = $("input:text").val();
var flickrApi = "https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?";
$.getJSON(flickrApi, {
tags: "searchTerm",
tagmode: "any",
format: "json"
})
.done(function(data) {
$.each(data.items, function(i, item) {
$("<img>").attr("src", item.media.m).appendTo("#images");
if (i === 3) {
return false;
}
});
});
})
</script>
</head>
<body>
<input type="text" id="search">
<button type="submit">Search</button>
<div id="images"></div>
</body>
</html>
Try removing double quotes in your variable "searchTerm" changing:
$.getJSON( flickrApi, {
tags: "searchTerm",
tagmode: "any",
format: "json"
})
to
$.getJSON( flickrApi, {
tags: searchTerm, // Local variable.
tagmode: "any",
format: "json"
})
Btw, you need to place your code inside a jQuery function. The jQuery functionality will successfully run when the DOM is ready to interact with HTML tags through jQuery/Javascript code.
$(document).ready(function()
{
// jQuery/Javascript code goes here...
});
Or the shorthand for $(document).ready():
$(function()
{
// jQuery/Javascript code goes here...
});
Demo

Creating a model clears the screen

This a code i have made, this is a form that takes Name and Phone Number and has a submit
button and a button to navigate to page named formentries to show the entries. I have created a view and written some HTML stuff and the entries when are input and submit is clicked an alert box shows the input. I need to create a model, collection (and 1 more view) to show all the entries on a different page. But when i create a model
FormView = backbone.Model.extend({
defaults: {
First Name: "Unknown",
Phone Number: "Not Defined
}
});
The form on the index page disappears.
The remaining code is
<html>
<meta charset="utf-8">
<title>BackboneJs Tutorial</title>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-
1.4.2.min.css">
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
</script>
<script src="underscore.js"></script>
<script src="backbone.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone-
localstorage.js/1.0/backbone.localStorage-min.js" type="text/javascript"></script>
</head>
<body>
<div id="form_container">a</div>
<script type="text/template" id="form_template">
<label class="ui-hidden-accessible"><b>First Name</b></label>
<input type="text" id="input_name" placeholder="First Name"/>
<label class="ui-hidden-accessible"><b>Phone Number</b></label>
<input type = "text" id="input_phonenumber" placeholder="Phone Number" />
<input type="button" id="search_button" value="Search" />
Form Entries
</script>
<script type="text/javascript">
FormView = Backbone.View.extend({
initialize: function(){
this.render();
alert("DOM Ready.!!");
},
render: function(){
// Compile the template using underscore
var template = _.template( $("#form_template").html(), {} );
// Load the compiled HTML into the Backbone "el"
this.$el.html( template );
},
events: {
"click input[type=button]": "doAction"
},
doAction: function( event ){
// Button clicked, you can access the element that was clicked with
event.currentTarget
alert( "Form Inputs are " + $("#input_name").val() +" and " +
$("#input_phonenumber").val() );
}
});
var form_view = new FormView({ el: $("#form_container") });
</script>
</body>
</html>
#mrak is right - change First Name and Phone Number to First_Name" and "Phone_Number"
General Pointer
When some JS weirdly stops other, seemingly unrelated parts of your JS from executing, it points to an error in parsing. The V8 interpreter (in webkit at least) just stops and doesn't execute the rest. If you open up your DevTools the console should direct you to the error.
Word of advice
In this case, just remember all JS Objects are associative arrays, and there are some rules governing the characters in the keys of the array. Please see Valid javascript object property names - there are some differences between Dot Notation . and Bracket Notation []. To play safe, use the old-school rules: Avoid special characters and use underscores for spaces. Makes for better looking code as well!

Categories