Lux
Lux

Reputation: 18240

How can I render a block only if a specific route is active?

I wanna render a block in Ember Handlebars only, if a specific route is active. So, how can I create a 'ifRoute' helper, with the same conditons then the 'active' class on the 'linkTo' helper?

I want this, because I've a two layer navigation. So, I want to show the sub-navigation only, if the head navigation point is active. I dont wanna use the 'active' class, because I use lazy loading and I only want to load the sub navigation when the head navigation point is active.

So, what I want to do is:

<ul>
    {{#each assortmentGroups}}
        <li>
            {{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
            {{#ifRoute "assortmentGroup" this}}
                <ul>
                    {{#each itemCategories}}
                        <li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
                    {{/each}}
                </ul>
            {{/ifRoute}}
        </li>
    {{/each}}
<ul>

How can I do this or is there a better solution?

Thanks

Upvotes: 8

Views: 4905

Answers (4)

Vasyl Marchuk
Vasyl Marchuk

Reputation: 131

Just add to the controller:

needs: ['application'], 
isCorrectRouteActive: Ember.computed.equal('controllers.application.currentRouteName', 'correctRoute')

Similarly:

isCorrectPathActive: Ember.computed.equal('controllers.application.currentPath', 'correct.path')
isCorrectURLActive: Ember.computed.equal('controllers.application.currentURL', 'correctURL')

I am quite sure latest Ember does the rest

Upvotes: 8

Karl Glaser
Karl Glaser

Reputation: 849

I found an easy way to check if a route is active, but to get this into a computed property may not be so easy.

// Test if you are currently in a route by it's lowercase name
App.isInRoute = function(name) {
    return App.Router.router.currentHandlerInfos.mapProperty('name').contains(name);
}

To use:

App.isInRoute('posts.show'); // true if in the route

Upvotes: 1

Lux
Lux

Reputation: 18240

After investigating the ember code for the linkTo and if helpers, the answer from intuitivepixel and a blog post about writing my own bound block helper, I've found a solution:

var resolveParams = Ember.Router.resolveParams;

var resolvedPaths = function(options) {
    var types = options.options.types.slice(1),
    data = options.options.data;

    return resolveParams(options.context, options.params, { types: types, data: data });
};

Ember.Handlebars.registerHelper('ifRoute', function(name) {
    var options = [].slice.call(arguments, -1)[0];
    var params = [].slice.call(arguments, 1, -1);
    var theResolvedPaths = resolvedPaths({ context: this, options: options, params: params });
    var router = options.data.keywords.controller.container.lookup('router:main');

    var self = this;
    var evaluateIsCurrentRoute = function() {
        self.set('current_route_is_active_bool_for_ifroute', (function() {
            return router.isActive.apply(router, [name].concat(theResolvedPaths)) ||
                router.isActive.apply(router, [(name + '.index')].concat(theResolvedPaths));
        })());
    };

    evaluateIsCurrentRoute();
    router.addObserver('url', evaluateIsCurrentRoute);

    options.contexts = null;

    return Ember.Handlebars.helpers.boundIf.call(this, 'current_route_is_active_bool_for_ifroute', options);
});

Upvotes: 1

intuitivepixel
intuitivepixel

Reputation: 23322

Here are two possible options, although for both you first have to save the currentPath in your ApplicationController to have access to it whenever you need it:

var App = Ember.Application.create({
  currentPath: ''
});

App.ApplicationController = Ember.ObjectController.extend({
  updateCurrentPath: function() {
    App.set('currentPath', this.get('currentPath'));
  }.observes('currentPath')
});

Using a computed property

Then in the controller backing up the template, let's say you have a NavigationController you create the computed property and define also the dependency to the ApplicationController with the needs API to gather access, then in the CP you check if the currentPath is the one you want:

App.NavigationController = Ember.Controller.extend({
  needs: 'application',
  showSubMenu: function(){
    var currentPath = this.get('controllers.application.currentPath');
    return (currentPath === "assortmentGroup");
  }.property('controllers.application.currentPath')
});

So you can use a simple {{#if}} helper in your template:

...
{{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
  {{#if showSubMenu}}
    <ul>
      {{#each itemCategories}}
        <li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
      {{/each}}
    </ul>
  {{/if}}
</li>
...

Using a custom '{{#ifRoute}}' helper

But if your really want a custom helper to deal with your condition then this is how you could do it, note that the currentPath stored on your application is still needed since we need a way to get the value of the current route:

Ember.Handlebars.registerHelper('ifRoute', function(value, options) {
  if (value === App.get('currentPath')) {
    return options.fn(this);
  }
  else {
    return options.inverse(this);
  }
});

And then you could use it like this:

...
  {{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
  {{#ifRoute "assortmentGroup"}}
    <ul>
      {{#each itemCategories}}
        <li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
      {{/each}}
    </ul>
  {{/ifRoute}}
</li>
...

See here also a simple Demo of the "custom helper" solution: http://jsbin.com/izurix/7/edit

Note: with the second solution there is a catch! Since bound helpers do not support blocks (in embers handlebars customization) I used a simple helper that does not reevaluate the condition depending on bindings which is may not what you want.

Hope it helps.

Upvotes: 4

Related Questions