I\'m trying to create navigation tabs (taken from Twitter Bootstrap):
-
A lot of the proposed solutions here do not work for any recent Ember version (e.g. views being deprecated). Moreover just using the link-to
helper will not solve the issue, as bootstrap expects the active
class to be present on the <li>
not the <a>
!
So I will try to summarize the solutions that do in fact work as of now:
The addon provides a component for this special use case:
<ul class="nav nav-tabs">
{{#active-link}}
{{#link-to "foo"}}Foo{{/link-to}}
{{/active-link}}
{{#active-link}}
{{#link-to "bar"}}Bar{{/link-to}}
{{/active-link}}
</ul>
Taken from https://stackoverflow.com/a/29939821/5556104
ember-bootstrap provides many components that integrate bootstrap functionality into your ember app, amongst them the nav components:
{{#bs-nav type="tabs"}}
{{#bs-nav-item}}
{{#link-to "foo"}}Foo{{/link-to}}
{{/bs-nav-item}}
{{#bs-nav-item}}
{{#link-to "bar"}}Bar{{/link-to}}
{{/bs-nav-item}}
{{/bs-nav}}
Taken from https://stackoverflow.com/a/38279975/5556104
Somewhat hacky, but should work without any additional addon:
<ul class="nav nav-tabs">
{{#link-to "foo" tagName="li"}}
{{#link-to "foo"}}Foo{{/link-to}}
{{/link-to}}
{{#link-to "bar" tagName="li"}}
{{#link-to "bar"}}Bar{{/link-to}}
{{/link-to}}
</ul>
Taken from https://stackoverflow.com/a/23966652/5556104
Not sure if it's very dynamic but try to see solution at http://codebrief.com/2012/07/anatomy-of-an-ember-dot-js-app-part-i-redux-routing-and-outlets/ The main idea is to check state of your app
JavaScript:
function stateFlag(name) {
return Ember.computed(function() {
var state = App.router.currentState;
while(state) {
if(state.name === name) return true;
state = state.get('parentState');
}
return false;
}).property('App.router.currentState');
}
ApplicationController: Ember.Controller.extend({
isHome: stateFlag('home'),
isSections: stateFlag('sections'),
isItems: stateFlag('items')
})
Handlebars:
<li class="home" {{bindAttr class="isHome:active"}}>
</li>
<li class="sections" {{bindAttr class="isSections:active"}}>
</li>
<li class="items" {{bindAttr class="isItems:active"}}>
</li>
Update: pangratz's solution looks prettier
Some of the above suggestions are still valid for twitter bootstrap case. You can also try something like this
{{#link-to 'dashboard' tagName='li'}}
{{#link-to 'dashboard'}}Link Title{{/link-to}}
{{/link-to}}
link-to
with li
tagName applies the active class to the lilink-to
would be a anchor
element which gives you Open in New Tab
functionality when right-clickedYou can also change the isActive method into something like this:
isActive: function() {
return App.getPath('router.currentState.path') === "root.firms";
}.property("App.router.currentState"),
or
isActive: function() {
return this.get('controller.target.currentState.path') === "root.firms";
}.property("controller.target.currentState"),
If you're using Ember >= 1.11, then https://stackoverflow.com/a/14501021/65542 below is the correct answer.
I would create a NavigationView
, see http://jsfiddle.net/pangratz666/z8ssG/:
Handlebars:
<script type="text/x-handlebars" data-template-name="navigation">
<ul class="nav nav-tabs">
{{#view view.NavItemView item="home" }}
<a {{action gotoHome}} >Home</a>
{{/view}}
{{#view view.NavItemView item="profiles" }}
<a {{action gotoProfiles}} >Profiles</a>
{{/view}}
{{#view view.NavItemView item="messages" }}
<a {{action gotoMessages}} >Messages</a>
{{/view}}
</ul>
</script>
JavaScript:
App.NavigationView = Em.View.extend({
templateName: 'navigation',
selectedBinding: 'controller.selected',
NavItemView: Ember.View.extend({
tagName: 'li',
classNameBindings: 'isActive:active'.w(),
isActive: function() {
return this.get('item') === this.get('parentView.selected');
}.property('item', 'parentView.selected').cacheable()
})
});
And inside your route's connectOutlets
you have to set the current navigation item via router.set('navigationController.selected', 'home');
...
Also take a look at the ember-bootstrap repository, which wraps this and more features of Bootstrap inside Ember.js
Sooner or later want to change the naming of your states or whatever you have to go through the code AND the view as well, also adding a function to transitionTo every route seems not desirable. My approach is a bit more programmatic and modularized:
# Parent View-Tamplate, holding the navbar DOM elements
App.NavView = Ember.View.extend(
controller: App.NavArrayController
templateName: "ember-nav"
)
# We push NavItems into this array
App.NavArrayController = Ember.ArrayController.create(
content: Ember.A([])
)
# NavItem has two settable properties and
# an programmatic active state depending on the router
App.NavItem = Ember.Object.extend(
title: ''
goto: null # <=this is the name of the state we want to go to!
active: (->
if App.router.currentState.name == @.get "goto"
true
else
false
).property('App.router.currentState.name').cacheable()
)
# the actual NavElement which gets the class="active" if the
# property "active" is true, plus a on-click binding to
# make the Router transition to this state
App.NavItemView = Ember.View.extend(
tagName: "li"
classNameBindings: ["active"]
click: ->
App.router.transitionTo(@get('goto'))
false
)
nav-view.hbs (for twitter-bootstrap-style navs)
<div class="nav-collapse collapse">
<ul class="nav">
{{#each App.NavArrayController}}
{{#view App.NavItemView classBinding="active" gotoBinding="goto"}}
<a href="#" {{bindAttr data-goto="goto"}}> {{title}}</a>
{{/view}}
{{/each}}
</ul>
</div>
This way, I can just create and mess around with my routes in the Router, and keep the Nav-Definitions side-by-side:
# put this somewhere close to the Router
App.NavArrayController.pushObjects(
[
App.NavItem.create(
title: 'Home'
goto: 'home'
),
App.NavItem.create(
title: 'Chat'
goto: 'chat'
),
App.NavItem.create(
title: 'Test'
goto: 'test'
)
]
)