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({});