I want to start using Backbone.js to better structure my JavaScript files. However, I don't want to redo my application to ouput just JSON via an API. Correct my if I'm wrong, but so far I have the impression that I can still use Backbone.js, even without a JSON API. Now I'm running into a problem where my server returns HTML and the Backbone model doesn't like that and returns an error.
Basically, I want to load an HTML snippet depending on a category:
var Filter = Backbone.Model.extend({
url: '/filters/',
});
var FilterView = Backbone.View.extend({
initialize: function() {
this.model.on('change', this.updateFilter, this);
this.changeFilter();
},
changeFilter: function() {
this.model.fetch({data: $.param({category: this.options.category})});
},
updateFilter: function(filters) {
console.log(filters);
this.$el.html(filters);
},
});
var filter = new Filter();
var filterView = new FilterView({
el: $( '#filterContainer' ),
category: $( '#categorySlug' ).data( 'slug' ),
model: filter,
});
Now I thought that I can use this simple model to retrieve my HTML snippet via Ajax. The request fires correctly, but Backbone returns an error and updateFilter
is never getting called.
Am I not getting something? What do I need to change to make it work with HTML instead of a JSON response? Or shouldn't I use a model at all?
You need to extend your Backbone model to override fetch so that as with the answer from lost you need to pass a datatype to Backbone.Sync
fetch: function(options) {
return Backbone.Model.prototype.fetch.call(this, _.extend({ dataType: "html"}, options));
}
This isn't really the way that Backbone was designed to operate, but you may be able to shim your content into a model field (call it snippet
) by including an appropriate parse
function in your model:
var Filter = Backbone.Model.extend({
url: '/filters/',
parse: function (response) {
return {
snippet: $(response)
}
}
});
Again, you're a little outside of Backbone's natural order, here, but you should now be able to use the usual fetch()
, get()
, and set()
methods to manage the content of the model. For instance,
// in view:
updateFilter: function (filter) {
this.$el.html(filter.get('snippet'));
},
// etc..
In my opinion the problem is that the accept header of the request contains application/json
as default which triggers the error. You can tweak the accept header by doing model.fetch({ dataType : 'html'})
. The accepted answer to this question: Backbone.js fetch with parameters helped me finding this out.
I think the answer to this problem is that you need to replace the sync method of your backbone model with one that works with HTML. The below code produces models with the data in the 'snippet' value:
sync: function (method, model, options) {
// NOTE: This generic function won't work for pushing changes
if (method !== 'read') {
console.log('Cannot update html model');
return false;
}
// As we're only reading default type to GET - see Backbone.sync
// for a more generic way of doing this.
var params = _.extend({ type: 'GET',
contentType: 'application/xml',
dataType: 'text'
});
var urlError = function () {
throw new Error('A "url" property or function must be specified');
};
// ensure that we have a URL
if (!params.url) {
params.url = _.result(model, 'url') || urlError();
}
// NOTE: If you extend this function to deal with creates you'll need to ensure
// params.data is set and make sure that the data isn't processed.
// See the Backbone.sync methods for more info
// Finally wrap the backbone success callback to plonk the data in snippet field of an object
var origSuccess = options.success;
options.success = function (data, textStatus, xhr) {
origSuccess({ snippet: data }, textStatus, xhr);
};
// Make the request, allowing the user to override any Ajax options.
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
model.trigger('request', model, xhr, options);
// make the request
return xhr;
}
The standard backbone reading functions should just work following this. The writing ones will fail, but you can always extend the function to deal with those.
This question is a little bit older, but I found a project on git that describes a really good way of updating your models by looping through the items server side and adding them to the collections when the page is rendered.
https://github.com/runemadsen/Backbone-With-Server-Side-Rendering/blob/master/views/modeltest.erb