Knockout bindings are not updating - javascript

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);

Related

Iteration of model array using _.each in backbone js

I am trying to iterate values using a model array instead of using collections...
JS file:
var Line = Backbone.Model.extend({
attributes : {
data:[]
}
});
var header = new Line({data : ["Backbone JS"]});
var footer = new Line({data: ["Thank You"]});
var lowerList = new Line({data: ["Models","Collections","Views","Events","Routers"]});
var LineView = Backbone.View.extend({
tagName: "li",
render : function() {
var self =this;
_.each(this.model.get("data"),function(item,index){
self.$el.append(item+"<br>");
return item;
});
}
});
var headerView = new LineView({el:"#header",model: header});
headerView.render();
var footerView = new LineView({el: "#footer",model: footer});
footerView.render();
var lowerListView = new LineView({el: "#lowerList",model: lowerList});
lowerListView.render();
HTML File:
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/normalize.min.css">
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/styles.css">
<script src="js/lib/modernizr-2.6.2.min.js"></script>
</head>
<body>
<header id="header">
</header>
<hr>
<ul id="lowerList">
</ul>
<hr>
<footer id="footer">
</footer>
<script src="js/lib/jquery-min.js"></script>
<script src="js/lib/underscore-min.js"></script>
<script src="js/lib/backbone-min.js"></script>
<script src="js/main.js"></script>
</body>
</html>
I want to pass the data to the html page in a list without using the <br> tag in the render function
render : function() {
var self =this;
_.each(this.model.get("data"),function(item,index){
self.$el.append(item+"<br>");
return item;
});
}
without using <br> tag the output is like:
output:
ModelsCollectionsViewsEventsRouters
The values are getting iterated in the same line.
Can you suggest any way to iterate the values one by one without using the <br> tag?
Since your view's el is a <ul> (tagName: "li" is ignored when you pass el option), your view should append <li> to it per item, something like:
var LineView = Backbone.View.extend({
render: function() {
_.each(this.model.get("data"), function(item, index) {
this.$el.append("<li>" + item + "</li>");
}, this);
}
});
A better option would be to use a CollectionView and an ItemView concept:
var LineView = Backbone.View.extend({
tagName: "li",
initialize(options) {
this.item = options.item;
this.render();
},
render: function() {
this.$el.text(item);
}
});
var LinesView = Backbone.View.extend({
render: function() {
_.each(this.model.get("data"), function(item, index) {
var childView = new LineView({item: item});
this.$el.append(childView.$el);
});
}
});
Even better option would be to actually use a Backbone.Collection instance for the Collection view

Any Event Listener

Are there any Event Listeners that can be attached to a word. So when the word is clicked, information like a definition can be displayed on the page. Using jQuery
Thanks,
Adam
Sorry for not posting code. I have to make it so that when the user clicks on the name of a person in the list, the box of data on the right side of the screen fills with the description of the location of the artwork. Which is in my JSON file.
Here is my code so far
<!DOCTYPE html>
<hmtl lang="en">
<head>
<meta charset="utf-8" />
<title>AJAX</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<script src="jquery.js" type="application/javascript"></script>
<script src="ajax.js" type="application/javascript"></script>
</head>
<body>
<div id="loaded-data"></div>
<div id="result-box"></div>
</body>
</hmtl>
$(function() {
let request = $.ajax({
method: 'GET',
url : 'people.json',
dataType: 'json',
});
request.done(function(data) {
let list = data.body.list;
let resultBox = $('#result-box');
let unorderedList = $('<ul>');
resultBox.append(unorderedList);
for (let person of list) {
let listItem = $('<li>');
listItem.text(person.name);
listItem.attr('data-url', person.links[0].href);
unorderedList.append(listItem);
}
});
request.fail(function(response) {
console.log('ERROR: ' + response.statusText);
});
});
{
"links":[{"rel":"self","href":"http://www.philart.net/api/people.json"},{"rel":"parent","href":"http://www.philart.net/api.json"}],
"head":{"title":"People","type":"listnav"},
"body":{
"list":[
{"name":"Adam","links":[{"rel":"self","href":"http://www.philart.net/api/people/325.json"}]},
{"name":"Abigail Adams","links":[{"rel":"self","href":"http://www.philart.net/api/people/157.json"}]},
{"name":"John Adams","links":[{"rel":"self","href":"http://www.philart.net/api/people/410.json"}]},
{"name":"Samuel Adams","links":[{"rel":"self","href":"http://www.philart.net/api/people/439.json"}]},
{"name":"Lin Zexu","links":[{"rel":"self","href":"http://www.philart.net/api/people/347.json"}]},
{"name":"James A. Zimble","links":[{"rel":"self","href":"http://www.philart.net/api/people/345.json"}]},
{"name":"Doris Zimmerman","links":[{"rel":"self","href":"http://www.philart.net/api/people/171.json"}]}
]
}
}
Teemu has already mentioned a way to accomplish this behavior in the comment. You can do it as follows
// handle click and add class
$(".word").click(function() {
var word = $(this).text()
alert(word);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<p class="word">Hello</p>
</div>
You could replace the word and wrap it with a div.
$('p').html(function(index, value) {
return value.replace(/\b(here)\b/g, '<div class ="event">here</div>');
});
$('.event').click(function() {
console.log('definition');
});
<p>This is the information: Click here.</p>

Correct way to append data into existing data table using ajax

Introduction
I am working with the functions where user search donor organizations by name.
Data loads in DataTable, paging enabled and works fine for the initial data load.
(Data load with initial call from jquery is about 100 records)
Lately, i have tried to implement the ajax method, which is suppose to load "next 100 records" and append to the existing records(now record reaches at 200 aprox).
Problem
Record loading on ajax call is loaded into datatable but displays this recent record on current page(no paging applied on it).
When user change the page to navigate between records, this recent record disappear.
I am just manipulating DOM elements, i think i have to pass it to datatable, yes?
Complete Code(just copy and paste whole code to test,cdn libs used)
<!DOCTYPE html>
<!--[if IE 8]> <html lang="en" class="ie8"> <![endif]-->
<!--[if IE 9]> <html lang="en" class="ie9"> <![endif]-->
<!--[if !IE]><!-->
<html lang="en">
<!--<![endif]-->
<head>
<title>Demo : Test</title>
<!-- Meta -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.9/css/jquery.dataTables.min.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-3">
<form>
<input type="text" id="searchParam" name="searchParm" placeholder="enter search param">
<br>
<input type="submit" value="Submit" onclick="searchDonors(document.getElementById('searchParam').value); return false;">
</form>
<br />
</div>
<div class="col-md-9">
<div id="demoApim"><table id="demoApi"><thead><tr><td>Organization Name</td><td>Address</td></tr></thead><tbody id="tBody"></tbody></table></div>
</div>
<div class="col-md-3" id="searchBtn"><input type="submit" value="Load Next 100 Records" onclick="loadNext(); return false;"></div>
</div>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.9/js/jquery.dataTables.min.js"></script>
<script type="text/javascript">
var count;
$('#searchBtn').hide();
$(document).ready(function () { $('table').hide();});
function searchDonors(searchParam) {
window.searchDonorsParam = searchParam;
count = 100;
var request = new XMLHttpRequest();
request.open("GET", "http://graphapi.firstgiving.com/v1/list/organization?q=organization_name:" + searchParam + "*%20AND%20country:US&&page_size=100&page=1", false);
request.send();
var xml = request.responseXML;
//$.each(xml, function (key, val) {
var oName = xml.getElementsByTagName("organization_name");
//console.log(oName);
var oAddress = xml.getElementsByTagName("address_line_1");
var counts = xml.getElementsByTagName("organization_name").length;
for (var i = 1; i < counts; i++) {
var html = [];
html.push('<tr><td>', oName[i].innerHTML)
html.push('</td><td>', oAddress[i].innerHTML)
html.push('</td></tr>')
$("#tBody").append(html.join(''));
}
$('#demoApi').DataTable();
$('table').show();
$('#searchBtn').show();
//});
//console.log(oName);
//console.log(oAddress);
}
function loadNext()
{
if (count = 100)
{
$.ajax({
url: "http://graphapi.firstgiving.com/v1/list/organization?q=organization_name:" + searchDonorsParam + "*%20AND%20country:US&&page_size=100&page=2",
method: "GET",
dataType: "xml",
success: function (xml) {
var xmlDoc = $.parseXML(xml),
$xml = $(xmlDoc);
console.log(xml.getElementsByTagName("organization_name"));
var oNameMore = xml.getElementsByTagName("organization_name");
var oAddressMore = xml.getElementsByTagName("address_line_1");
var countsNew = xml.getElementsByTagName("organization_name").length;
var html;
for (var i = 1; i < countsNew; i++) {
html = [];
html.push('<tr><td>', oNameMore[i].innerHTML)
html.push('</td><td>', oAddressMore[i].innerHTML)
html.push('</td></tr>')
$("#tBody").append(html.join(''));
}
},
error: function () {
console.log("call failled");
}
});
}
}
</script>
</body>
</html>
If someone have idea about that problem please let me know, any kind of help or reference will be appreciated.
"I think i have to pass it to datatable, yes?". Yes. The correct way is to go through the API. Without using the API, dataTables cannot be aware of whatever changes you have made to the underlying <table> and therefore your recent records disappear :
var table; //outside your function scopes
in searchDonors() :
table = $('#demoApi').DataTable();
in loadNext() use row.add() instead of injecting markup to <tbody> :
for (var i = 1; i < countsNew; i++) {
table.row.add([oNameMore[i].innerHTML, oAddressMore[i].innerHTML]);
}
table.draw();
yes ofc modify DOM its not enought for datatables, you need to use datatables function to access data, use this:
initialize the table:
var myTable = $('#demoApi').DataTable();
then
myTable.row.add( [oNameMore[i].innerHTML,oAddressMore[i].innerHTML] );
all the data are stored inside datables settings object,
updating the DOM don't change the current table settings so you will
lose you change after any table redraw ( search, change page, ecc.. )

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

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/

Javascript error: Uncaught TypeError: Object [object Object] has no method 'src'

I'm using the following code to take images from a parse.com class and return them to the page inserted within a div.
At the moment I get a Uncaught TypeError: Object [object Object] has no method 'src'
and no images are being returned.
The images are stored in the class Gbadges in parse.com and as a string (URL) in the column.
I cannot find an complete match on SO or google to this issue. I presume its something to do with the image url?
Please note that this code is is based on backbone.js framework, which lets you ebed script tags into your html 5 code.
I've created a fiddle here http://jsfiddle.net/Dano007/cQgJG/
<!doctype html>
<head>
<meta charset="utf-8">
<title>My Parse App</title>
<meta name="description" content="My Parse App">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/styles.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://www.parsecdn.com/js/parse-1.2.17.min.js"></script>
</head>
<body>
<div id="main">
<h1>You're ready to use Parse!</h1>
<p>Read the documentation and start building your JavaScript app:</p>
<ul>
<li>Parse JavaScript Guide</li>
<li>Parse JavaScript API Documentation</li>
</ul>
<div style="display:none" class="error">
Looks like there was a problem saving the test object. Make sure you've set your application ID and javascript key correctly in the call to <code>Parse.initialize</code> in this file.
</div>
<div style="display:none" class="success">
<p>We've also just created your first object using the following code:</p>
<code>
var TestObject = Parse.Object.extend("TestObject");<br/>
var testObject = new TestObject();<br/>
testObject.save({foo: "bar"});
</code>
</div>
</div>
<script type="text/javascript">
Parse.initialize("79tphN5KrDXdjJnAmehgBHgOjgE2dLGTvEPR9pEJ", "9lblofQNZlypAtveU4i4IzEpaOqtBgMcmuU1AE6Y");
var TestObject = Parse.Object.extend("TestObject");
var testObject = new TestObject();
testObject.save({foo: "bar"}, {
success: function(object) {
$(".success").show();
},
error: function(model, error) {
$(".error").show();
}
});
var GlobalBadges = Parse.Object.extend("GBadges");
var query = new Parse.Query(GlobalBadges);
query.exists("Global_Badge_Name");
query.find({
success: function(results) {
// If the query is successful, store each image URL in an array of image URL's
imageURLs = [];
for (var i = 0; i < results.length; i++) {
var object = results[i];
imageURLs.push(object.get('Global_Badge_Name'));
}
$('#Image01').src(imageURLs[0]); //first image
$('#Image02').src(imageURLs[1]); //second image
$('#Image03').src(imageURLs[2]); //third image
},
error: function(error) {
// If the query is unsuccessful, report any errors
alert("Error: " + error.code + " " + error.message);
}
});
</script>
<div >
<img id="Image01"/>
<img id="Image02"/>
<img id="Image03"/>
</div>
</body>
</html>
</body>
</html>
You need to use attr() to set the value of src attribute.
$('#Image01').attr('src',imageURLs[0]);
It is because the javascript is before the elements. Place the javascript below the elements (Image elements) and it should work fine.

Categories