KnockoutJS/AJAX update viewmodel - ajax

Currently playing about with KnockoutJS. Just trying to update an observable array from a ajax/json feed (using twitter) in this example.
It seems to lose scope of what "this" is when trying to update my observable array (currentTweets). I've tried adding bind to various places but no such luck.
The error I get is: Uncaught TypeError: Cannot call method 'push' of undefined
I'm sure I am doing something stupid, here it is in action (not much to look at!)
http://jsbin.com/oyuteb
I've read a lot about Knockout mapping but don't feel confident enough to take that on yet!
So, any help or guidance would be fab.
Thanks

The simplest way to solve this is to proxy your viewmodel's "this" into another variable so it is available inside handlers. When jquery ajax calls the success handler, the context is different so this refers to something else.
So you would have
function twitterViewModel() {
var self = this;
this.currentTweets = ko.observableArray([]);
...
this.getTweets = function(){
$.ajax({
dataType: 'jsonp',
url: 'http://search.twitter.com/search.json?callback=?&q='
+ this.searchTerm() + '&rpp=50',
success: function (data) {
$.each(data.results, function(i,tweet){
self.currentTweets.push({'keywords' : 'bugger'});
});
}
});
}
}
You can eliminate the bind calls and use self instead.
Hope this helps.

Related

Ajax request in Backbone View render function

I want to pass an extra variable (the userid) with before rendering my backbone view. I am getting the the extra variable with a ajax request but because is asynchronous I think my page is being rendered before I get the variable. For simplicity lets say I have this in my backbone view :
PostsApp.Views.Post = Backbone.View.extend({
template: _.template($('#post-template').html()),
render: function(){
var newObject = this.model.toJSON();
$.ajax({
url:"/user",
success:function(result){
newObject.twittername = result.name; ;
}
});
this.$el.html(this.template(newObject));
}
});
I guess I can put this.$el.html(this.template(newObject)); in the callback but than 'this'refers to something else.. Can anyone think of a work around for this?
Or is it completely very bad to send such a request in the render function..
You are correct in your assumption. It will not render correctly.
The simple fix
Just like the more general case, you can perform the actual rendering inside the success callback.
render: function(){
var newObject = this.model.toJSON();
var that = this; // to fix that `this` refers to in the callback
$.ajax({
url:"/user",
success:function(result){
newObject.twittername = result.name; ;
that.$el.html(that.template(newObject));
}
});
}
The better fix
What I'd do is:
Have the twitter name as a part of the model
fetch it from the model (maybe even with a restful .fetch
listen to change events in the view, and call render on such events.
This is because the view should not be responsible for changing model data in Backbone. It's mixing "business logic" with presentation and it can get ugly pretty fast.
[I think this example 2 from Addy Osmani's "Backbone Fundamentals" should give you a general idea on how this sort of structure is laid out.

How to properly clear the content of an Ember.Enumerable?

I was looking at Emberjs recently and found this useful article written by one of its main contributors: Advice on & Instruction in the Use Of Ember.js
It walked me through an example which fetch a list of user data from a server and render them on screen. I'll briefly explain how it worked:
The app contacts the server to fetch a list of user data though ajax
call.
At the end of the ajax call an empty enumerable is returned
immediately, which is later used as a property of a controller.
Once the ajax call is completed, it populates the enum with data which
in turns update the controller's property, and finally triggers an
automatic re-rendering.
This works fine as long as the list is not revisited. As a user revisit the list, say he/she navigates to another state and then comes back, the logic will be triggered again, fetching the data from server and populates the list. However, the list this time is not empty! Thus we have a list of duplicated data. I would like to resolve this by clearing the content of the list when the ajax call is successful. Below is the code for the ajax call:
allAwesomebergs: [],
fetch: function(){
$.ajax({
url: 'https://api.github.com/repos/emberjs/ember.js/contributors',
dataType: 'jsonp',
context: this,
success: function(response) {
response.data.forEach(function(awesomeberg){
this.allAwesomebergs.addObject(App.Awesomeberg.create(awesomeberg))
}, this);
}
});
return this.allAwesomebergs;
},
The above code does not clear the content of the list. I tried adding a line "allAwesomebergs = []" at the beginning of the success function, but what I got was just a blank screen. I thought I may not be doing this correctly, but I looked at the document from Ember and didn't see anything about clearing the content of an Enumerable.
Thus the question is: what is the easiest way to resolve this duplicate loading issue? Clearing the content before hand seems the most obvious but I can't make it work.
You can call clear() before you start adding the new objects. See this documentation.
New code would be:
allAwesomebergs: [],
fetch: function(){
$.ajax({
url: 'https://api.github.com/repos/emberjs/ember.js/contributors',
dataType: 'jsonp',
context: this,
success: function(response) {
this.allAwesomebergs.clear();
response.data.forEach(function(awesomeberg){
this.allAwesomebergs.addObject(App.Awesomeberg.create(awesomeberg))
}, this);
}
});
return this.allAwesomebergs;
},
I think your approach was ok, but it should have been:
this.allAwesomebergs = []
It is all about the this in front of it. So clear is not needed here.

Return false not working for jQuery live

Well this has me well and truly stumped. After searching for the last few hours I still cannot seem to work out where I am going wrong.
I am trying to append an AJAX response to a container when it gets clicked. That works fine but I don't want it to append another object when the elements from the AJAX response also gets clicked.... so:
<div id="container">
<!-- AJAX response to get inserted here, for example -->
<span id="ajaxResponse"></span>
</div>
Here is my script:
$('#container').click(function(e) {
var current_el = $(this).get(0);
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
}
});
return false;
});
So it works fine but for some reason the click event on #container also fires when I click on the AJAX response span!?
According to jQuery documentation:
To stop further handlers from
executing after one bound using
.live(), the handler must return
false. Calling .stopPropagation() will
not accomplish this.
But unless I am mistaken, I am calling false? :(
Anyone help me out on this?
UPDATED:
So the only way I can get it to work is by updating my code to this:
$('#container').live('click', function() {
var current_el = $(this).get(0);
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
}
});
});
$('#ajaxResponse').live('click', function(e) {
return false;
});
This seems a little messy though... anyone have a better solution?
Where is live part you mention in the title of the question ?
It is how the event model works.. If you click on element which does not handle the event, the event will travel up the DOM hierarchy until it finds an element that handles the click (and stops its propagation..). Otherwise you would not be able to put an image inside a <a> tag and click on it..
You can bind a canceling handler on the inner element assuming you have someway to target it..
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
// assuming the returned data from ajax are wrapped in tags
$(current_el).children().click(function(){ return false;});
}
});
I think the return false is referring to something else in this case...
you should try calling stopPropagation() - this should stop the "click" function from propagating down to the ajaxResponse span....
One option that you may want to try is switching over to using live(). Essentially, the click event you setup is calling bind(), and the solution you referenced is using live() which is a variation on bind().
For example:
$('#container').live("click", function(e) {
var current_el = $(this).get(0);
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
}
});
return false;
});
HTH

JQM (jQueryMobile) problem with AJAX content listview('refresh') not working

This is a mock of what I'm doing:
function loadPage(pn) {
$('#'+pn).live('pagecreate',function(event, ui){
$('#'+pn+'-submit').click( function() {
$.mobile.changePage({
url: 'page.php?parm=value',
type: 'post',
data: $('form#'+pn+'_form')
},'slide',false,false);
loadAjaxPages(pn);
});
});
function loadAjaxPages(page) {
// this returns the page I want, all is working
$.ajax({
url: 'page.php?parm=value',
type: 'POST',
error : function (){ document.title='error'; },
success: function (data) {
$('#display_'+page+'_page').html(data); // removed .page(), causing page to transition, but if I use .page() I can see the desired listview
}
});
}
in the ajax call return if I add the .page() (which worked in the past but I had it out side of the page function, changing the logic on how I load pages to save on loading times), make the page transition to the next page but I can see the listview is styled the way I want:
$('#display_'+page+'_page').html(data).page();
Removing .page() fixes the transition error but now the page does not style. I have tried listview('refresh') and even listview('refresh',true) but no luck.
Any thoughts on how I can get the listview to refresh?
Solution:
$.ajax({
url: 'page.php?parm=value',
type: 'POST',
error : function (){ document.title='error'; },
success: function (data) {
$('#display_'+page+'_page').html(data);
$("div#name ul").listview(); // add div wrapper w/ name attr to use the refresh
}
});
Be sure to call .listview on the ul element
If it didn't style earlier, you just call .listview(), bot the refresh function. If your firebug setup is correct, you should have seen an error message telling you that.
I didn't have time to get down to creating some code before you posted your fix, but here's a little recommendation from me:
if(data !== null){ $('#display_'+page+'_page').html(data).find("ul").listview() }
This is a bit nicer than a new global selector. Also - you don't need the div and you can provide a detailed selector if you have multiple ULs.
caution: the above code requires data !== null. If it's null - it will throw an error.
If you add items to a listview, you'll need to call the refresh() method on it to update the styles and create any nested lists that are added. For example:
$('#mylist').listview('refresh');
Note that the refresh() method only affects new nodes appended to a list. This is done for performance reasons. Any list items already enhanced will be ignored by the refresh process. This means that if you change the contents or attributes on an already enhanced list item, these won't be reflected. If you want a list item to be updated, replace it with fresh markup before calling refresh.
more info here.

How do I associate an Ajax error result with the original request?

I am sending data to the server in small bursts using the jQuery ajax function. Each chunk of data is a simple key-value map (the data property passed to the ajax function).
If a request fails, how can my error event handler identify which specific set of data failed to be uploaded?
The function signature of the jQuery error handler is:
error(XMLHttpRequest, textStatus, errorThrown)
Neither textStatus or errorThrown seem useful to identify the failed set of data, and the XHR doesn't seem to offer that capability either.
Simply put, if a request fails, I want to be able to get the id property of the data that I passed to the ajax function. How can I do this?
Check this... From the docs - pay attention to the highlighted section:
The beforeSend, error, dataFilter, success and complete options all take callback functions that are invoked at the appropriate times. The this object for all of them will be the object in the context property passed to $.ajax in the settings; if that was not specified it will be a reference to the Ajax settings themselves.
So, in the error() callback (also success and complete, etc), this will be the object passed to $.ajax() - unless you are using the context parameter to change it.
As a note, the data param passed into $.ajax() will be converted to a serialized string for a GET or POST request
The other option, is to just make sure your error() callback has access to the variable in the same scope:
(function() {
// create a scope for us to store the data:
var data = {id: 1};
$.ajax({
url: '/something-to-genereate-error',
data: data,
error: function() {
console.log(this, data);
}
});
})(); // call the function immediatey
See this fiddle for an example - Note that the data inside of this is "id=1" wereas the data we held a copy of is still {id:1}
Also - Note that the closure here ((function() { .... })()) Is pretty much unnecessary, it is just showing a way to store the data variable before passing it into $.ajax() and the using it in the callback. Most likely this ajax call already lies within a function where you could do this.
Ok, I think I found it (based on my previous answer and the one from gnarf,
$.ajax({
type: "POST",
url: "not-found",
context: {id: 123, name: "hello world"},
error: function(){
console.debug($(this)); //only works in chrome
}
});

Resources