Backbone: trying to figure out the basic control flow, could someone tell me if this is sane?

StackOverflow https://stackoverflow.com/questions/14752311

質問

I'm building a simple Backbone application with a search form and some results. I'd like to:

  • allow the user to change the value of an 'order by' select field, and update the URL and results appropriately
  • ensure that when the user arrives via a preset URL, the correct results are shown, and the selected form elements match the URL and results.

I think I've figured out the right control flow to use, but I'd really like to have someone sanity-check it. Basically, I'm letting my Views update the URLs directly, and then the Router does the work of updating the Model. Other Views then listen to the Model.

It seems to work OK, but I have to construct the URL by hand in the View, and parse it again in the Router, which feels repetitive. So it would just be great to know if this is the right way to do things in Backbone, or if I can do things better.

Here's the pseudocode:

 User changes value of select to "price-asc"    
           |                                        
           |                                        
           v                                        
 Form view hears change event, and              
 updates the URL to #order-by-price-asc            
           |                                       
           |                                       
           v                                       
 Router looks at new route,        <----- User loads page, with 
  sets model value                       #order-by-price-asc route
           |
           |
           v   
  Results view hears change 
  to model value, loads in 
  new results via Ajax

Here is the code:

HTML:

<select id="order">
    <option value=''></option>
    <option value='price-asc'>price (low to high)</option>
    <option value='price-desc'>price (high to low)</option>
</select>
<div id="results"></div>

JS:

var SearchModel = Backbone.Model.extend({
    initialize: function() {
        this.bind("change:query", function() {
        this.performSearch();
        });
    },
    performSearch: function() {
        // Will eventually do Ajax search here, 
        // but for the moment, just bind to query. 
        this.set("results", this.get("query"));
    },
});

var SearchFormView = Backbone.View.extend({
    el: $('#order'),
    events: {
        "change": "updateURL"
    },
    initialize: function() {
        this.model.on("change:results", this.updateFormClasses, this);
    },
    updateFormClasses: function(model, query) {
        this.$el.val(query);
    },
    updateURL: function(e) {
        var url = '';
        ($('#order').val() !== '') ? url += "/by-" + $('#order').val() : null;
        SearchApp.navigate(url, {trigger: true});
    }
});

var SearchResultsView = Backbone.View.extend({
    el: "#results",
    initialize: function() {
        this.model.on("change:results", this.displayResults, this);
    },
    displayResults: function(model, query) {
        this.$el.text(query)
    }
});

var AppRouter = Backbone.Router.extend({
    routes: {
    "*str": "index",
    },
    initialize: function() {
        this.searchModel = new SearchModel();
        this.searchFormView = new SearchFormView({
            model: this.searchModel
        });
        this.searchResultsView = new SearchResultsView({
            model: this.searchModel
        });
    },
    index: function(str) {
        var ordering = str.match(/by-.*/gi);
        if (ordering !== null) {
            this.searchModel.set({"query": ordering[0].replace('by-','')});
        }
    }
});
var SearchApp = new AppRouter();
Backbone.history.start({});
役に立ちましたか?

解決

There are few optimizations you can do:

  1. Use an optional route parameter instead of parsing the url manually:

    var AppRouter = Backbone.Router.extend({
        routes: {
          "*str(/by-:sort)": "index",
        },
        index: function(str, sort) {
          if(sort) {
            //...
          }
        }
    });
    
  2. Consider allowing view to handle the model change, instead of going via the router. In order to avoid code duplication, encapsulate this logic in a model method.

    var SearchModel = Backbone.Model.extend({
      sort: function(field) {
        this.set({query:field});
      }
    });
    
    var SearchFormView = Backbone.View.extend({
      el:"#order",
      events: {
        "change": "updateSort"
      },
      updateSort: function(e) {
        var sortOrder = this.$el.val();
    
        //update model
        this.model.sort(sortOrder);
    
        //navigate without trigger:true, will update the URL but not trigger route
        SearchApp.navigate(sortOrder ? "/by-" + sortOrder : "");
      }
    }    
    
    var AppRouter = Backbone.Router.extend({
        index: function(str, sort) {
          if(sort) {
            this.searchModel.sort(sort);
          }
        }
    }); 
    

Notice also that I changed the view.el declaration just to #order, and refer to the element with this.$el instead of a jQuery selector. These are unrelated, but still best practices.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top