What if i set the model upon the success of model.save - javascript

I am doing the following
var model = new Backbone.Model; // some new model
inside collection :
this.bind("change", this.onChange);
...... //
onChange: function( model, options ) {
model.save(null, {
error : function() {
console.log('error');
},
success: function() {
model.set( someNewData );
console.log('done');
}
});
},
In this case the browser sends two requests for updating the data.
First one for model.save() and second for model.set().
Why is this so? In general when i do model.set() it will NOT send the request to server.. But when i do the same inside the success callback in side model.save(), then model.set() also sends the request.
Why this is so?
and how to avoid it?

If you want to prevent the change event being triggered, you can pass a silent: true option to the set method:
model.set(someNewData, {silent: true});
And a demo http://jsfiddle.net/nikoshr/eekdb/6/
If you want the event but still want to cancel the save, you could pass a custom option to the set method. For example, a fromsuccess attribute:
var M = Backbone.Model.extend({
initialize: function() {
this.on('change', this.onChange);
},
onChange: function (model, options) {
if (options && options.fromsuccess)
return;
model.save(null, {
success: function () {
model.set({
data: 'data'
}, {fromsuccess: true});
}
});
}
});
http://jsfiddle.net/nikoshr/eekdb/7/

Related

BackBone collection fetch with parameter making duplicate call and not working

The Backbone model
define('PreferedDealerAddress.Model', function (){
'use strict';
return Backbone.Model.extend( {
urlRoot: '/c.3927030/mazda-mstore-checkout-1-0/service/PreferedDealerAddress.ss'
} );
});
The back bone collection
define('PreferedDealerAddress.Collection', ['PreferedDealerAddress.Model'], function (Model)
{
'use strict';
return Backbone.Collection.extend(
{
model: Model
, url: '../mazda-mstore-checkout-1-0/service/PreferedDealerAddress.ss'
, initialize: function(){
this.fetch({
success: this.fetchSuccess,
error: this.fetchError
});
},
fetchSuccess: function (collection, response) {
console.log('Collection fetch success', response);
console.log('Collection models: ', collection.models);
},
fetchError: function (collection, xhr, options) {
console.log(xhr.responseText);
throw new Error("Books fetch error");
}
} );
});
Now In router I am creating the back bone collection and calling the fetch function by passing the parameter/query string like belwo
var search_params = {
'zip': zip
};
new PreferedDealerAddress.Collection().fetch({data: $.param(search_params)});
But it is making two AJAX call the first one is
/mazda-mstore-checkout-1-0/service/PreferedDealerAddress.ss
/mazda-mstore-checkout-1-0/service/PreferedDealerAddress.ss?zip=92618
and the fetch method considering the first AJAX call which is returning a blank JSON array , due to missing query parameter.
The below is screenshot from firebug.
Please help me, I can use $.getJson() and initialize the collection but that is not the proper way.
That's actually quite simple. Your first fetch is in the initialize method - the one without the data param:
, initialize: function(){
this.fetch({
success: this.fetchSuccess,
error: this.fetchError
});
},
Then your second request is when you instantiate the Collection with the data param included.
So you just need to get rid of the fetch in the initialization.

using backbone with third party api

I'm trying to use backbone to grab hold of an instagram feed. This doesn't require authenticating the user, it is pulling a public feed available through:
https://api.instagram.com/v1/users/<user_id>/media/recent/?client_id=<client_id>
I've gotten as far as outputting the JSON response into the console, but I'm unable to make it display on my page.
In the code below, I use fetchData to grab the feed, and I'd like to eventually get it to a point where render outputs everything stylized on #social. However, despite setting the feed property to the JSON response, render still returns an empty object. console.log in fetchData however displays the proper information.
var social = {}
social.Instagram = Backbone.Model.extend();
social.InstagramFeed = Backbone.Collection.extend({
model: social.Instagram,
url: 'https://api.instagram.com/v1/users/<user_id>/media/recent/?client_id=<client_id>',
parse: function(response) {
return response.results;
},
sync: function(method, model, options) {
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: this.url,
processData: false
}, options);
return $.ajax(params);
}
});
social.InstagramView = Backbone.View.extend({
el: '#social',
feed: {},
initialize: function() {
this.collection = new social.InstagramFeed();
this.fetchData();
this.render();
},
render: function() {
console.log(this.feed);
},
fetchData: function() {
this.collection.fetch({
success: function(collection, response) {
// console.log(response);
feed = response;
// console.log(this.feed);
},
error: function() {
console.log("failed to find instagram feed...");
}
});
}
});
social.instagramview = new social.InstagramView;
I've tried to output the information using just the fetchData function however this.el.append(response) results in a notice saying that el is undefined.
Your render method is called before the fetching has completed. You should bind to the sync event of the collection and call render in the event handler.
social.InstagramView = Backbone.View.extend({
el: '#social',
feed: {},
initialize: function() {
this.collection = new social.InstagramFeed();
this.fetchData();
this.collection.on('sync', function(){
this.render();
}, this);
// this.render();
},
...
})
Quoting Backbone.js documentation : sync event is fired :
when a model or collection has been successfully synced with the server.

Backbone save model issues

I'm trying to save a model and on success, unrender it:
problem is that from within success i can't reference the this reference (which is the view) and I also cannot reference the variable isOk.status that this.model.save(...) returns.
the code:
save: function(e) {
e.preventDefault();
var isOk = this.model.save(null,
{
wait: true,
success: function(model, response){
console.log(response);
console.log(response.status);
},
error: function(model, response){
console.log("error");
console.log($.parseJSON(response.responseText));
$('#errorMessage').empty();
$('#errorMessage').append($.parseJSON(response.responseText).error);
$('#errorApproveModal').modal({
keyboard: true
});
}
});
console.log('logging isOk');
console.log(isOk);
//this one is working! It's on validate event
if(!isOk){
$('#errorMessage').empty();
$('#errorMessage').append("Error: there was an error");
$('#errorApproveModal').modal({
keyboard: true
});
return false
}
console.log(isOk);
**//both those checks are not working for some reason.**
//
if(isOk.status == 200 || isOk.statusText == "OK"){
console.log('in is ok');
this.remove();
}
return false;
}
Btw the view is:
App.Views.User = Backbone.View.extend({
model: App.Models.User
,
save: function...
});
Can someone please help?
Is there a better way to handle the success and error than this method?
Thanks!!
Roy
I'm not sure if this is the proper way to do it but I always just declare a variable referencing this from the view's function, then use that in success. Something like this:
save: function(e) {
// ADD THIS LINE
var me = this;
var isOk = this.model.save(null,
{
....
success: function(model, response){
// USE me IN HERE
me.render(); // e.g
},
....
}
You also can do this:
save: function(e) {
var isOk = this.model.save(null,
{
....
success: function(model, response,options){
// USE me IN HERE
this.options.me.render(); // e.g
},
//ADD me this
me : this
....
}
With the options,you can do all your parameters.

RequireJS and Backbone ajax Issue

I am new to RequireJS and Backbone and was trying to understand why the ajax (fetch) code is not working as excepted.
main.js
require.config({
shim: {
'backbone': {
deps:['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
exports: '_'
}
},
paths: {
'jquery': 'vendor/jquery/jquery',
'underscore': 'vendor/underscore/underscore',
'backbone': 'vendor/backbone/backbone'
}
});
require(['views/appViews'], function(AppView) {
new AppView();
});
AppView.js
define(['jquery', 'underscore','backbone', '../collections/appCollections'], function($, _, Backbone, AppCollections) {
var App = Backbone.View.extend({
initialize: function() {
_.bindAll( this, "render" );
this.collection = new AppCollections;
var $this = this;
this.collection.bind("all", this.render, this);
var x = this.collection.fetch();
/*
* This was not working
this.collection.fetch({
success: function() {
$this.render();
}
});
*/
},
template: _.template( $('#tweetsTemplate').html() ),
render: function() {
console.log(this.collection.toJSON());
//$(this.el).html(this.template({ tweets: this.collection.toJSON() }));
}
});
return App;
});
AppCollections.js
define(['jquery','underscore','backbone','../models/appModels'], function($, _, Backbone, AppModel) {
var AppCollection = Backbone.Collection.extend({
model: AppModel,
url: 'http://search.twitter.com/search.json?q=dog',
parse: function ( response, xhr ) {
return response.results;
},
// Overwrite the sync method to pass over the Same Origin Policy
sync: function (method, model) {
var $this = this;
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: $this.url,
processData: false
} );
return $.ajax(params);
}
});
return AppCollection;
});
AppModel
define(['underscore', 'backbone'], function(_, Backbone) {
var AppModel = Backbone.Model.extend({});
return AppModel;
});
Problem is: the render method is not called once collection is fetched. Also no error in developer tool. So not sure where to look.
Any pointer is helpful.
Thanks
Viral
The success callback is not called because your sync method is not passing it on to ajax.
The third parameter of sync is the options object, which has the success callback in it.
sync: function (method, model, options) {
var $this = this;
var success = options.success;
options.success = function(resp) {
if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
};
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: $this.url,
processData: false
}, options);
return $.ajax(params);
}
This way, ajax will properly call the success callback defined in Backbone Collection's fetch which will in turn call the success callback you passed into fetch.
Then fetch:
this.collection.fetch({
success: function() {
$this.render();
}
});
Here is fetch from Backbone source. You can see it passes the success callback to sync.
fetch: function(options) {
options = options ? _.clone(options) : {};
if (options.parse === void 0) options.parse = true;
var success = options.success;
options.success = function(collection, resp, options) {
var method = options.update ? 'update' : 'reset';
collection[method](resp, options);
if (success) success(collection, resp, options);
};
return this.sync('read', this, options);
},
When you overwrite the sync method in backbone it will not trigger the events properly. Try overwriting the sync method this way
Or, you can simply make your success function look like backbones source:
success = function(resp) {
if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
};
Great response Paul, but just wanted to point out the following:
When attempting to retrieve the data from your ajax call by overriding fetch's success function, I had to make the following modification to your code:
sync: function (method, model, options) {
var $this = this;
var success = options.success;
options.success = function(resp) {
if (success) success(resp);
model.trigger('sync', model, resp, options);
};
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: $this.url,
processData: false
}, options);
return $.ajax(params);
}
Note the difference in the line:
if (success) success(resp);
This was needed in order to properly pass the success function the response, otherwise it was being overwritten by the model. Now, in the success function of fetch, you can output the data:
var $this = this;
this.collection.fetch({
success: function(collection, response, options){
$this.render(response);
}
});
This passes on the ajax data (response) to the render function to do what you like with. Of course, you could also manipulate the data in any which way beforehand as well.
Ideally, I'd like to be able to pass the data into the collection.models object, as Backbone does by default. I believe it has something to do with how the data is being parsed, but I haven't figured it out yet. If anyone has a solution, I'd love to hear it :)
Update:
I've managed to override the parse function and process the JSON data from my ajax call in such a way so as to stay true to the way that Backbone structures its collection object. Here's the code:
parse: function(resp){
var _resp = {};
_resp.results = [];
_.each(resp, function(model) {
_resp.results.push(model);
});
return _resp.results;
}
This creates a new object with an array of your models called results, which is then returned to your fetch function, allowing you to directly access the attributes of each model.

fetch() not calling its Success and Error callback functions (backbone.js)

I have a fetch() in the callback function of $.post(). The fetch() grabs the data from the backend and updates the collection just fine, however when its time to run either its success or error callbacks, nothing happens! I placed console.log()s in both its callbacks and they never appear in the Javascript console.
Any idea what happened?
A method in a View
create_set: function() {
var self = this;
// Post data to server
$.post('api/create_set', {
user_id: $('#user_id').val(),
post_id: this.post_id,
set_name: $('#new_set_name').val()
}, function() {
// Update list of Sets
self.setList.fetch({
data: {
user_id: $('#user_id').val(),
post_id: this.post_id
},
processData: true
}, {
success: function() {
// Highlight the first class in list
$(self.setListView.el).children('div:first').addClass('active');
console.log('success'); // DOESNT RUN!
}
}, {
error: function() {
console.log('error'); // DOESNT RUN!
}
});
console.log('hello'); // RUNS!
});
}
success and error should be the properties of options object that you pass to fetch, you don't have to create separate objects for them:
self.setList.fetch({
data: {...},
processData: true,
success: function(){...},
error: function(){...}
})

Categories

Resources