How can I bind data returned from the model hook to the template?

I have a model hook in my route that returns an Ember.RSVP.hash with one of the elements being an array:

AlertsIndexRoute = Ember.Route.extend(
  AuthenticatedRouteMixin,
  {
    tasks: Ember.computed -> @store.peekAll('task')
    alerts: Ember.computed.filterBy('tasks', 'category', 'ALERT')

    filteredAlerts: Ember.computed 'alerts.@each.status', ->
      @get('alerts').rejectBy('status', 'DRAFT').rejectBy('status', 'DELETED')

    model: ->
      @fetchContent(@store.peekAll('organization'))
      return Ember.RSVP.hash
        columns: @get('columns')
        content: @get('filteredAlerts')

    fetchContent: (organizations) ->
      @store.query('task', {
        category: 'ALERT'
        organization_id: organizations.get('firstObject.id')
      }).then (results) ->
        return results.rejectBy('status', 'DRAFT').rejectBy('status', 'DELETED')
  }
)

The problem is that when AlertsIndexRoute#filteredAlerts is updated, those changes are not propagating through to the controller:

Can anyone point out where I’m going wrong? I thought returning the data from the model hook was the Ember Way ™ but I’m beginning to think I should just lookup the data directly in the controller.

Any help would be greatly appreciated.

Thanks!

1 Like

You should be able to have access to the model in the temple simple by:

{{model.content}}

It’s not working for you this way?

Wich ember version are you running?

Yup, here’s the relevant line in the template:

  filtered-task-table content=model.content columns=model.columns currentRouteName=currentRouteName duplicateTask=(action "duplicateTask")

However, I do have access to the data. It shows the original values. But, these are cached on the client side in Ember Data. If you look at the model hook, you’ll see that fetchContent queries EmberData and forces a network call. When this call comes back with additional data that is not in the client side cache, the DS.RecordArray returned by peekAll() in tasks is updated and, consequently, filteredAlerts is updated as well.

The problem is, model.content is not also being updated in the controller. The screen capture above shows the state after the network call returns and updates Ember Data. You can see that filteredAlerts has a length of 4 but model.content only has a length of 1. My assumption was that these would be the same.

DEBUG: -------------------------------
DEBUG: Ember             : 2.5.1
DEBUG: Ember Data        : 2.5.3
DEBUG: jQuery            : 2.2.3
DEBUG: Ember Simple Auth : 1.0.0
DEBUG: -------------------------------

Just to close this out incase anyone else (i.e. me in 6 months) comes across this, here’s the hack to get cached data working:

I exposed the filtered data in the store in the controller then bound that to the template. This breaks the pattern of returning the data to be bound to the template in the routes model hook:

AlertsIndexController = Ember.Controller.extend(
  {
    tasks: Ember.computed -> @store.peekAll('task')
    alerts: Ember.computed.filterBy('tasks', 'category', 'ALERT')

    filteredAlerts: Ember.computed 'alerts.@each.status', ->
      @get('alerts').rejectBy('status', 'DRAFT').rejectBy('status', 'DELETED')
  }
)

Pro-Tip: you’ll be duplicating code in the route and controller. Put the duplicate code in a mixin and add that to both to DRY up the final product.

Than, you can bind to filteredAlerts in the template:

filtered-task-table content=filteredAlerts columns=model.columns currentRouteName=currentRouteName duplicateTask=(action "duplicateTask")

Now, you can perform a full @store.query in the model hook (but don’t return a promise!) and the data with automatically update when the call to the back-end returns.

A computed property is a way to declare dependencies between properties. In the most simple case, the CP:

myComputedProperty: Ember.computed('p1', 'p2', 'p3', function fn() { }),

express that myComputedProperty shall be recomputed whenever p1, p2 or p3 changes, and the new value will be given by executing fn.

When getting the value of a CP you get the value of such property in the current state as a snapshot. See that this behaviour is consistent with ES6 getters in this JSBin.

You are looking for something like LiveArrays and there is an addon for a creating a live array from filters: ember-data-live-filter-by though I have not used it.

I think that plugin is solving a different issue. peekAll does return a DS.RecordArray which is updated when new records are added to the store. So, tasks is updated when the network call returns with new records.

The issue is that filteredAlerts (or just tasks if you want to keep is simple) must be a computed property on the controller and cannot be returned by the route’s model hook. IMO, you should always return the data for a template from the route’s model hook for consistency in your codebase. Also, to get loaders, error states, etc…, I still need to perform the network call to get the records from the route’s model hook. So, it feels wrong and unDRY to create a copy of the data in the controller and bind that to the template.