Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,10 @@ for a detailed explanation.
inherit from `Ember.ObjectProxy` directly.

Added in [#5156](https://github.com/emberjs/ember.js/pull/5156)

* `ember-routing-multi-current-when`

Allows the `link-to` helper's currentWhen property to accept multiple routes
using a `|` delimiter, for more control over a link's active state.

Added in [#3673](https://github.com/emberjs/ember.js/pull/3673)
1 change: 1 addition & 0 deletions features.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ember-routing-linkto-target-attribute": null,
"ember-routing-will-change-hooks": null,
"ember-routing-consistent-resources": true,
"ember-routing-multi-current-when": null,
"event-dispatcher-can-disable-event-manager": null,
"ember-metal-is-present": null,
"property-brace-expansion-improvement": null,
Expand Down
82 changes: 51 additions & 31 deletions packages/ember-routing-handlebars/lib/helpers/link_to.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,47 +290,67 @@ var LinkView = Ember.LinkView = EmberComponent.extend({
or the application's current route is the route the `LinkView` would trigger
transitions into.

The `currentWhen` property can match against multiple routes by separating
route names using the `|` character.

@property active
**/
active: computed('loadedParams', function computeLinkViewActive() {
if (get(this, 'loading')) { return false; }

var router = get(this, 'router'),
loadedParams = get(this, 'loadedParams'),
contexts = loadedParams.models,
currentWhen = this.currentWhen || loadedParams.targetRouteName,
handlers = router.router.recognizer.handlersFor(currentWhen),
leafName = handlers[handlers.length-1].handler,
maximumContexts = numberOfContextsAcceptedByHandler(currentWhen, handlers);

// NOTE: any ugliness in the calculation of activeness is largely
// due to the fact that we support automatic normalizing of
// `resource` -> `resource.index`, even though there might be
// dynamic segments / query params defined on `resource.index`
// which complicates (and makes somewhat ambiguous) the calculation
// of activeness for links that link to `resource` instead of
// directly to `resource.index`.

// if we don't have enough contexts revert back to full route name
// this is because the leaf route will use one of the contexts
if (contexts.length > maximumContexts) {
currentWhen = leafName;
}
var router = get(this, 'router');
var loadedParams = get(this, 'loadedParams');
var contexts = loadedParams.models;
var currentWhen = this.currentWhen;
var isCurrentWhenSpecified = Boolean(currentWhen);
currentWhen = currentWhen || loadedParams.targetRouteName;

function isActiveForRoute(routeName) {
var handlers = router.router.recognizer.handlersFor(routeName);
var leafName = handlers[handlers.length-1].handler;
var maximumContexts = numberOfContextsAcceptedByHandler(routeName, handlers);

// NOTE: any ugliness in the calculation of activeness is largely
// due to the fact that we support automatic normalizing of
// `resource` -> `resource.index`, even though there might be
// dynamic segments / query params defined on `resource.index`
// which complicates (and makes somewhat ambiguous) the calculation
// of activeness for links that link to `resource` instead of
// directly to `resource.index`.

// if we don't have enough contexts revert back to full route name
// this is because the leaf route will use one of the contexts
if (contexts.length > maximumContexts) {
routeName = leafName;
}

var args = routeArgs(currentWhen, contexts, null);
var isActive = router.isActive.apply(router, args);
if (!isActive) { return false; }
var args = routeArgs(routeName, contexts, null);
var isActive = router.isActive.apply(router, args);
if (!isActive) { return false; }

if (Ember.FEATURES.isEnabled("query-params-new")) {
if (!this.currentWhen && leafName === loadedParams.targetRouteName) {
var visibleQueryParams = {};
merge(visibleQueryParams, loadedParams.queryParams);
router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams);
isActive = shallowEqual(visibleQueryParams, router.router.state.queryParams);
if (Ember.FEATURES.isEnabled("query-params-new")) {
if (!isCurrentWhenSpecified && leafName === loadedParams.targetRouteName) {
var visibleQueryParams = {};
merge(visibleQueryParams, loadedParams.queryParams);
router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams);
isActive = shallowEqual(visibleQueryParams, router.router.state.queryParams);
}
}
return isActive;
}

if (isActive) { return get(this, 'activeClass'); }
if (Ember.FEATURES.isEnabled("ember-routing-multi-current-when")) {
currentWhen = currentWhen.split('|');
for (var i = 0, len = currentWhen.length; i < len; i++) {
if (isActiveForRoute(currentWhen[i])) {
return get(this, 'activeClass');
}
}
} else {
if (isActiveForRoute(currentWhen)) {
return get(this, 'activeClass');
}
}
}),

/**
Expand Down
37 changes: 37 additions & 0 deletions packages/ember/tests/helpers/link_to_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,43 @@ test("The {{link-to}} helper does not disregard currentWhen when it is given exp
equal(Ember.$('#other-link.active', '#qunit-fixture').length, 1, "The link is active when currentWhen is given for explicitly for a resource");
});

if (Ember.FEATURES.isEnabled("ember-routing-multi-current-when")) {
test("The {{link-to}} helper supports multiple currentWhen routes", function() {
Router.map(function(match) {
this.resource("index", { path: "/" }, function() {
this.route("about");
});
this.route("item");
this.route("foo");
});

Ember.TEMPLATES.index = Ember.Handlebars.compile("<h3>Home</h3>{{outlet}}");
Ember.TEMPLATES['index/about'] = Ember.Handlebars.compile("{{#link-to 'item' id='link1' currentWhen='item|index'}}ITEM{{/link-to}}");
Ember.TEMPLATES['item'] = Ember.Handlebars.compile("{{#link-to 'item' id='link2' currentWhen='item|index'}}ITEM{{/link-to}}");
Ember.TEMPLATES['foo'] = Ember.Handlebars.compile("{{#link-to 'item' id='link3' currentWhen='item|index'}}ITEM{{/link-to}}");

bootApplication();

Ember.run(function() {
router.handleURL("/about");
});

equal(Ember.$('#link1.active', '#qunit-fixture').length, 1, "The link is active since currentWhen contains the parent route");

Ember.run(function() {
router.handleURL("/item");
});

equal(Ember.$('#link2.active', '#qunit-fixture').length, 1, "The link is active since you are on the active route");

Ember.run(function() {
router.handleURL("/foo");
});

equal(Ember.$('#link3.active', '#qunit-fixture').length, 0, "The link is not active since currentWhen does not contain the active route");
});
}

test("The {{link-to}} helper defaults to bubbling", function() {
Ember.TEMPLATES.about = Ember.Handlebars.compile("<div {{action 'hide'}}>{{#link-to 'about.contact' id='about-contact'}}About{{/link-to}}</div>{{outlet}}");
Ember.TEMPLATES['about/contact'] = Ember.Handlebars.compile("<h1 id='contact'>Contact</h1>");
Expand Down