How to access a calculated field of a backbone model from handlebars template?

后端 未结 4 1723
遇见更好的自我
遇见更好的自我 2021-02-05 16:01

I would like to access the calculated fields I have implemented in the model (backbone.js) from the template. Do I need always to define a helper to do it?

I think the p

相关标签:
4条回答
  • 2021-02-05 16:04

    Here is another possibility: (from the model initialize)

    initialize: function() {
            this.on("change", function () {
                this.set({ calculatedColumn: this.get("otherColumn") }, { silent: true });
            });
        },
    

    Computed properties in Backbone

    0 讨论(0)
  • 2021-02-05 16:10

    I have had the same issue. @DerickBailey is right, of course, that overriding toJSON does the job. But it also leaks into the communication with the server (see muu's comment on his answer).

    So eventually, I have built a Backbone plugin to specifically handle data export to templates, and do so with a minimum of fuss: Backbone.Marionette.Export. It also deals with nested structures, takes care of circular references etc. See the docs.

    Here's how it works. Include the plugin file into your project and declare

    MyModel = Backbone.Model.extend({
    
      foo: function () {
          return "I am a calculated value";
      },
    
      exportable: "foo"    // <-- this is the one line you have to add
    
    });
    

    If you are a Marionette user, you are already done at this point. foo shows up in your templates as if it were a model attribute.

    In plain Backbone views, just call myModel.export() or myCollection.export() instead of their toJSON counterparts when you render.

    For methods taking arguments, there is an onExport handler. Examples, again, are in the docs.

    0 讨论(0)
  • 2021-02-05 16:17

    Always pass this.model.toJSON() to your templates.

    What you need to do to get your calculated values, is override your toJSON method on your model.

    
    MyModel = Backbone.Model.extend({
    
      myValue: function(){
        return "this is a calculated value";
      },
    
      toJSON: function(){
        // get the standard json for the object
        var json = Backbone.Model.prototype.toJSON.apply(this, arguments);
    
        // get the calculated value
        json.myValue = this.myValue();
    
        // send it all back
        return json;
      }
    
    })
    

    And now you have access to myValue from the the JSON that is returned by toJSON, which means you have access to it in the view.

    The other option, as you mentioned, is to build helper methods and register them with Handlebars. Unless you have some functionality that changes based on how the template is being rendered, and/or what data is being passed to the template, I wouldn't bother with that.

    0 讨论(0)
  • 2021-02-05 16:28

    The best way to do it is to add this to your model:

    function initialize() {
        this.set("calculatedColumn", function () { return this.otherColumn; });
    }
    

    A backbone model normally stores the actual data values internally in "model.attributes". That is why when you pass your model directly to the template, it only has functions added directly to model and not any data. And if you use model.toJSON() it is normally implemented in backbone as _.clone(model.attributes) (see backbone.js). So you have the data and not the functions added directly to the model. That is why the above works - you set the function on model.attributes, not on the model object itself. Do not reference model.attributes directly, use model.get("calculatedColumn") and model.set("calculatedColumn", ...).

    So model.get("calculatedColumn") returns a function. If you go {{calculatedColumn}} in handlebars (assuming you're using handlebars), it shows the value returned by the function. But calculatedColumn will not be sent to the server because backbone does a JSON.stringify to model.toJSON in sync (in backbone.js) and JSON.stringify ignores functions. If you want JSON.stringify to not ignore the function (so the function is turned into a data value whenever toJSON is run on the model - during view rendering and model sync-ing), override model.toJSON just as @Derick Bailey described.

    Also, you can derive your own BaseModel from Backbone.Model and override .toJSON and derive all your models from BaseModel if you need to. Then you would need a generic version of .toJSON that could be applied to any model.

    0 讨论(0)
提交回复
热议问题