Backbone model with server-side rendered HTML snip

2019-04-13 11:35发布

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?

5条回答
成全新的幸福
2楼-- · 2019-04-13 11:48

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

查看更多
贪生不怕死
3楼-- · 2019-04-13 11:49

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.

查看更多
Summer. ? 凉城
4楼-- · 2019-04-13 11:59

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..
查看更多
相关推荐>>
5楼-- · 2019-04-13 11:59

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.

查看更多
闹够了就滚
6楼-- · 2019-04-13 12:07

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));
}
查看更多
登录 后发表回答