Async in Emberjs

Async Routing

Reference to ember guide.
If you want to make sure model is ready before entering the target route. You could use async routing. How?
Make your model as Promise object in route’s model function. For example: In year route i return a model that comes from ModelMgr.listMonth(params.year_id). Because model is Promise object, i add a then method to set id attribute to model(resolve will be model here)

YearRoute
1
2
3
4
5
model: (params) ->
  model = ModelMgr.listMonth(params.year_id)
    model.then (resolve, reject)->
      resolve.set 'id', params.year_id
  return model

In ModelMgr.listMonth method, use Ember.RSVP.Promise to wrap your function. Because i don’t return anything in the last line of function. CoffeeScript will return the promise object automatically. months will be the real model object.

ModelMgr
1
2
3
4
5
6
7
8
9
10
11
12
listMonth: (year) ->
  months = Ember.ArrayProxy.create({content:[]})
  new Ember.RSVP.Promise (resolve) ->
    $.ajax '/api/list',
    type: 'GET'
    dataType: 'json'
    data: 'y='+year
    success: (data, textStatus, jqXHR) ->
      for raw in data
        months.addObject raw
      months.set 'isReady', true
      resolve(months)

Wait multiple function call ready

Sometimes you will use 3rd party library and it requires element existed in the DOM tree. For example: I use highchartJS to draw diagram.
In the route it queries two models to finish the whole scenarios. model is the result from ModelMgr.listMonth and modelForTrend is the result from ModelMgr.queryTrend. The models all have attributes called isReady with false default. After ModelMgr get the data, it will set to true.

SummaryRoute
1
2
3
4
5
6
7
8
  model: (params)->
    model = ModelMgr.listMonth(params.year_id)
    model.year = params.year_id #For queryTrend needs year as parameter
    return model
  setupController: (controller, model)->
    modelForTrend = ModelMgr.queryTrend(model.year)
    controller.set 'model', model
    controller.set 'modelForTrend', modelForTrend

We have two attribute bindings to model’s attributes in controller.

SummaryController
1
2
3
SummaryController = Ember.ObjectController.extend
  dataReadyBinding: 'model.isReady'
  trendDataReadyBinding: 'modelForTrend.isReady'

Create a method to observe all attributes we care about. View shouldn’t connect to Model directly. So i make drawLineChart method to observe controller’s attribute.

SummaryView
1
2
3
4
5
6
7
8
9
10
11
SummaryView = Ember.View.extend
  elementReady: false
  drawLineChart: ( ->
    if @get('controller.dataReady') and @get('controller.trendDataReady') and @get('elementReady')
      content = @get 'controller.model.content' # content is ready
      contentForTrend = @get 'controller.modelForTrend.content' # contentForTrend is ready
      $('#myTrend').highcharts( ... ) # element is ready.
      # do something here...
  ).observes('controller.dataReady', 'elementReady', 'controller.trendDataReady')
  didInsertElement: ->
    @set 'elementReady', true

Comments