EmberJS: How to transition to a router from a controller's action
Asked Answered
A

3

13

I have an action:

{{action create target="controller"}}

which I have targeted to the bound controller (rather than the router) like this:

App.AddBoardController = Ember.Controller.extend
    create: ->
        App.store.createRecord App.Board, {title: @get "boardName"}
        App.store.commit()
        //TODO: Redirect to route

How do I redirect back to a route from the controller action?

Agreement answered 19/7, 2012 at 0:38 Comment(0)
G
17

In fact, this is not Ember idiomatic. From what I know, and what I have learnt from Tom Dale himself, here are some remarks about that code:

  • First, you should not transitionTo from elsewhere than inside the router: by doing so, you are exposing yourself to serious issues as you don't know in which state is the router, so to keep stuff running, you will quickly have to degrade your design, and by the way the overall quality of you code, and finally the stability of your app,
  • Second, the action content you are showing should be located inside the router to avoid undesired context execution. The router is indeed a way to enforce a coherent behavior for the whole app, with actions being processed only in certain states. While you are putting the actions implementation into Controllers, those actions can be called at anytime, any including wrong...
  • Finally, Ember's controllers are not aimed to contain behavior as they rather are value-added wrappers, holding mainly computed properties. If you nevertheless want to factorize primitives, maybe the model can be a good place, or a third party context, but certainly not the Controller.

You should definitely put the action inside the router, and transitionTo accordingly.

Hope this will help.

UPDATE

First example (close to your sample)

In the appropriated route:

saveAndReturnSomewhere: function (router, event) {
  var store = router.get('store'),
      boardName = event.context; // you pass the (data|data container) here. In the view: {{action saveAndReturnSomewhere context="..."}}
  store.createRecord(App.Board, {
    title: boardName
  });
  store.commit();
  router.transitionTo('somewhere');
}

Refactored example

I would recommend having the following routes:

  • show: displays an existing item,
  • edit: proposes to input item's fields

Into the enclosing route, following event handlers:

  • createItem: create a new record and transitionTo edit route, e.g
  • editItem: transitionTo edit route

Into the edit route, following event handlers:

  • saveItem: which will commit store and transitionTo show route, e.g
Grunion answered 19/7, 2012 at 6:22 Comment(10)
OK that makes sense about not putting transitionTo in the router. So if I want to perform some action like saving an entity and then redirect, what would be the best way to do this without redirecting from the router?Agreement
Sorry if my english is not that clear... :-P That is the exact opposite: I am saying that you should call transitionTo only from the router, not from the controller.Grunion
Sorry! That was my typo, I meant the controller, not the router. The action to save the entity would still be in the controller though, so how would I then redirect after that has completed?Agreement
So...this doesn't seem right. You are committing from the store from the router? That definitely seems like something the controller should do, the router should just handle routing and perhaps binding data to controllers. Otherwise it's in danger of just becoming like a massive switch/case statement for all the UI logic..Agreement
That's something almost right: the router is the heart of both the UI management & the processing bound to user actions. I am just trying to share with you the knowledge I received right from Tom Dale, so the router was intended to be like that... :-) In fact, the name Controller has not to be confused with a 'traditional' MVC controller. In fact, in Ember idiom, it is only a data wrapper, not much.Grunion
@MikeAski do you know if this approach is still valid for the current router in 1.0.0-pre4?Antipyretic
That doesn't make sense(on ember's part). How are we to delete an object and redirect if not within the controller?Transmogrify
@JanDupal In fact, as you can see, this question is tagged ember-old-router and no more applies to current routerGrunion
@Transmogrify see my previous commentGrunion
Ember completely supports transitioning from within a Controller and it's no less idiomatic (only from 3.0 onwards Ember stops supporting it). I feel it is perfectly fine in an MVC world for the C to route to a different page or a state of the app. In fact, Router and C have been viewed as one entity at multiple places. Top level actions that can set/change the state reside in Controllers. Things like ember-route-actions are hacks built around the assumed principle of not using controllers. emberjs.com/api/ember/2.18/classes/Controller/methods/transitionToRoute?anchor=transitionToRouteHigginbotham
M
19

Use transitionToRoute('route') to redirect inside an Ember controller action:

App.AddBoardController = Ember.Controller.extend({
    create: function(){ 
        ...
        //TODO: Redirect to route
        this.transitionToRoute('route_name');
    }
...
Member answered 25/7, 2013 at 2:5 Comment(1)
transitionToRoute seems to no more exist in Ember 3 and raises the error when using in a controller: Uncaught TypeError: Cannot read property 'transitionToRoute' of undefinedIndices
G
17

In fact, this is not Ember idiomatic. From what I know, and what I have learnt from Tom Dale himself, here are some remarks about that code:

  • First, you should not transitionTo from elsewhere than inside the router: by doing so, you are exposing yourself to serious issues as you don't know in which state is the router, so to keep stuff running, you will quickly have to degrade your design, and by the way the overall quality of you code, and finally the stability of your app,
  • Second, the action content you are showing should be located inside the router to avoid undesired context execution. The router is indeed a way to enforce a coherent behavior for the whole app, with actions being processed only in certain states. While you are putting the actions implementation into Controllers, those actions can be called at anytime, any including wrong...
  • Finally, Ember's controllers are not aimed to contain behavior as they rather are value-added wrappers, holding mainly computed properties. If you nevertheless want to factorize primitives, maybe the model can be a good place, or a third party context, but certainly not the Controller.

You should definitely put the action inside the router, and transitionTo accordingly.

Hope this will help.

UPDATE

First example (close to your sample)

In the appropriated route:

saveAndReturnSomewhere: function (router, event) {
  var store = router.get('store'),
      boardName = event.context; // you pass the (data|data container) here. In the view: {{action saveAndReturnSomewhere context="..."}}
  store.createRecord(App.Board, {
    title: boardName
  });
  store.commit();
  router.transitionTo('somewhere');
}

Refactored example

I would recommend having the following routes:

  • show: displays an existing item,
  • edit: proposes to input item's fields

Into the enclosing route, following event handlers:

  • createItem: create a new record and transitionTo edit route, e.g
  • editItem: transitionTo edit route

Into the edit route, following event handlers:

  • saveItem: which will commit store and transitionTo show route, e.g
Grunion answered 19/7, 2012 at 6:22 Comment(10)
OK that makes sense about not putting transitionTo in the router. So if I want to perform some action like saving an entity and then redirect, what would be the best way to do this without redirecting from the router?Agreement
Sorry if my english is not that clear... :-P That is the exact opposite: I am saying that you should call transitionTo only from the router, not from the controller.Grunion
Sorry! That was my typo, I meant the controller, not the router. The action to save the entity would still be in the controller though, so how would I then redirect after that has completed?Agreement
So...this doesn't seem right. You are committing from the store from the router? That definitely seems like something the controller should do, the router should just handle routing and perhaps binding data to controllers. Otherwise it's in danger of just becoming like a massive switch/case statement for all the UI logic..Agreement
That's something almost right: the router is the heart of both the UI management & the processing bound to user actions. I am just trying to share with you the knowledge I received right from Tom Dale, so the router was intended to be like that... :-) In fact, the name Controller has not to be confused with a 'traditional' MVC controller. In fact, in Ember idiom, it is only a data wrapper, not much.Grunion
@MikeAski do you know if this approach is still valid for the current router in 1.0.0-pre4?Antipyretic
That doesn't make sense(on ember's part). How are we to delete an object and redirect if not within the controller?Transmogrify
@JanDupal In fact, as you can see, this question is tagged ember-old-router and no more applies to current routerGrunion
@Transmogrify see my previous commentGrunion
Ember completely supports transitioning from within a Controller and it's no less idiomatic (only from 3.0 onwards Ember stops supporting it). I feel it is perfectly fine in an MVC world for the C to route to a different page or a state of the app. In fact, Router and C have been viewed as one entity at multiple places. Top level actions that can set/change the state reside in Controllers. Things like ember-route-actions are hacks built around the assumed principle of not using controllers. emberjs.com/api/ember/2.18/classes/Controller/methods/transitionToRoute?anchor=transitionToRouteHigginbotham
B
3

EDIT: Keep reading, Mike's answer discusses some of the problems with this approach.

You can just call transitionTo directly on the router. If you are using defaults this looks like App.router.transitionTo('route', context).

Borneol answered 19/7, 2012 at 1:19 Comment(1)
[Bad solution, but...] A better implementation would use the target property instead of using the App.router instance (better especially for testing purpose).Grunion

© 2022 - 2024 — McMap. All rights reserved.