Re-render backbone view when fetching collection with add:true parameter - javascript

I have backbone collection and under some circumstances I am fetching more models into the collection with:
collection.fetch({data: {...}, add: true});
I need view of the collection to be re-rendered when new members arrive.
"reset" event is not fired because of add:true parameter hence I see two options.
Bind this.render function to collection's "add" event. This is work but makes render function to be called for each new model arrived from server.
Pass silent:true as fetch parameter and call this.render() explicitly in next line, however at next line data still not arrived, hence render function called with old data.
I haven't found any other way to overcome the issue :(
Any advises what should I do in this case?

you can also pass success callback as option to the fetch method and trigger full rerender after successful ajax operation if this is your preferred way of doing it
collection.fetch({data: {...}, add: true, success: function() { ... } });
// or reference to function - you get the drill

Related

Backbone.sync clarification

After reading the docs, this is my understanding of sync.
I instantiate some Backbone.Model and call Collection.create(). create() eventually calls sync() and the Model is POSTed to the server. Then there is a sync in the opposite direction such that the Model on the client is given an id.
Does this update then trigger componentDidUpdate()?
Note: componentDidUpdate is a ReactJS thing, so if that doesn't make sense, the question reduces to "Is the client-side model updated and the view re-rendered?"
Since inside of my componentDidUpdate() I am making a call to save() to keep everything up to date, this subsequently makes a call to sync() which then fires a PUT request (because the Model already has an id).
I'm asking, because in my current application, creating a TodoItem seems to result in a POST and then a PUT which I find redundant. Perhaps it is for an unrelated reason.
It actually fires two POSTS and then two PUTS when adding one item, but that is another question.
The first time you save a model (one which doesn't have an id) it will make a POST, thereafter it will make a PUT (update). I think you are confusing when to use create/add/save:
Use save at any point to save the current client collection/model state to the server
Use add to add Model(s) to a collection (a single Model, an array of Models, or an array of objects which contain attributes and let the collection create them)
Use create as a shorthand for creating a model, adding it to the collection, and syncing the collection to the server.
My guess is that you are calling create and save in one operation - you should be using add and save instead, or just create.
The view will not automatically update for you, you will need to listen to changes or events on the collection/model and update the view yourself - there is no equivalent of componentDidUpdate. For example:
initialize: function() {
this.listenTo(this.collection, 'sync', this.onCollectionSync);
},
onCollectionSync: function() {
this.render();
}

Backbone running a method based on a collection listener

In my application have the following code,
initialize: function() {
Pops.Collections.TeamCollection = this.collection;
this.collection.fetch();
this.collection.on('sync', this.render, this);
},
render: function() {
this.addAll();
return this;
},
Its pretty self explanatory, fetch the collection, once it is synced with the server run the render collection. At the time of writing this sequence of code it seemed like a a good idea however it now looks like that when I save a model of the collection it runs the sync listener and runs render again. This is not the behaviour I want. Is there another listener I can use to listen for the initial fetch being complete?
According to the backbone documentation,
Backbone.sync is the function that Backbone calls every time it attempts to read or save a model to the server.
The event is essentially the "catch-all" for any CRUD operations communicating with the server, which is why it's being fired on saving a model. Looking deeper into the documentation gives clues as to how .fetch() works
When the model data returns from the server, it uses set to (intelligently) merge the fetched models, unless you pass {reset: true}, in which case the collection will be (efficiently) reset.
By revising your call to this.collection.fetch({reset: true}), the collection will load the data and fire a reset event that can be listened to instead of sync. This will solve your problem.

Setting dynamic page titles in Durandal 2.1.0

I use an observable inside the updateDocumentTitle function that gets a new value after completing an ajax request. But I noticed that the updateDocumentTitle function doesn't fire again after the observable changes its value.
userShell.prototype.router.updateDocumentTitle = function(instance, instruction) {
document.title = instance.userArr().name();
};
I tried wrapping the contents of updateDocumentTitle inside a computed observable, but for some reason when I navigate between user pages that are under the same shell (users/100 to users/105), the computed observable gets called as many times as I have navigated between pages without refreshing.
Are there any other successful ways of setting a dynamic document title?
It's a competition between when updateDocumentTitle() fires and when your AJAX request completes successfully.
Where does your AJAX call get made, in which Durandal handler for the viewModel? You have activate, attached, and compositionComplete handlers to choose from.
[EDIT]
You will need to abandon updateDocumentTitle() in this case. Simply create an observable in your viewModel. Update that observable from the activate handler upon successful completion of your AJAX call. Make sure you bind to that observable in your view. We actually do that ourselves for the same reasons you would need to.
For robustness, make sure you provide a default title in the event your AJAX call fails.

Wiring an AJAX call in Knockout.js

Apologies if this has been asked before, I have searched but I'm finding it very hard to express my problem in a search friendly way. And I can't figure it out from the knockout documentation, however it seems like a basic question.
I have 3 select lists and a Knockout view model. Selecting a value in the first list updates an observable in the view model. I then need to make an ajax post, sending that value to the server and retrieving a list of values which I put in an observable array in the view model, which will in turn update the other 2 lists.
I'm happy with wiring up to observables and that part is working fine, my question is how and where to trigger the ajax call.
If I trigger it on the change event of the first select it seems to cause a race condition which means it sometimes gets called before the view model has updated. I could trigger it without using the observable, but that doesn't seem very knockoutish.
If I use a custom binding for retrieving values it will cause the ajax call to be made twice and I can't put the retrieval in a function because it needs to run asynchronously (and it would be called twice).
I feel like I need something which listens to an observable and triggers an ajax call without any visual element.
Any help would be gratefully received.
Triggering things that should happen in response to view model changes generally works through subscriptions in knockout.
function ViewModel() {
var self = this;
self.someValue = ko.observable();
self.otherValue = ko.observable();
self.someValue.subscribe(function (newValue) {
// do something with newValue, like an Ajax request.
// assuming jQuery
$.get("your/url", {val: newValue})
.done(function (data) {
self.otherValue(data);
})
.fail(function () {
alert("could not retrieve value from server");
});
});
}

Where to code inorder to override backbone.sync

I want to override backbone.sync i have already asked this but the problem is i don't quite get it. I need to know where to put the codes if i were to override the sync function.
If i put it on the model like this
model = Backbone.Model.extend({ sync:"" });
Then how should i call it? if i were to use the save method. Also i need to change the methodMap of create from POST to PUT. temporarily i resorted to this 'create': 'PUT', actually editing the backbone.js file ( iknow its not good ). Before i forgot i also need to add this
sendAuthentication = function (xhr) {
xhr.setRequestHeader('Authorization', auth)
};
As a beforeSend parameter since my server has authentication. Again where should i do it? Where should i go and put the codes? in my model? in my collection? or in my views? Any help? THank you.
update
Also can i override the sync on my collection? i mean can i have something like this?
collection = Backbone.Collection.extend({ sync:""});
The strategy behind Backbone framework is to make it simple for editing and flexible for every need. So if you look up the source code you'll find out that every method, which calls Backbone.sync in fact calls first "this.sync".
From the Backbone manual you can read :
The sync function may be overriden globally as Backbone.sync, or at a
finer-grained level, by adding a sync function to a Backbone
collection or to an individual model.
So you have two options
Option One - Replacing global Backbone.sync function
If you override the global Backbone.sync you should place your code in your global application file ( actually anywhere you want, but it must be evaluated ( executed ) at your initial javascript loading, to work as expected
// Anywhere you want
Backbone.sync = function(method, collection, options) {
console.log(method, collection options)
}
This will override Backbone.sync and actually will display on your console what is called every time you call collection.fetch, save, delete, etc.
Here you have no default Methodmap, infact you have nothing else except the arguments :
method - which is a string - 'read', 'create', 'delete', 'update'
collection - which is your collection instance which calls the method
options - which has some success, error functions, which you may or may not preserve.
Debug this in your browser, while reading the Backbone source code, it's very easy to understand.
Option Two - Adding to your model/collection sync method
This is used if you wish to use the default Backbone.sync method for every other model/collection, except the one you specifically define :
mySocketModel = Backbone.Model.extend({
sync : function(method, collection, options) {
console.log('socket collection '+this.name+' sync called');
}
});
Partners = new mySocketModel({ name : 'partners' });
Users = new mySocketModel({ name : 'users' });
Log = new Backbone.Collection;
So if you call Partners.fetch() or Users.fetch(), they won't call Backbone.sync anymore, but yor Log.fetch() method will.

Categories

Resources