Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone.js state management / view initialization based on url fragment

Tags:

I'm trying to keep track of the state in this app using Backbone.js:

enter image description here

I have a "ChartAppModel" with a set of defaults:

ChartAppModel = Backbone.Model.extend({  defaults: {      countries : [],      selectedCountries : [],     year : 1970, },  initialize: function() {      loadAbunchOfData();     checkStartState(); }  }); 

If given a start fragment, this default state should however be overwritten:

var startState = $.deparam.fragment(); //using Ben Alman's BBQ plugin this.set({selectedCountries: startState.s, year: startState.y}); 

Now, for example the SidebarView is ready to be updated:

ChartAppViewSidebar = Backbone.View.extend({  initialize: function(){       this.model.bind("change:selectedCountries", this.render); },  render : function(){       [... render new sidebar with check boxes ...] }, 

Problem is I also have an event handler on the sidebar that updates the model:

events: { "change input[name=country]": "menuChangeHandler", },  menuChangeHandler : function(){       [... set selectedCountries on model ...] }, 

So there will be a feedback loop ... And then, I'd also like a way of pushing a new state - so I listen to model changes:

ChartAppModel = Backbone.Model.extend({  initialize: function() {      this.bind("change", this.setState); }  }); 

... and relatively soon this state-manager will collapse ...

Questions:

1) How do I init my views (for example "which checkboxes should be checked") based on the fragment? (any hints on best practices for state / start state that is not a typical "route" are appreciated)

2) How can I avoid my views setting an attribute on the model which they themselves listen for?

3) How can I push a new state based on a part of the model?

Bonus :)

4) How would you lay out the code for the app described?

Thanks!

like image 707
dani Avatar asked May 25 '11 15:05

dani


People also ask

What is backbone JS?

Backbone.js. Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface. The project is hosted on GitHub , ...

How to update and save the application as URL in backbone?

The Backbone.js navigate method is used to update and save the application as URL. It can also be done by calling the route function. fragment: It specifies the name of the parameter in which URL will be displayed. options: It specifies the options such as trigger and replace to call the route function and to update the URL. Let's take an example.

What are the methods to manipulate the backbone JS views?

Following is a list of methods that can be used to manipulate the Backbone.js views: 1. It is used to extend the Backbone.view class to create a custom view class. 2. It is used to instantiate the view by using new keyword. 3. It defines which element to be used as the view reference. 4. It represents the jQuery object for the view's element. 5.

Who is responsible for URL-encoding the fragment in advance?

You are responsible for properly URL-encoding the fragment in advance. The options object can contain trigger: true if you wish to have the route callback be fired (not usually desirable), or replace: true, if you wish to modify the current URL without adding an entry to the history. navigate: function ( fragment, options) { if (!


1 Answers

That is one well defined question!

There is a question over what is a model. I believe there is a definition floating around as to what constitutes a model in the backbone world, and I'm not sure your strategy is in keeping with that definition. Also you are storing the state in both the url, and the model. You can just store the state in the url, as I will explain.

If I was doing this, there would be 2 views. One for your app controls, and nested inside that one for your graph: GraphView, and AppView. The model will be the data your going to plot, not the state of the interface.

Use a controller to kick start the app view and also to process any interface state defined in the url.

There is a question about levers of state in Backbone. Traditional web applications used a link/url as the primary lever of state but all that is now changing. Here is one possible strategy:

Checkbox Change Event -> Update location fragment -> trigger route in controller -> update the view Slider Change Event -> Update location fragment -> trigger route in controller -> update the view 

The great thing about such a strategy is that it takes care of the case where urls are passed around or bookmarked

Url specified in address bar -> trigger route in controller -> update the view 

I'll take a stab at a pseudo code example. For this, I will make some assumptions on the data: The data is the dog population over time (with a granularity of year), where the slider should have a lower and upper bound, and there volume data is too large to load it all to the client at once.

First let's look at the Model to represent the statistical data. For each point on the graph we need something like { population: 27000, year: 2003 } Lets represent this as

DogStatModel extends Backbone.Model -> 

and a collection of this data will be

DogStatCollection extends Backbone.Collection ->     model: DogStatModel     query: null // query sent to server to limit results     url: function() {         return "/dogStats?"+this.query     } 

Now lets look at the controller. In this strategy I propose, the controller lives up to its name.

AppController extends Backbone.Controller ->     dogStatCollection: null,     appView: null,      routes: {          "/:query" : "showChart"     },      chart: function(query){         // 2dani, you described a nice way in your question         // but will be more complicated if selections are not mutually exclusive         // countries you could have as countries=sweden;france&fullscreen=true         queryMap = parse(query) //           if (!this.dogStatCollection) dogStatCollection = new DogStatCollection         dogStatCollection.query = queryMap.serverQuery         if (!this.appView) {            appView = new AppView()            appView.collection = dogStatCollection         }         appView.fullScreen = queryMap.fullScreen         dogStatCollection.fetch(success:function(){           appView.render()         })                 } 
like image 103
34m0 Avatar answered Oct 12 '22 00:10

34m0