backbone.js adding a custom sync to make a loading effect - javascript

i'm building a app with backbone.js, now i wanna adding a global loading effect when backbone.js is making http request to the server, this is a single page webapp, so there are lots of asynchronous http requests. I think i can modify the Backbone.sync method to make this easy. How can i do this?
Post a sample of code
// let's say there is a function to generate the loading and remove it
// mask.create();
// mask.remove();
var BookList = Backbone.Collection.extend({
model:Book,
url:'/api/list/1',
});
var list = new BookList();
list.bind('reset', function(){
$('.content').html('');
list.each(function(book){
self.addOne(book);
})
});
list.fetch();

It sounds like you're looking to connect your display to the sync for UI feedback. You don't want to muddling concerns though so you don't want the sync itself to care about the presentation. The default sync implementation provides some events (and there's likely more covered in the docs):
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.
So you could start with binding to those events, but either way you should stick to some kind of approach where "sync" remains focused on its responsibility.

#MattWhipple is absolutely correct about separation of concerns: you shouldn't mix UI logic to the persistence layer. However that doesn't mean that you shouldn't override the Backbone.sync method, as long as you provide a decoupling mechanism to separate the concerns.
One such mechanism is the mediator pattern, i.e. using a global event bus to publish and consume messages. As it happens, Backbone provides just such a mechanism with the evented global Backbone object. You can override your sync to trigger an event:
Backbone.trigger("request:start");
And listen to it in the UI layer:
Backbone.on("request:start", callback);
In this way neither layer has to know of each other, and you can still achieve the global request handling.
Furthermore, if all you want is to catch all AJAX requests and you don't care whether they originated from Backbone, you can drop one level lower to listen to jQuery's global events and leave Backbone out of it altogether.

Related

Should the Router be responsible for network requests? (Backbone.js)

I'm in disagreement with a co-worker about the responsiblities of a Router. Inside of our single page application, it's my impression to have network requests (AJAX) handled via events being triggered (i.e. Backbone.Events), like so:
events : {
'click a#getUsers' : 'updateModels'
}
updateModels: function() {
$.ajax(); or this.Model.fetch();
}
Yet it is his understanding that network requests should be handled via the router on URL change (depending on a click which changes the url like a[href="#getThings"] like so:
var App = Backbone.Router.extend({
routes: {
"" : "main",
"thing" : "getThings"
}
getThings: function() {
this.newView = new NewView();
$.ajax(); // which populates the view with data
}
});
I wanted to know what the backbone reason is for doing network requests.
The vast majority of your AJAX requests should start with a backbone model or collection and be handled via the backbone.sync function. The key methods are model.fetch(), collection.fetch(), and model.save().
Backbone is flexible and only handles a very primitive set of use cases, so there are cases when you need to step outside these boundaries, but any call to $.ajax in a view or a router is a strong code smell of MVC failure.
To address some of your specifics.
Can a view call model.fetch() to load data?
Yes, this is perfectly fine and idiomatic backbone.
Can a view emit an event which will eventually load data?
Yes, this is perfectly fine. This is only necessary beyond just calling model.fetch() as your application grows in complexity and the decoupling that event emitting allows is valuable and warranted. For a todo list, it's usually overkill. For a huge application, this can be a clean approach.
Does every model fetch cause a change in the browser URL?
No. In fact many single page applications simply live at "/" forever. Routing to unique URLs is optional. Some applications lend themselves easily to it, others not so much. Don't equate "the application needs data X" with "the browser URL needs to be X". These are orthogonal. If a user clicks on the view for "Bill Clinton" in a list of presidents and you want to change the URL to "/presidents/clinton", then go ahead and fire a new route and that makes perfect sense, but sometimes you just want some data without changing the URL.
To summarize the responsibilities of the router, I think of it as follows:
Based on the URL, which View is supposed to be displayed?
Also based on the URL, are there model IDs that need to be extracted and instantiated into model instances?
Wire things up and render the view
So a typical router method pseudocode might be:
when the URL matches /presidents/:name, respond as follows
grab the :name parameter and make a President model
instantiate a PresidentView, passing it the model
call presidentView.render() and swap the view's element into the DOM at the appropriate spot in the overall page layout
With Backbone, you should let Backbone be doing most of the ajax requests for you, which will be more often than not triggered from your view, not the router. And I would never use a jQuery ajax request when you could use a Backbone request instead. this.model.fetch() is the right way to get the model. I see the router as a way to orient your page, but the finer details should be left to the views. However, choosing between logic in the router vs. in the views/collections/models is more art than science, and I would take a look at other Backbone examples to get more guidance. I think, in a lot of cases, the network requests are handled in the views because it's easier.
In response to your comment below, whenever data is returned from the server, Backbone uses parse to get the information it needs, so rewriting fetch would be a poor decision. You can overwrite the parse method for your model to get the information that you want, and nothing more. i.e.:
var YourModel = Backbone.Model.extend({
parse: function(response) {
//You can manipulate the object in other ways as well,
//but here's how you'd delete info
delete response.junk;
return response;
}
});
Similarly, every time you do this.model.save(), it uses toJSON() before the model gets sent to the server, which you can also overwrite:
toJSON: function() {
// default -> return _.clone(this.attributes);
return _new code here_;
}

backbone model get attribute within initialize method

I've looked for a solution to this but cannot find anything that works.
Note: I am overriding the Backbone.sync method globally.
When I instantiate a Session model I pass a number of attributes to it. These, as you would expect, should be "gettable" via mySessionInstance.get('someAttribute'). The session model calls this.fetch() within the initialize method. My custom Backbone.sync method needs to access some of the session attributes but when it runs I get an error saying the "get" method does not exist for that instance.
It's as if I was trying to access .get for my session instance before it was fully initialized. Any ideas on how to fix it? I wish there was a "ready" or "initialized" event for models so I could just listen for that and ensure certain things only happen when the model is truly done initializing.
Thanks,
Luis
There are two events that a model fetch will trigger:
"change" events will be triggered if anything is changed (i.e. if anything came back from the server). A fetch is mostly a set that is called by an AJAX success handler after all.
A "sync" event will be triggered when the model is synced with the server, syncing includes fetching. The fetch docs aren't explicit on this but you can easily see it in the source and the master events list notes it.
The "change" event is probably more useful in general and should serve to indicate that the model is ready for use.
If you just need a one-time notification, you could use use the success callback from the fetch:
model.fetch({
success: function(model, response) {
// ...
}
});

How to bind server side events on client objects and vice versa with meteor

Is it possible to directly bind server side events to client side objects in meteor?
I would like to update a view for example when a server side event triggers. On the other hand I'd like to fire a server side method when a user clicks a view item.
I could use Meteor#methods for all the events but that seems odd.
Or can I specify an eventhandler for example using EventEmitter outside the client- and server-scope so that it is available on both sides and trigger/bind events ob that very object?
Some confused about that I am thankful for hints into the right direction.
Regards
Felix
Update:
Using Meteor#methods works great in case user events should be mapped to server side actions. The other way around is still unclear. Asynchronous actions on serverside could persist their results in a collection which is pub/sub'ed to the client, which in turn could update some view due to the reactive context. But thats odd, cause persisting that kind of info is slow, wasted space and time. Any suggestions?
I believe you can use the Collection.observe on the server side to 'observe' events on the Collection as clients are inserting, updating, removing, etc... That might be a start if you are focused on Collections alone. I used it like a sort of server side event loop to watch for collection changes.
When a user clicks on something in a view try binding a Template Event to the View css selector and then calling a Meteor method which will notify the server of the event. See the examples of binding a key handler and/or button handlers to a Template.entry.event which then call a Meteor method notifying the server that something happened.
What about storing the progress in Session? You could do something like this:
Template.progress.value = function() {
return Session.get('progress');
}
Then, whenever you update the Session on the server, the client template will automatically get those changes.
Out of curiosity, how exactly are you performing asynchronous actions on the server? I'm still trying to figure that out.

Are both the 'sync' event and Backbone.sync connected in some way -- what is the difference between the two?

What is the difference between 'sync' event and Backbone.sync?
...and what are they each specifically?
There is no connection between the two, although they are both related to the task of syncing data to the server.
Backbone.sync implements the persistence strategy utilized by your Backbone.js app. If you need to change something about the way your data is stored, you are welcome to override the default implementation. Most people won't need to worry about this, but if you need to do this you might want to check out How to override Backbone.sync?
The sync event indicates an update to a model has been successfully synchronized with the server. Like other model events, it will bubble up to the collection as well. This happens when:
Successfully saving a model.
Successfully destroying a model.
Successfully creating a model in a collection.
Note: Prior to v0.9.9, the sync event was not guaranteed to fire - if you declared a success callback during any one of these operations, the sync event would not be triggered.

Are there any Backbone.js tutorials that teach ".sync" with the server?

I read many Backbone.js tutorials, but most of them deal with static objects.
Of course, I have data on the server. I want a tutorial that shows how backbone.js can communicate with the server to fetch data, post data, etc.
This is .sync, right? I read the backbone.js documentation, but still fuzzy on how to use this feature.
Or can someone show me an example?
According to: http://documentcloud.github.com/backbone/#Sync
Backbone.sync is the function that Backbone calls every time it
attempts to read or save a model to the server.
But when? Where do I put the function? I don't know how to use it, and the documentation doesn't give any examples. When does the data get loaded into my models? I get to define when...right?
You never really have to look at .sync, unless you plan to overwrite it. For normal uses, you can simply call model.save() whenever you want and that will execute a post or put (depending on whether the record exists already). If you want to get the data from your backend, use collection.fetch()
You'll of course also need to specify a URL, do so through your collection attribute, collection.url
You can override Backbones native sync functionality if you override it:
Backbone.sync = function() {
//Your custom impl here
}
After that this function is called whenever you call a backbone function like .save() on models or .fetch() on collections. You do not have to care about data transport anymore.
I would suggest taking a look into Backbones source and look how the default sync function is implemented. Then create your own or adopt your server to support the native function.
They are not free, but the following screencasts both have a piece on backend work and how to send data to and get data from Backbone.
Tekpub is a 9 part screencast about asp.net MVC3, with the whole 6th part about using backbone to write an admin module to manage productions. it shows all about handling routing in MVC3 and sending & receiving data
Peepcode
http://peepcode.com/products/backbone-js about basic backbone stuff
http://peepcode.com/products/backbone-ii about interactivity
http://peepcode.com/products/backbone-iii about persistance (it's this third one you will need for server connection information).

Categories

Resources