0
votes

I have a Property model and a Pricing Summary model, which relate to each other and are shown below:

App.Property = DS.Model.extend({
    totalRoomCount: DS.attr(),
    name: DS.attr(),
    address: DS.attr(),
    city: DS.attr(),
    state: DS.attr(),
    zip: DS.attr(),
    pricingSummaries: DS.hasMany('pricingSummary', {async: true})
});

App.PricingSummary = DS.Model.extend({
    startDate: DS.attr(),
    endDate: DS.attr(),
    days: DS.hasMany('day', {async: true}),
    property: DS.belongsTo('property', {async: true})
});

Inside of my Property route I set the model to a Property, and then in the template, I want to output a list of the PricingSummary's that are related to that Property, as follows:

{{#each pricingSummary in pricingSummaries}}

{{render 'summaryRow' pricingSummary}}

{{/each}}

This works, and I'm able to output the attributes of each particular PricingSummary inside of the summaryRow template, like its startDate and endDate, for example. But what I REALLY want to do here is modify/format the startDate and output this formatted version. Basically I think I want a controller at this point, but I don't know how to tie a controller to the specific Pricing Summary model being output.

How do I do this? And furthermore, you can see that a PricingSummary also has a relationship to my Day model, so I'm going to want to do this again, another level deep.

Please help!

1

1 Answers

0
votes

There are several ways to accomplish this, and all of them are relatively simple.

In relation to actually decorating a model, the easiest method would be to create a computed property on the model itself. Some people don't like this because they believe the models should be skinny and decorators should be in controllers/components, but it's all up to your preference. You could accomplish it this way:

App.YourModel = DS.Model.extend({
  date: attr('date'),
  formattedDate: function() {
    var date = this.get('date');
    return date ? this.get('date').toDateString() : null ; // Use your own format :-)
  }.property('date')
});

Alternatively, I like to use a getter/setter pattern so you can use two-way bindings and it will marshal the value to a date on set, or to a string on get. In the following example, I'm using moment.js to parse/format:

App.YourModel = DS.Model.extend({
  date: attr('date'),
  dateMarshal: function(key, value) {
    if (arguments.length > 1) {
      var parsed = moment(value);
      this.set('date', parsed.isValid() ? parsed.toDate() : null);
    }
    return this.get('date') ? moment(this.get('date')).format('MM/DD/YYYY') : null ;
  }.property('date'),
});

Another option would be to provide an itemController property to the {{#each}} helper, but that's effectively the same as using render without having to use a custom view.

If you're using more properties and perhaps some actions on the pricing summary row (to delete it, for instance), my preference would be to use a component:

{{#each pricingSummary in pricingSummaries}}
  {{pricing-summary-item content=pricingSummary}}
{{/each}}

And your component:

App.PricingSummaryItem = Ember.Component.extend({
  content: null,
  dateFormatted: function() {
    var formattedDate = this.get('content.date');
    // Format your date
    return formattedDate;
  }.property('content.date')
  actions: {
    'delete': function() {
      this.get('content').deleteRecord();
    },
    markRead: function() {
      this.set('content.isRead', true);
      this.get('content').save();
    }
  }
});

Finally, to address JUST the date issue and not decoration, I would make a bound helper. Again, this example uses moment.js (and I'm using ember-cli as well, so pardon the ES6 syntax):

import Ember from 'ember';

export function formatDate(input, options) {
  input = moment(input);
  if (options.hashContexts.fromNow) {
    return input.fromNow();
  } else {
    return input.format(options.hash.format || 'LL');
  }
}

export default Ember.Handlebars.makeBoundHelper(formatDate);

Then you just use {{format-date yourDateProperty}} in your template.