I load my store as follows:
store.load({
params: {
paramMap
},
callback: function(records, options, success) {
if (success) {
var form = formPanel.getForm();
var jsonStr = Ext.JSON.encode(records[0].raw);
var jsonObj = Ext.JSON.decode(jsonStr);
form.loadRecord(jsonObj);
}
}
});
The thing is I only want this call back to fire the first time the store is loaded. I want to remove it after that so that when I reload or load the store again it doesn't call this callback again.
Any idea, I tried getting the callback in the options config but that doesn't seem to work.
Not exactly sure what the problem is. The callback will only get called when the store is loaded with the callback.
store.load({callback:myCallback});
//callback will be called
store.load();
//callback will not be called
If you want to just do something once you might want to use a listener with single set to true. Listeners instead of callbacks is my preferred way.
store.on('load', onStoreLoad, this, {single:true});
The callback function arguments api is slightly different than a load listener, check the docs to see.
Related
I have a div#game.
In here I want some data from the server to appear.
So when I use the initialize function I request data from my server in my game model with an ajax call.
Game = Backbone.View.extend(
{
id: 'game',
initialize: function ()
{
this.model.on('getGame', function() {});
this.model.getGame();
}
}
Because a callback doesn't seem to work in backbone a trigger has to be made.
So the app listens to the trigger getGame which is triggerd when the data from the server has been returned and saved into a variable in the model.
So far so good, this all works. The only problem now is that I want my div#game to fadeIn when it's done appending all data from the getGame function.
But I think because off the model.on(trigger) the initialize function 'thinks' it's ready after running the getGame() function, without actually having the data from the server appended yet.
So the parent div#all running the following:
this.$el.append(new Game().el);
also 'thinks' the div#game is ready and appends the div#game to itself and gives it a fadeIn.
div#game doesn't contain the server data yet, so when it actually does come back the images and text pop into existence instead of fadeIn in nicely...
Does some one know how to resolve this problem?
Thanks in advance!
======================================================
SOLVED
The problem was in the asynchronous property of the $.get and $.post functions. This is why the initialize was ready before the results had come back from the server. I changed it into a $.ajax and made the async: false. Now it all loads in the order I programmed/want.
Thank you for your detaild explaination Tallmaris, it will come in handy for sure since I'll be using a lot of triggers!
Rather than having the div#all render and append the view to itself, you can pass it as a parent to the View:
this.gameView = new Game({parent: this});
Then in your Game view:
initialize: function (options)
this.parent = options.parent
{
this.model.on('getGame', function() { this.$el.appendTo(this.parent); });
this.model.getGame();
}
Alternatively you could use a global Backbone event object like this:
window.vent = $.extend({}, Backbone.Events);
Now you have a global object (which you can name and put in any namespace as you like, don't just do it as in the code above), that you can use like this:
MAIN VIEW:
initialize: function () {
vent.on("GameReady", function ()
{
// fade in game view
});
}
GAME VIEW:
initialize: function (options)
this.parent = options.parent
{
this.model.on('getGame', function() { vent.trigger("GameReady"); });
this.model.getGame();
}
You can also pass parameters around. Read here for more documentation.
There's a new behaviour in the latest version of Backbone (1.0.0 in which the reset event is no longer triggered by default after fetching a Collection.
http://backbonejs.org/#changelog
Renamed Collection's "update" to set, for parallelism with the similar
model.set(), and contrast with reset. It's now the default updating
mechanism after a fetch. If you'd like to continue using "reset", pass
{reset: true}.
The problem is that I want to capture the event when the collection has been finally fetched (pretty common case, indeed!)
I could listen to add, remove and change event, but if the collection is empty I don't know how to catch the event.
So, what would be the new, recommended way to catch when the collection request has finalized, or is it passing a { reset = true } the only way to achieve it???
ps: here's the original question, BTW can't catch Backbone Collection reset event
From Backbone.sync doc,
Whenever a model or collection begins a sync with the server, a
"request" event is emitted. If the request completes successfully
you'll get a "sync" event, and an "error" event if not.
For example,
var C = Backbone.Collection.extend({
url: '/echo/json/'
});
var c = new C();
c.on('sync', function() {
console.log('sync');
});
c.fetch();
And a demo http://jsfiddle.net/nikoshr/GLATm/
We can pass a method as a success handler when we call fetch on collection and as you said you just want to do something when everything[add,remove,update or reset] has happened, you can do inside this success handler.
collection.fetch({
success: function() {
// Do Something
// This is called when all add, remove and update operations have been done
}
});
Note: success handler is always executed irrespective of you have passed reset:true or not. Irrespective of your collection gets empty or not and It will be called at the last step when all the add,remove and update events have occurred.
Let me know if it does not solve your problem.
My own solution is indeed pretty simple. I already have a BaseCollection with added features, so in there I just set as default { reset: true }. The code should be something like this (my own BaseCollection has a lot of stuff that is not pertinent here):
var BaseCollection = Backbone.Collection.extend({
fetch: function(options) {
options = options || {};
options.reset = (options.reset === undefined ? true : options.reset);
// just call super.fetch
return Backbone.Collection.fetch.call(this, options);
};
});
Using promises...
// you could use promises as well
// P.S.: pardon jquery promises :)
var C = Backbone.Collection.extend({
url: '/echo/json/'
});
var c = new C();
// c.fetch() returns jqXHR object that you can listen too
$.when( c.fetch() )
.done(successFn)
.fail(failFn)
.always(alwaysFn);
I would like to render a view for a model when the model is first fetched but not on every change.
My setup is as follows:
var m = new $.model.Customer({id: customer});
var v = new $.view.GeneralEditView({el: $("#general"), model: m});
m.fetch();
Then in the view initialize I bind the change event to the render method to render when the model is loaded:
this.model.bind('change', this.render);
The problem is that the view then renders on every change. I'd like to only render after the fetch. Unfortunately I'm not aware of any event that's fired after a fetch for a model other than change.
Is there something like 'reset' for collections that I can bind to?
EDIT:
Perhaps to put it more succinctly, for Backbone models is there a way to distinguish when a model is loaded from the server versus changed locally?
There are a bunch of different ways to approach this (these all assume var view = this; somewhere in your view code):
Call .fetch() with a one-time success callback:
m.fetch({
success: function() {
view.render();
}
});
Bind to change but unbind in the handler:
function handle() {
view.render();
view.model.off('change', handle);
}
this.model.bind('change', handle);
Use _.once to limit handler calls:
this.model.bind('change', _.once(function() {
view.render();
}));
Use a .ready() pattern for your models - example here. I like this option in cases where multiple views need to know when the model is loaded, and when you need to want to be able to write the same code without worrying about whether your model is loaded yet. The downside of this is that it requires you to add a model method like .isFullyLoaded() to test every time; the upside is that using a test function, rather than setting a flag, allows models to be loaded in bulk as part of a collection without having to change your code.
Models
You can make the change event specific to a certain key changing, such as the uniqueId (if you have one):
this.model.bind('change:id', this.render, this);
By default, fetch does not fire any event directly, but indirectly fires the change event once new data is loaded using set
If that is not an option, you can always trigger an event in your fetch function:
initialize: function () {
this.model.bind("fetch", this.update, this);
}
fetch: function () {
// do stuff
this.model.trigger("fetch", this);
}
update: function () {
// your refresh stuff here
}
I may have a general solution from https://github.com/documentcloud/backbone/pull/1468#issuecomment-6766096. I overwrote the sync method on Backbone as follows:
Backbone.Model.prototype.sync = function(method, model, options) {
var succ = options.success;
var customSuccess = function(resp, status, xhr) {
//call original
succ(resp, status, xhr);
model.trigger('synced', model, options);
}
options.success = customSuccess;
Backbone.sync(method, model, options);
}
To save the original success method as I don't want to mess that unless I need to, pass the custom success method. When the custom success method is invoked the custom event is triggered as suggested by #Austin and then the original success method in invoked.
I am attempting to override a models save method and set an error callback. I am using a mixture of localStorage and server side data so in the event that the app can't connect to the server, I want to save the model to local storage. Here is my model code:
var Project = Backbone.Model.extend({
urlRoot: Settings.urls.projects.project,
save: function(attributes, options){
options || (options = {});
this.set("last_updated", new Date().toISOString(), {silent: true});
options.error = function(){
console.log("Error callback");
}
return this.constructor.__super__.save.apply(this, arguments);
},
As you can see, I am attempting to set options.error within the save method and then call the super method to actually action the save. For some reason it is ignoring the function and the console log statement is not getting called. Anyone have any ideas?
Check this reference:
http://backbonejs.org/#Model-extend
You need to do something like this:
return Backbone.Model.prototype.save.call(this, attributes, options);
I am in the app.js file. there is a function that gets executed when user logs in to the stytem successfully;
loginSuccess: function() {
this.getViewport().getLayout().setActiveItem(1).store.load(); // Calls the Store of the i need to navigate
this.getViewport().getLayout().setActiveItem(1); // The view i will be navigating
}
I am getting an error stating this.getViewport().getLayout().setActiveItem(1).store is undefined. i think i'm calling the Store the wrong way. how could i correct this ? How can i call the Store from the app.js ?
UPDATE
var st = Ext.getStore('myStore');
st.load();
st.on('load', function() {
this.getViewport().getLayout().setActiveItem(1);
});
Correct way to initiate store loading is:
var st = Ext.getStore('MyStore');
st.load();
just remember that loading is async process, so if you want something to be executed after store is loaded you can't just write a code after load() - you would need to subscribe to the load event.