Combine linkTo and action helpers in Ember.js

34,742

Solution 1

Ember Link Action addon

This is OK for SEO solution!

Install addon

ember install ember-link-action

Usage

You can pass closure action as invokeAction param to {{link-to}} component:

{{#link-to 'other-route' invokeAction=(action 'testAction')}}
  Link to another route
{{/link-to}}

To pass parameters to action you can use:

{{#link-to 'other-route' invokeAction=(action 'testAction' param1 param2)}}
  Link to another route
{{/link-to}}

Compatibility

Automated test suite confirms that addon works with 1.13 up to latest Ember 3 releases.

It works with a release, beta and canary versions of Ember.

Addon GitHub repository. Contributions are welcome.

Solution 2

Update: See Michael Lang's comment below for Ember 1.8.1+

The problem with Myslik's answer (not using link-to at all but instead using an action and then transitionToRoute) is that it's useless for SEO, search engine bots will see nothing.

If you want what your link is pointing to to be indexed, it's easiest to have a good old <a href=x> in there. It's best to use link-to so that your link URLs are kept in sync with your route URLs. The solution I use gives both an action to do the work and a handy link-to to index the pages.

I override some functionality of Ember.LinkView:

Ember.LinkView.reopen({
  action: null,
  _invoke: function(event){
    var action = this.get('action');
    if(action) {
      // There was an action specified (in handlebars) so take custom action
      event.preventDefault(); // prevent the browser from following the link as normal
      if (this.bubbles === false) { event.stopPropagation(); }

      // trigger the action on the controller
      this.get('controller').send(action, this.get('actionParam'));
      return false; 
    }           

    // no action to take, handle the link-to normally
    return this._super(event);
  }
});

Then I can specify which action to take and what to pass the action in Handlebars:

<span {{action 'view' this}}>
  {{#link-to 'post' action='view' actionParam=this}}
    Post Title: {{title}}
  {{/link-to}}
</span>

In the controller:

App.PostsIndexController = Ember.ArrayController.extend({
  actions: {
    view: function(post){
      this.transitionToRoute('post', post);
    }
  }
}

This way, when I cache a rendered copy of the page and serve that to an indexing bot, the bot will see a real link with an URL and follow it.

(note also that transitionTo is now deprecated in favour of transitionToRoute)

Solution 3

None of these combinations will work in Ember.js, but you do not need to combine these two helpers. Why don't you just use action helper and let it bubble to controller or route? There you can use transitionToRoute in controller or transitionTo in route.

For example in controller you could have code like this:

App.PostsController = Ember.ArrayController.extend({
    clear: function () {
        // implement your action here
        this.transitionToRoute('index');
    }
});

Solution 4

This works fine in 1.6.0-beta.5:

<span {{action "someAction"}}>
  {{#link-to "some.route"}}
    Click Me
  {{/link-to}}
</span>

The link will happen and then the click will bubble up to the action handler. It's documented (albeit indirectly) here.

Edit: corrected syntax in opening link tag

Solution 5

I like Cereal Killer's approach for its simplicity, but unfortunately it exhibits a problem for me. When the browser navigates to another route, it restarts the Ember application.

As of Ember 2.6, the following simple approach does the trick:

<span {{action 'closeNavigationMenu'}}> {{#link-to 'home' preventDefault=false}} Go Home {{/link-to}} </span>

This achieves the following:

  • navigates to route 'home'
  • action 'closeNavigationMenu' is invoked
  • on mouseover, browser displays link that will be followed (for SEO and better UX)
  • browser navigation does not result in reboot of Ember app
Share:
34,742
Daniel Kmak
Author by

Daniel Kmak

LinkedIn: https://www.linkedin.com/in/kmakdaniel/. Job offers: [email protected]. GitHub: Kuzirashi

Updated on June 09, 2021

Comments

  • Daniel Kmak
    Daniel Kmak almost 3 years

    I need to combine linkTo and action helpers in Ember.js. My code is:

    {{#link-to 'index'}}<span {{action 'clear'}}>Clear</span>{{/link-to}}
    

    But I would like to make this something like this:

    {{#link-to 'index' {{action 'clear'}} }}Clear{{/link-to}}
    

    And also:

    <li>
        {{#link-to 'support'}}
            <span {{action 'myAction' 'support'}}>Support</span>
        {{/link-to}}
    </li>
    

    To:

    <li>
        {{#link-to 'support' {{action 'myAction' 'support'}} }}Support{{/link-to}}
    </li>
    

    How can I achieve this?

    Solution

    Check my answer for Ember 2.0 compatible, OK for SEO solution.

  • Birju Shah
    Birju Shah almost 11 years
    why is it then that in Fireup ember tutorial they do it this this way <a href='#' {{action "addFood" this}}>
  • BJ McDuck
    BJ McDuck about 10 years
    I'm not an SEO expert. But I've heard briefly of Google's new way of indexing sites that use JS frameworks. If you had a setup to use the new JS SEO system, would they still be looking at the href attributes?
  • Noland
    Noland about 10 years
    I would assume that any future/pending solution for at least the next few years would still look at the href attribute. Ignoring anchor tags would be a major change to the how the web is indexed. The above solution could possibly be made redundant if some some search bots find a brilliant way to run JS but I can't see it not working.
  • Andreas Andreou
    Andreas Andreou almost 10 years
    To perfect this great answer, it's best imho to also check if the link-to is disabled ( via this.get('_isDisabled') ) before invoking the action.
  • Michael Lang
    Michael Lang over 9 years
    Thanks for this answer! It appears the design of the link-to helper may have changed since this was posted though. In Ember 1.8.1, I had to replace this.get('controller').send(action, this.get('actionParam')); with this.sendAction('action', this.get('actionParam')); in order to get the action to propagate to my route's controller.
  • Epirocks
    Epirocks about 6 years
    At this day and age in Ember.js you can inject the router service and transition without it having to bubble to the top
  • Sam Sedighian
    Sam Sedighian over 4 years
    this goes against a few a11y guidelines please do not do this!
  • Andrey Mikhaylov - lolmaus
    Andrey Mikhaylov - lolmaus over 2 years
    Just ran into this issue again... And this solution does not work for me: adding the {{on modifier to the LinkTo component causes is to stop making the transition. Ember 3.28.6.
  • Cory Loken
    Cory Loken over 2 years
    This works on 3.28.8 as I just added it to a project. I had to make sure the method I was calling had the action decorator, but seems to work great.