问题
I\'m trying to create navigation tabs (taken from Twitter Bootstrap):
<ul class=\"nav nav-tabs\">
<li class=\"active\"><a href=\"#\">Home</a></li>
<li><a href=\"#\">Profile</a></li>
<li><a href=\"#\">Messages</a></li>
</ul>
The active tab is marked with class=\"active\"
.
There is great example of static navbar and Router/Outlet feature at http://jsfiddle.net/schawaska/pfbva/, but I can\'t understand how to create a dynamic navbar/menu/tab view.
As far as I understand, it is possible to use class bindings in each menu item:
classNameBindings: [\'isActive:active\']
But where is the right place to switch isActive attributes ?
回答1:
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
回答2:
Ember 1.11+:
{{#link-to "dashboard" tagName="li"}}
<a href="{{view.href}}">Dashboard</a>
{{/link-to}}
Ember < 1.11 (bind-attr
required):
{{#link-to "dashboard" tagName="li"}}
<a {{bind-attr href="view.href"}}>Dashboard</a>
{{/link-to}}
回答3:
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}}
- The
link-to
withli
tagName applies the active class to the li - The inner
link-to
would be aanchor
element which gives youOpen in New Tab
functionality when right-clicked
回答4:
Recently an Ember-cli addon came available to just do this. It is called ember-cli-active-link-wrapper.
Install: ember install ember-cli-active-link-wrapper
You can use it like this:
{{#active-link}}
{{link-to "Index" "index"}}
{{/active-link}}
which results in:
<li class='active'>
<a href="/" class='active'>Index</a>
</li>
回答5:
I know this is old post, but here are updates for Ember 2.4.0
For creating links you can write
{{#link-to 'photoGallery'}}
Great Hamster Photos
{{/link-to}}
or
{{link-to 'Great Hamster Photos' 'photoGallery'}}
Ember will automatically set class to active when current route matches link's route (in this example photoGallery
).
If you want to control 'active' class on other routes as well, you can do it by setting current-when
attribute.
{{#link-to 'photoGallery' current-when='photoGallery photoPreview'}}
Great Hamster Photos
{{/link-to}}
This link will have active
class on both photoGallery
and photoPreview
routes.
https://github.com/emberjs/ember.js/blob/v2.4.0/packages/ember-routing-views/lib/components/link-to.js#L140
回答6:
Handlebars
<ul class="nav">
<li>{{#linkTo "index"}}Index{{/linkTo}}</li>
<li>{{#linkTo "about"}}About{{/linkTo}}</li>
</ul>
Javascript
App.Router.map(function() {
this.route("about");
});
It will add active class automatically based on route. Note: It is tested using ember-1.0.0-pre.4.js
回答7:
You 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"),
回答8:
I see this question is quite old, but if you upgraded Ember.js to the RC3 you can use tagName
property, like:
{{#link-to messages tagName="li"}}Messages{{/link-to}}
Here is the API - http://emberjs.com/api/classes/Ember.LinkView.html
回答9:
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
回答10:
Here's a full working solution:
View:
App.NavView = Ember.View.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews.firstObject.active');
}.property()
});
Template:
<ul>
{{#each item in controller}}
{{#view App.NavView}}
{{#linkTo "item" item tagName="li"}}
<a {{bindAttr href="view.href"}}>
{{ item.name }}
</a>
{{/linkTo}}
{{/view}}
{{/each}}
</ul>
回答11:
Beginning with v0.8.0 ember-bootstrap supports navs, including handling the active state correctly. And that without any link-to/tagName kind of hacks:
{{#bs-nav type="pills"}}
{{#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}}
See http://kaliber5.github.io/ember-bootstrap/api/classes/Components.Nav.html
回答12:
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:
use ember-cli-active-link-wrapper
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
use ember-bootstrap
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
link-to Hack
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
回答13:
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'
)
]
)
回答14:
baijum's answer above is mostly correct however in the latest versions of Ember the "bind-attr" is deprecated. Here is the new way to write it:
{{#link-to "dashboard" tagName="li"}}
<a href="{{view.href}}">Dashboard</a>
{{/link-to}}
As you can see, it's even easier and kind of works like magic..
回答15:
Like other people said, using {{#link-to}}
to link to an existing route
, when that route is current URL, {{#link-to}}
will automatically add active
to its CSS classes.
See Ember issue 4387
来源:https://stackoverflow.com/questions/11628489/emberjs-how-to-mark-active-menu-item-using-router-infrastructure