17
votes

How do I transition to a route pragmatically from inside a component action?

I tried to use @get('controller').transitionToRoute('images'), but the controller refers to the component itself. I understand that components should be self contained, so should I be using a view instead to interact with controllers/routes better?

Example

App.ImageEditorComponent = Ember.Component.extend
  ...
  actions:
    delete: ->
      App.Files.removeObject(object)
      @transitionToRoute('images') # This throws an exception
  ...
6
I feel like the idea is that a component shouldn't know about the host controller's actions. You might want to send a message instead. - aceofspades
@aceofspades Quite possibly. So far I've understood that views may know of the controller, but the controller may not know about the view in Ember. Could you show me an example as an answer? Curious how this would work; have a vague idea. - al3xnull
@aceofspades Assuming you mean something like this then I suppose instead of the currentControllerBinding I could set a fileDeletedAction and then this.sendAction('fileDeletedAction'). - al3xnull
Did just that. I like the de-coupling more. Very easy to change to another location in a different section of the application. Please write answer. - al3xnull

6 Answers

9
votes

You could pass the controller in via a binding and then access it inside your component like so:

{{image-editor currentControllerBinding="controller"}}

App.ImageEditorComponent = Ember.Component.extend
  ...
  actions:
    delete: ->
      App.Files.removeObject(object)
      @get('currentController').transitionToRoute('images')
  ...
8
votes

Create action on parent controller.

export default Ember.Controller.extend({
  actions: {
    transInController() {
       this.transitionToRoute('home')
    }
  }
});

Specify this action on component call.

{{some-component transInComponent=(action "transInController")}}

AFTER v3.4.0 (August 27, 2018)

some-component.js

export default Component.extend({
  actions: {
      componentAction1() {
          this.transInComponent();
      }
  }
});

OR simpler in some-component.hbs

<button onclick={{@transInComponent}}>GO HOME</button>

BEFORE v3.4.0

Ember.component.sendAction

"Send Action" from component up to controller

export default Ember.Component.extend({
  actions: {
      componentAction1() {
          this.sendAction('transInComponent');
      }
  }
});
4
votes

A component is supposed to be isolated from its context, so while you could pass in a reference to the controller, that's probably outside the scope of what a component is for. You might want to just stick with using a view with its own controller instead. Check out Views Over Components - An Ember Refactoring Story.

From Ember.js, Sending Actions from Components to Your Application, there's discussion about sending actions from a component up the route hierarchy.

3
votes

A lot of things changed in Ember since the original post. So maybe today the best option would be to pass down to the component a route action that takes care of the transition (maybe using the fancy addon ember-cli-route-action.

Otherwise you can create an initializer with ember g initializer router and inside put in there a code like this one

export function initialize (application) {
   application.inject('route', 'router', 'router:main')
   application.inject('component', 'router', 'router:main') 
}

export default {
  name: 'router',
  initialize
}

This way you can access the router in your component with this.get('router') and, for instance, perform a transition

 this.get('router').transitionTo('images')
1
votes

At component.HBS component file make a {{#link-to "routeDestinationYouWant" }}

For Example:

<section class="component">
{{#link-to "menu.shops.category.content" "param"}}
        <figure id="{{Id}}" class="list-block yellow-bg text-center" {{action "showList" "parameter"}}>
            <section class="list-block-icon white-text my-icons {{WebFont}}"></section>
            <figcaption class="list-block-title black-text">{{{Title}}}</figcaption>
        </figure>
          {{/link-to}}
</section>
0
votes

I've written this answer for another similar question.

If you want to use the router only in a specific component or service or controller, you may try this:

Initialize an attribute with the private service -routing. The - because it's not a public API yet.

router: service('-routing'),

And then inside any action method or other function inside the service or component:

this.get('router').transitionTo(routeName, optionalParams);

Note: It'll be transitionToRoute in a controller.

Link to question: Ember transitionToRoute cleanly in a component without sendAction

Link to answer: https://stackoverflow.com/a/41972854/2968465