If you’ve ever tried to use twitter bootstrap with Ember, you’ll have hit this problem. The “active” class for links in bootstrap that are in a list of links needs to be on the li
element, not on the link:
<li class="active">
<a href="foo">Link</a>
</li>
Up until now the solution to achieve this in ember has been a little ugly IMO:
{{#link-to 'dashboard' tagName="li" href=false}}
{{#link-to 'dashboard' bubbles=false}}
Dashboard
{{/link-to}}
{{/link-to}}
I decided to see if I could come up with something nicer using a component, and I think I did:
{{#link-li}}
{{#link-to 'dashboard'}}
Dashboard
{{/link-to}}
{{/link-li}}
The component is implemented like this - no template necessary:
App.LinkLiComponent = Em.Component.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews').anyBy('active');
}.property('childViews.@each.active')
});
Another win for components, which I am currently enjoying abusing in various ways
15 Likes
Good stuff. Is the Ember.Handlebars.helper call at the end necessary? I thought components automatically registered a helper.
I believe it is necessary if you don’t provide a template, although that may have changed recently
Nice work. An almost identical solution to @mixonic ’s http://emberjs.jsbin.com/iFEvATE/2/edit , which adds a currentWhen
property so that the behaviour can be customised (e.g. to apply an active
class to a parent route link).
However I was scratching my head trying to get it to work with query params, so this looks ideal for that.
Note that with my approach, you can specify currentWhen
on the actual link-to and it will work as expected - in @mixonic ’s approach you have to specify currentWhen
on the wrapper for it to work.
@rwjblue
’s solution is less verbose but seems more inflexible - it wouldn’t handle linking to a route with dynamic segments without alteration at the moment.
Interesting to see all the approaches to this - I hadn’t seen either of those before and probably wouldn’t have attempted this if I had!
It does seem to have changed - it works fine without registering it
I can confirm this.
Great finding, just used it in one of my projects.
@alexspeller , is it alright if I add it to Ember Components website?
P[quote=“muchweb, post:8, topic:5018”]
@alexspeller , is it alright if I add it to Ember Components website?
[/quote]
Please do, that’s fine by me!
@alexspeller a lot has changed since this solution was originally posted. Is this still a valid approach?
I’m a bit of a noob, and not exactly sure how to implement this. Where do I drop this component in my project (using ember-cli 0.1.2)?
Thanks
startup
December 2, 2014, 9:20pm
12
bump.
I’m encountering the same issue. Working + code that will last more than a few months would be ideal.
Anybody?
What issue are you experiencing exactly?
ohcibi
February 20, 2015, 6:34pm
14
app/components/link-li.js
In in Ember 1.13 beta 1, which was published today, the active-link-wrapper doesn’t work anymore. I don’t know if it’s because of something in 1.13, but there are no childViews anymore
https://github.com/alexspeller/ember-cli-active-link-wrapper/blob/master/addon/components/active-link.js
In
return this.get('childViews').anyBy('active');
The length of this.get('childViews')
is always 0.
zoltan
May 28, 2015, 10:55am
18
This works for me quite well:
{{#link-to 'index' tagName="li"}}<a href>Home</a>{{/link-to}}
{{#link-to 'about' tagName="li"}}<a href>About</a>{{/link-to}}
Unfortunately need that empty href
.
2 Likes
It is my solution
Ember.LinkView.reopen({
activeParent: false,
isActive: false,
addIsActiveObserver: function () {
if (this.get('activeParent')) {
this.addObserver('isActive', this, 'activeObserver');
this.activeObserver();
}
}.on('didInsertElement'),
activeObserver: function () {
if (this.get('isActive')) {
this.$().parent().addClass('active');
} else {
this.$().parent().removeClass('active');
}
},
active: Ember.computed('resolvedParams', 'routeArgs', function () {
var isActive = this._super();
Ember.set(this, 'isActive', !!isActive);
return isActive;
})
});
And in template
<ul class="nav navbar-nav navbar-right">
<li>{{#link-to 'index' activeParent=true}}Главная{{/link-to}}</li>
<li>{{#link-to 'news' activeParent=true}}Новости{{/link-to}}</li>
</ul>
Work in v1.11
1 Like
zoltan
October 23, 2015, 9:08am
20
Update: I just extracted in a simple add-on (Ember.js 2.1 or greater): ember-bootstrap-nav-link - npm
ember install ember-bootstrap-nav-link
Using components:
Generate a component with
ember g component nav-link-to
Update the js
file and hbs
file as below.
// nav-link-to.js
import Ember from 'ember';
export default Ember.LinkComponent.extend({
tagName: 'li',
hrefForA: Ember.computed('models', 'qualifiedRouteName', function computeLinkToComponentHref() {
let qualifiedRouteName = this.get('qualifiedRouteName');
let models = this.get('models');
if (this.get('loading')) {
return this.get('loadingHref');
}
let routing = this.get('_routing');
let queryParams = this.get('queryParams.values');
return routing.generateURL(qualifiedRouteName, models, queryParams);
})
});
// nav-link-to.hbs
<a href="{{hrefForA}}">{{yield}}</a>
You can use it in your template.
<ul class="nav navbar-nav">
{{#nav-link-to 'index'}}Home{{/nav-link-to}}
{{#nav-link-to 'about'}}About{{/nav-link-to}}
{{#nav-link-to 'contact'}}Contact{{/nav-link-to}}
</ul>