What is the best way for Parent Template to depend on data in Child Controller?


#1

Hello, first post and Ember n00b here. Thank you in advance for your input.

My objective is to visually indicate in the UI which section my user is in. I’m able to solve this, but it seems clunky so I’m guessing there’s a better way.

First, I understand that {{#link-to ...} automatically renders class="active" when the rendered link matches the current route, but my problem is that {{#link-to}} always renders an <a> tag while my CSS is looking for <li class="active"><a>...</li></a> and adjusting CSS is not an option here.

Here’s how I managed to solve this, but it’s a little ugly. First, here is my routes file:

Router.map(function() {
    this.resource("standardShell", { path: "/" }, function() {
       this.resource("reminders", { path: "/reminders" }, function() {
          this.route("dashboard");
       }
    }
}

I am visiting the URL http://myApp/reminders/dashboard.

I want my reminders template to indicate that it’s on the dashboard page. My reminders.hbs template is as follows:

<li {{bind-attr class="isActiveSectionIsDashboard:active"}}>{{#link-to "reminders.dashboard"}}Dashboard{{/link-to}}</li>

The isActiveSectionIsDashboard is calling my RemindersController which is defined as follows:

var RemindersController = Ember.Controller.extend({
    activeSection: "",

    // Set the active navigation section. Called by child view.
    setActiveSection: function( activeSection ) {
        this.set('activeSection', activeSection);
    },

    // Boolean functions for Handlebars to know which section is active
    isActiveSectionIsDashboard: function() { 
           return this.get('activeSection') === "dashboard"; 
        }.property('activeSection'),\
});

Now here’s where it gets messy. The RemindersController needs its activeSection property set from the child controller/view/route. So, here’s my DashboardController:

var DashboardController = Ember.Controller.extend({
    needs: "reminders",
    sectionName: "dashboard"
});

and finally here’s my DashboardView:

var DashboardView = Ember.View.extend({

    didInsertElement: function() {
        var controller = this.get('controller.controllers.reminders');
        controller.setActiveSection( this.get('controller').sectionName );
    }

});

This just seems like an incredible amount of work just for the child page to set a property at the parent page level. Am I missing something, or is this the best way to handle this?

Thanks again for your input!

Josh


#2

Not an expert, but I would have tried something like this, since we can always access the currentPath and currentRoute from the application controller, which would always be active on all pages. Lets see what others have to say

StandardShellController = Ember.Controller.extend({
  needs: ['application'],
  init: function() {
    this._super();
   var currentPath = this.get('controllers.application.currentPath') ;
  Ember.run.scheduleOnce('afterRender', window, function(){
     // You can make the href from current path here (which will have something like  reminders.dashboard), so you could make an href from this, that can be #/reminders/dashboard, then use jquery to look for the anchor that has this href, go to its parent and apply the class.
   });
});

#3

You might find a better solution here: Bootstrap, active links and LIs


#4

Much better, thanks :smile:


#5

The linked solution was great, thank you! Very simple and elegant. As a result, I deleted every bit of code from my original post and replaced it with this in my template:

<ul class="nav nav-tabs">
        {{#link-li}}    {{#link-to "reminders.dashboard"}}Dashboard{{/link-to}}             {{/link-li}}
        {{#link-li}}    {{#link-to "reminders.page2"}}Page 2{{/link-to}}                 {{/link-li}}
        {{#link-li}}    {{#link-to "reminders.page3"}}Page 3{{/link-to}}             {{/link-li}}
        {{#link-li}}    {{#link-to "reminders.page4"}}Page 4{{/link-to}}               {{/link-li}}
        {{#link-li}}    {{#link-to "reminders.page5"}}Page 5{{/link-to}}    {{/link-li}}
</ul>

and this for /app/components/link-li.js:

var LinkLiComponent = Em.Component.extend({
    tagName: 'li',
    classNameBindings: ['active'],
    active: function() {
        return this.get('childViews').anyBy('active');
    }.property('childViews.@each.active')
});

export default LinkLiComponent;

#6

Another option could be something like this:

<ul class="nav nav-tabs">
  {{#link-to "reminders.dashboard" tagName="li"}}<a href="#">Dashboard</a>{{/link-to}}
  {{#link-to "reminders.page2" tagName="li"}}<a href="#">Dashboard</a>{{/link-to}}
</ul>

The <a> tag is optional (in case it is required for your styling), the href is there to trigger default link styling (cursor, etc).