javascript - knockout - how it works? - javascript

I am trying to use knockout and I wonder myself how it works REALLY from a network point of view : where can I see the "calls" from the browser client to the server to retrieve data?
I mean : I used to use Ajax calls to populate forms, tables... => we can see the ajax calls to the server. Same thing when I need to submit a form : I can see the ajax calls.
That means I can debug with Firefbug and see the parameters sent/ the response received, inluding the headers (request/response).
With knocknout, the data in the form are "binding" automatically by the framework ko. So, does someone know how it works really? how the calls are done? is there any way to "see" the data flow?

From a network point of view, nothing changes when using knockout. You'll still need to make AJAX calls to populate your view models, but they're outside the framework, not part of it. This means that you can still put a breakpoint on your AJAX calls and observe the stuff being sent and received.
A major code departure is that your network calls will probably now exist within a knockout viewmodel.
var someVm = function(data) {
var self = this;
self.Id = ko.observable(data.Id);
// ...
self.getItems = function() {
// AJAX call here, now method on a vm
}
}
However, as TJ Crowder points out - the key mechanic of knockout is binding a client side view model to a user interface, either for data population or visibility control in a single page application. Networking is something you'll have to handle, but it's not part of knockout. Most likely, you'll make small changes in your placement of AJAX calls.

It's based on the publish-subscribe pattern.
Whenever something is changed it notifies about it.
Here's some info about it http://msdn.microsoft.com/en-us/magazine/hh201955.aspx

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_;
}

Using Data Generated from AJAX Call for OnClick Event

I have a web application where I want to get some data from an API right when the document is ready to execute javascript. I want to store this data in an Object for later access. When a user clicks on a certain link on the page, I will need access to this Object.
I'm wondering what the safest/most accepted pattern for achieving this is, since it involves to events to listen for (the success of the AJAX request and the later possible click). I don't really want to wrap all of my code in the AJAX request if I don't have to, but I also don't want to run the risk of the user clicking before the AJAX request has finished (it's a fairly small API call, but still).
Deferred objects to the rescue!
(function($){ // this prevents polluting the global scope
var request = $.ajax({...});
//... later on...
$(someelement).on("click",function(){
request.done(function(data){
console.log(data);
});
});
})(jQuery);

What exactly happens when you save a Backbone model?

What exactly happens when you save a Backbone model? Here's the best I can piece together by reading the documentation here:
model.save([attributes], [options]) is called
A "change" event is fired (but only if the attributes are new)
The server is notified of the change?
A "sync" event is called once the server returns
But I'm a Backbone noob and I'm sure someone else could do a way better job of explaining.
I'm partly just curious what happens. I'm also having trouble understanding how Backbone comes up with the JSON object it sends to the server. I'm having a separate problem where the JSON object is not what I want it to be, but I don't know how to change it.
The detailed process can be found in the annotated source code for Backbone.Model.save and Backbone.sync.
If you ignore options.wait and options.silent, your decomposition is mostly correct.
When you issue a model.save:
the attributes passed to the function are set, a change event is fired if the values changed
save delegates the request to model.sync or Backbone.sync
sync serializes the data to a JSON string by calling JSON.stringify(model.toJSON())
An Ajax request is sent to sent to server, a POST request for a new object, a PUT for an update. The target URL is defined by model.url (or collection.url/id)
When the request completes, the model is updated with the server response, if any, and triggers a change event accordingly.
Success or error callbacks are called, a sync event is triggered if no success callback is defined.
Usually, you can customize this behaviour by overriding model.toJSON or model.sync
first,I suggest you read the source code of the backbone, is really very simple.Default backbone and server-side interaction is achieved through backbone.sync.
second,You can trace debug model.save method of code again, naturally know the details.
I suggest you start hereļ¼š
http://backbonejs.org/examples/todos/index.html

One ajax handler or multiple handler?

My webpage needs to send/receieve several ajax operations when being used. Currently I use one ajax handler to handle all events. All ajax requests are sent/received using this format "(type) | {json string}".
A piece of js code will handler the ajax requests/responses: parsing response text -> getting type -> select...case doing something in each type case.
This works but as ajax events grow there are too many cases like from 0 to 99. Well it's not likely to be an easy job for maintenance or further developing.
Maybe I need to split the single ajax handler to mulitiple ones? Then how does the browser know which ajax response should be sent to the specific handler?
Any advice is appreciated.
Currently the code looks like this: (one of the pages using simple javascript, no framework used)
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//alert(xmlhttp.responseText);
var reply = decodeArray(xmlhttp.responseText); //convert to json object and some other stuff
switch (reply.type) {
case 0:
case 1:
....
}
This is one of the fundamental challenges with software engineering. You have a task that you do repeatedly, but the details change slightly with each variation, so how do you reuse code and keep things clean....every substantial app has this problem.
You need to use a design approach that enforces good Separation of Concerns. I usually develop on API for my app that is completely separate from other parts of the app. My API does one thing and one thing only -- it communicates with the server.
It has no application specific code.
This means I can reuse my api in different apps, if necessary. It also means I can test my API independently of the app.
So lets say you need to do something like loadObject. The applications sees
App.API = {
loadObject: function(params, onSuccess, onFail) {...}
}
The key to keeping this decouples is the onSuccess and onFail callbacks. The application that is using the API passes in these functions; so the API knows nothing of the application specific logic -- all it knows is that if fires these callbacks appropriately. The callbacks are methods that take arguments that are the response data; all the API does is pass the response data to the callback.
Since most of the time, the details of your ajax calls have lots of common items, I also would create some sort of XhrWrapper that fires requests. So in your loadObjects you would use your xhr helper to make the request
xhr.sendRequest(url, method, onSuccess, onFail);
This way all the tedium of firing xhrs is minimized.
Obviously, you can go further; for example, most of the time failure is bad, so my xhr wrapper will have a default onFail implementation, and specific API methods can pass in an override, only if it makes sense.

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