How to access Backbone.Model methods from Marionette.js ItemView template?
Asked Answered
P

2

5

I'm trying to access a model's method from within an .eco template using backbone/marionette.js. I have an Expense model with a day() method which, using moment.js, returns '13th'; for example:

class Expense extends Backbone.Model
  day: ->
    moment.get('date').format('Do')

I can create a new Expense as follows, and call the day() method:

coffee = new Expense({name: "Coffee", amount: 2.50, date: "2014-01-13T13:50:00Z"})
coffee.day() # 13th

However, trying to access day() from within the following view and template is causing me some problems:

class ExpenseView extends Marionette.ItemView
  template: "views/_expense"
# views/_expense.jst.eco
<h3 class="expense__name"><%= @name %></h3>
<p class="expense__day"><%= @day() %></p>

I understand why it isn't working...the ItemView calls serializeData which returns @model.toJSON()... therefore, the Expense's day() method isn't accessible. Is there an established pattern in the backbone/marionette community that makes model methods available to templates?

So far, I've done the following to make it work:

class ExpenseView extends Marionette.ItemView
  template: "views/_expense"

  serializeData: ->
    _.extend(@model.toJSON(), model: @model)

  templateHelpers:
    day: ->
      @model.day()

But I'm unsure whether this is the best way to go about the problem? Thanks!

Posterity answered 13/1, 2014 at 14:8 Comment(0)
A
8

You can always add it to templateHelpers or serializeData - but what you're really asking about is a virtual attribute - one that can be part of every template, an attribute that is used solely as part of a ViewModel. In this way we can also disable it from syncing to the server on sync events such as save()

There are several plugins out there that do this, my personal favorite to use is Backbone Mutators - https://github.com/asciidisco/Backbone.Mutators

Others like Backbone Computed Fields - https://github.com/alexanderbeletsky/backbone-computedfields give you Ember like computed properties.

With Backbone Mutators you would write your mutators in the Backbone Model.

class Model extends Backbone.Model
  mutators:
    day: ->
      moment.get('date').format('Do')

Alternatively to prevent the 'day' attribute from syncing to the backend...

class Model extends Backbone.Model
  mutators:
    day:
      get: -> moment.get('date').format('Do')
      transient: true

The day attribute will be present in all toJSON() calls now.

Autography answered 13/1, 2014 at 15:46 Comment(1)
Thanks Brian, I quite like this Backbone.Mutators plugin! I like how it moves the logic out of my views and into the model – a bit like :scope in rails. The transient: true is a nice addition too!Posterity
S
0

Why don't you just add it to serializeData ? (Can't remember Coffee syntax...)

serializeData: function(){
    var data = _.clone(this.model.attributes);
    data.day = this.model.day();
    return data;
}
Swagsman answered 13/1, 2014 at 15:35 Comment(4)
Thanks David, I was thinking maybe it'd just be better to override Expense's toJSON method instead...that way the logic is encapsulated in the model rather than the view?Posterity
The rpoblem is that if you add it to toJSON it will get sent to the API endpoint also, which might be an issue.Swagsman
Ahh I see. Maybe I could extend serializeData in all Marionette.Views such that it would call a new method toData (or a better name) if it existed, else just call toJSON as normal. Then I could optionally implement toData in any of my models to merge the models attributes with any custom ones I choose?Posterity
That's a possibility. Or define an extendJSON method with the extra params you wan to add. Then in Marionette's serailzeData you simply _.extend the normal value that gets returned (if extendJSON is defined) by adding the result of extendJSON. This would avoid code duplication.Swagsman

© 2022 - 2024 — McMap. All rights reserved.