Vinch
Vinch

Reputation: 1561

Detect route/view transition in Ember.js application

The router of my application looks like this (it's CoffeeScript):

App.Router.map () ->
  @resource 'conversations', { path: '/' } ->
    @resource 'conversation', { path: ':conversation_id' }
    @route 'new'

So, in my app, I have paths like /new, /1, /2, etc.

I would like to detect a transition from /1 to /2 to make some initializations in my view (basically, put the focus on a textarea field). Unfortunately, as /1 and /2 use the same route, it seems nearly impossible to detect that transition.

I tried by using didInsertElement in the view (as described here) or by observing currentPath in the controller (as described here). It works fine if I go from /new to /1 (different routes) but not if I go from /1 to /2.

I found this gist suggesting to use the StateManager but it seems outdated (and I'm not sure it's really what I need).

What do you suggest me to do?

EDIT

It seems that setupController is called every time so I decided to overload it like this:

App.ConversationRoute = Ember.Route.extend {

  setupController: (controller, model) ->
    controller.set 'model', model
    # do something here?

}

And I want the init method in my view to be called:

App.ConversationView = Ember.View.extend {

  init: ->
    @$('form textarea').focus()

}

But I still can't figure out how to make these two things work together (it's a problem because I read that the controller is not supposed to know about the view).

Thank you for your help!

Upvotes: 0

Views: 2283

Answers (3)

Jeremy Gillick
Jeremy Gillick

Reputation: 2700

The didInsertElement method of the view is the best method if you need to instantiate something on your view. If you need to have the controller do something when the template loads, you can put the call in the setupController method of your route:

App.FirstRoute = Ember.Route.extend({
    setupController: function(controller){
        controller.onload();
    }
});

Here's a jsfiddle with a full example: http://jsfiddle.net/jgillick/Q5Kbz/

This will be called each time that route is loaded. Try the jsfidle. Click along to the second template and then use your browser's back button. The onload should fire again.

Also, fun fact, you can use the deactivate method as an unload, to do anything you need to that controller when the user navigates away from that route:

App.FirstRoute = Ember.Route.extend({
  deactivate: function(){
    this.controller.unload();
  }
});

Unrelated

One thing to note, (not directly related to your question) if you set the model on the controller in the setupController method, it will overwrite your controllers content property each time that route is loaded. To prevent this, put a conditional around the assignment:

setupController: function(controller, model) {
  controller.onload();
  if (model && (!controller.content || Ember.keys(controller.content).length === 0)) {
    controller.set('content', model);
  }
}

Upvotes: 1

Jonathan Tran
Jonathan Tran

Reputation: 15276

Use the didInsertElement view hook and an observer.

App.ConversationView = Ember.View.extend

  didInsertElement: ->
    @focusOnTextarea()

  modelChanged: (->
    @focusOnTextarea()
  ).observes('controller.model')

  focusOnTextarea: ->
    @$('form textarea').focus()

In the case of going from /1 to /2, the route and view are not changing. Ember does the least amount of work possible. There's no need to re-render the view, so it doesn't. But this tripped me up too, and I think it's a big gotcha.

Also, if you override init in your view, make sure to call @_super().

Note: the model hook is only called when landing on a page to deserialize the URL, not when transitioning from another page and changing the model instance.

Upvotes: 3

James A. Rosen
James A. Rosen

Reputation: 65242

Route#model is your friend here. It will receive a params hash containing information from the URL on every route change (even when changing just which instance of a class is being viewed) In your case,

App.ConversationRoute = Ember.Route.extend {
  model: (params) ->
    App.Conversation.find params.conversation_id
  setupController: (controller, conversation) ->
    // you have the correct Conversation object

The guides have more examples.

Upvotes: 1

Related Questions