Skip to content

Instantly share code, notes, and snippets.

@tim-evans
Last active June 10, 2016 20:53
Show Gist options
  • Save tim-evans/d9e07256baaa8d61944205a66a29336b to your computer and use it in GitHub Desktop.
Save tim-evans/d9e07256baaa8d61944205a66a29336b to your computer and use it in GitHub Desktop.
Router Service RFC polyfill
import Ember from 'ember';
const { get, observer } = Ember;
/**
Returns whether a route is currently active
Example:
```handlebars
<li class="{{if (is-active 'blog.post') 'active'}}">
{{link-to post.name "blog.post" post}}
</li>
```
```handlebars
<li class="{{if (is-active 'blog.posts' (query-params page=1)) 'active'}}">
{{link-to post.name "blog.posts" (query-params page=1)}}
</li>
```
@public
@method is-active
@param {String} routeName The route name to check
@param {Object} ...models The context for the given route to be tested
@param {QueryParams} [queryParams] The query params to test against for the current route.
@return {Boolean} Whether the route is active
@for Helpers
*/
export default Ember.Helper.extend({
router: Ember.inject.service(),
compute([routeName, ...models], hash) {
let queryParams = models[models.length - 1];
if (queryParams && queryParams.isQueryParams) {
queryParams = models.pop().values;
} else {
queryParams = null;
}
return get(this, 'router').isActive(routeName, ...models, queryParams);
},
currentRouteChanged: observer('router.currentRoute', function () {
this.recompute();
})
});
import Ember from 'ember';
const { get, set, getOwner } = Ember;
const { alias, reads } = Ember.computed;
/**
Polyfill for the [Router Service RFC](https://github.com/emberjs/rfcs/pull/95)
used here as a way to provide a boundary between Ember and other parts
of our apps. This boundary can be exploited to write tests that mock
out the router with a dummy object.
@class Router
@namespace Services
@public
*/
export default Ember.Service.extend({
init() {
let owner = getOwner ? getOwner(this) : this.container;
let router = owner.lookup('router:main');
set(this, '_router', router);
router.on('didTransition', () => {
set(this, '_routeSegments', Ember.A(router.router.currentHandlerInfos));
});
// Set route segments on initialization of the router
if (router.router == null) {
let initRouterJs = router._initRouterJs;
router._initRouterJs = () => {
initRouterJs.call(router);
this.interceptRouterEvents(router.router);
}
} else {
this.interceptRouterEvents(router.router);
}
},
/**
@private
Intercept router events and notify the service of any changes made.
*/
interceptRouterEvents(router) {
set(this, '_routeSegments', Ember.A(router.currentHandlerInfos || []));
let triggerEvent = router.triggerEvent;
router.triggerEvent = (...args) => {
let [handlerInfos, , [event]] = args;
triggerEvent.apply(router, args);
if (event === 'queryParamsDidChange') {
set(this, '_routeSegments', Ember.A(handlerInfos));
}
};
},
/**
The `location` property determines the type of URL's that your
application will use.
The following location types are currently available:
* `history` - use the browser's history API to make the URLs look just like any standard URL
* `hash` - use `#` to separate the server part of the URL from the Ember part: `/blog/#/posts/new`
* `none` - do not store the Ember URL in the actual browser URL (mainly used for testing)
* `auto` - use the best option based on browser capabilites: `history` if possible, then `hash` if possible, otherwise `none`
Note: If using ember-cli, this value is defaulted to `auto` by the `locationType` setting of `/config/environment.js`
@property location
@default 'hash'
@see {Ember.Location}
@public
*/
location: alias('_router.location'),
/**
Represents the URL of the root of the application, often '/'. This prefix is
assumed on all routes defined on this router.
@property rootURL
@default '/'
@public
*/
rootURL: alias('_router.rootURL'),
/**
Represents the current route. This property is immutable,
and is updated by the service with every transition.
@method currentRouteName
@return {Object} The current route object, as a RouteInfo.
@public
*/
currentRoute: reads('_routeSegments.lastObject'),
/**
Represents the current route name.
@method currentRouteName
@return {String} The current route name.
@public
*/
currentRouteName: reads('currentRoute.name'),
/**
Represents the current URL.
@method currentURL
@return {String} The current URL.
@public
*/
currentURL: reads('_router.url'),
/**
Returns the currently active route segment
for the model.
@method routeFor
@params {Object} model The model to test against
@params {Object} [conditions] Conditions to match
@params {Regex|String} [conditions.name] The name to check against.
@return {Object} The current route object, as a RouteInfo.
*/
routeFor(model, conditions={}) {
return get(this, '_routeSegments').find(function (segment) {
let matches = segment.context === model;
if (matches && conditions.name) {
if (typeof conditions.name === 'string') {
matches = segment.name === conditions.name;
} else {
matches = conditions.name.test(segment.name);
}
}
return matches;
});
},
/**
Returns the name of the active route segment
for the model given.
@method routeNameFor
@params {Object} model The model to test against
@params {Object} [conditions] Conditions to match
@params {Regex|String} [conditions.name] The name to check against.
@return {String} The current name of the route segment for the model.
*/
routeNameFor(model, conditions={}) {
let segment = this.routeFor(model, conditions);
return segment && segment.name;
},
/**
This takes the same arguments as transitionTo, but
instead of initiating the transition it returns the
resulting URL as a string.
@method url
@param {String} routeName The route name to generate a URL for.
@param {Object} ...models The necessary context to provide to generate the URL.
@param {Object} queryParams Query parameters to append to the URL.
*/
url(routeName, ...models /*, queryParams */) {
return get(this, '_router').generate(routeName, ...models, { queryParams: models.pop() });
},
/**
Determines if the supplied route is currently active.
@method isActive
@param {String} routeName The route name to check against.
@param {Object} ...models The context for the route.
@param {Object} queryParams Query parameters to test against.
@return {Boolean}
@public
*/
isActive(routeName, ...models /*, queryParams */) {
return get(this, '_router').isActive(routeName, ...models, { queryParams: models.pop() });
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment