Skip to content

Commit

Permalink
Merge pull request emberjs#15414 from rwjblue/router-service-is-active
Browse files Browse the repository at this point in the history
[FEATURE ember-routing-router-service] isActive and Cleanup
  • Loading branch information
rwjblue authored Jun 26, 2017
2 parents 8d37123 + 3b9489c commit 2d9e3aa
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 36 deletions.
2 changes: 1 addition & 1 deletion packages/ember-application/lib/system/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ function commonSetupRegistry(registry) {

if (EMBER_ROUTING_ROUTER_SERVICE) {
registry.register('service:router', RouterService);
registry.injection('service:router', 'router', 'router:main');
registry.injection('service:router', '_router', 'router:main');
}
}

Expand Down
95 changes: 86 additions & 9 deletions packages/ember-routing/lib/services/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ import {
Service,
readOnly
} from 'ember-runtime';
import { assign } from 'ember-utils';
import RouterDSL from '../system/dsl';


function shallowEqual(a, b) {
let k;
for (k in a) {
if (a.hasOwnProperty(k) && a[k] !== b[k]) { return false; }
}
for (k in b) {
if (b.hasOwnProperty(k) && a[k] !== b[k]) { return false; }
}
return true;
}

/**
The Router service is the public API that provides component/view layer
Expand All @@ -17,11 +31,11 @@ import {
@category ember-routing-router-service
*/
const RouterService = Service.extend({
currentRouteName: readOnly('router.currentRouteName'),
currentURL: readOnly('router.currentURL'),
location: readOnly('router.location'),
rootURL: readOnly('router.rootURL'),
router: null,
currentRouteName: readOnly('_router.currentRouteName'),
currentURL: readOnly('_router.currentURL'),
location: readOnly('_router.location'),
rootURL: readOnly('_router.rootURL'),
_router: null,

/**
Transition the application into another route. The route may
Expand All @@ -40,8 +54,25 @@ const RouterService = Service.extend({
attempted transition
@public
*/
transitionTo(/* routeNameOrUrl, ...models, options */) {
return this.router.transitionTo(...arguments);
transitionTo(...args) {
let queryParams;
let arg = args[0];
if (resemblesURL(arg)) {
return this._router._doURLTransition('transitionTo', arg);
}

let possibleQueryParams = args[args.length - 1];
if (possibleQueryParams && possibleQueryParams.hasOwnProperty('queryParams')) {
queryParams = args.pop().queryParams;
} else {
queryParams = {};
}

let targetRouteName = args.shift();
let transition = this._router._doTransition(targetRouteName, args, queryParams, true);
transition._keepDefaultQueryParamValues = true;

return transition;
},

/**
Expand All @@ -62,13 +93,14 @@ const RouterService = Service.extend({
@public
*/
replaceWith(/* routeNameOrUrl, ...models, options */) {
return this.router.replaceWith(...arguments);
return this.transitionTo(...arguments).method('replace');
},

/**
Generate a URL based on the supplied route name.
@method urlFor
@category ember-routing-router-service
@param {String} routeName the name of the route
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
Expand All @@ -78,8 +110,53 @@ const RouterService = Service.extend({
@public
*/
urlFor(/* routeName, ...models, options */) {
return this.router.generate(...arguments);
return this._router.generate(...arguments);
},

/**
Determines whether a route is active.
@method isActive
@category ember-routing-router-service
@param {String} routeName the name of the route
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {boolean} true if the provided routeName/models/queryParams are active
@public
*/
isActive(/* routeName, ...models, options */) {
let { routeName, models, queryParams } = this._extractArguments(...arguments);
let routerMicrolib = this._router._routerMicrolib;
let state = routerMicrolib.state;

if (!routerMicrolib.isActiveIntent(routeName, models, null)) { return false; }
let hasQueryParams = Object.keys(queryParams).length > 0;

if (hasQueryParams) {
this._router._prepareQueryParams(routeName, models, queryParams, true /* fromRouterService */);
return shallowEqual(queryParams, state.queryParams);
}

return true;
},

_extractArguments(routeName, ...models) {
let possibleQueryParams = models[models.length - 1];
let queryParams = {};

if (possibleQueryParams && possibleQueryParams.hasOwnProperty('queryParams')) {
let options = models.pop();
queryParams = options.queryParams;
}

return { routeName, models, queryParams };
}
});

function resemblesURL(str) {
return typeof str === 'string' && (str === '' || str[0] === '/');
}

export default RouterService;
2 changes: 1 addition & 1 deletion packages/ember-routing/lib/system/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ let Route = EmberObject.extend(ActionHandler, Evented, {
qp.serializedValue = svalue;

let thisQueryParamHasDefaultValue = (qp.serializedDefaultValue === svalue);
if (!thisQueryParamHasDefaultValue) {
if (!thisQueryParamHasDefaultValue || transition._keepDefaultQueryParamValues) {
finalParams.push({
value: svalue,
visible: true,
Expand Down
35 changes: 27 additions & 8 deletions packages/ember-routing/lib/system/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ const EmberRouter = EmberObject.extend(Evented, {
}
},

_doTransition(_targetRouteName, models, _queryParams) {
_doTransition(_targetRouteName, models, _queryParams, _keepDefaultQueryParamValues) {
let targetRouteName = _targetRouteName || getActiveTargetName(this._routerMicrolib);
assert(`The route ${targetRouteName} was not found`, targetRouteName && this._routerMicrolib.hasRoute(targetRouteName));

Expand All @@ -776,7 +776,7 @@ const EmberRouter = EmberObject.extend(Evented, {
this._processActiveTransitionQueryParams(targetRouteName, models, queryParams, _queryParams);

assign(queryParams, _queryParams);
this._prepareQueryParams(targetRouteName, models, queryParams);
this._prepareQueryParams(targetRouteName, models, queryParams, _keepDefaultQueryParamValues);

let transitionArgs = routeArgs(targetRouteName, models, queryParams);
let transition = this._routerMicrolib.transitionTo(...transitionArgs);
Expand Down Expand Up @@ -817,13 +817,17 @@ const EmberRouter = EmberObject.extend(Evented, {
@param {String} targetRouteName
@param {Array<Object>} models
@param {Object} queryParams
@param {boolean} keepDefaultQueryParamValues
@return {Void}
*/
_prepareQueryParams(targetRouteName, models, queryParams) {
_prepareQueryParams(targetRouteName, models, queryParams, _fromRouterService) {
let state = calculatePostTransitionState(this, targetRouteName, models);
this._hydrateUnsuppliedQueryParams(state, queryParams);
this._hydrateUnsuppliedQueryParams(state, queryParams, _fromRouterService);
this._serializeQueryParams(state.handlerInfos, queryParams);
this._pruneDefaultQueryParamValues(state.handlerInfos, queryParams);

if (!_fromRouterService) {
this._pruneDefaultQueryParamValues(state.handlerInfos, queryParams);
}
},

/**
Expand Down Expand Up @@ -945,7 +949,7 @@ const EmberRouter = EmberObject.extend(Evented, {
@param {Object} queryParams
@return {Void}
*/
_hydrateUnsuppliedQueryParams(state, queryParams) {
_hydrateUnsuppliedQueryParams(state, queryParams, _fromRouterService) {
let handlerInfos = state.handlerInfos;
let appCache = this._bucketCache;

Expand All @@ -955,12 +959,27 @@ const EmberRouter = EmberObject.extend(Evented, {
if (!qpMeta) { continue; }

for (let j = 0, qpLen = qpMeta.qps.length; j < qpLen; ++j) {
let qp = qpMeta.qps[j];
var qp = qpMeta.qps[j];

let presentProp = qp.prop in queryParams && qp.prop ||
var presentProp = qp.prop in queryParams && qp.prop ||
qp.scopedPropertyName in queryParams && qp.scopedPropertyName ||
qp.urlKey in queryParams && qp.urlKey;

assert(
`You passed the \`${presentProp}\` query parameter during a transition into ${qp.route.routeName}, please update to ${qp.urlKey}`,
(function() {
if (qp.urlKey === presentProp) {
return true;
}

if (_fromRouterService) {
return false;
}

return true;
})()
);

if (presentProp) {
if (presentProp !== qp.scopedPropertyName) {
queryParams[qp.scopedPropertyName] = queryParams[presentProp];
Expand Down
109 changes: 109 additions & 0 deletions packages/ember/tests/routing/router_service_test/isActive_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
Controller,
inject,
String
} from 'ember-runtime';
import { Component } from 'ember-glimmer';
import { Route, NoneLocation } from 'ember-routing';
import {
get,
set
} from 'ember-metal';
import {
RouterTestCase,
moduleFor
} from 'internal-test-helpers';

import { EMBER_ROUTING_ROUTER_SERVICE } from 'ember/features';

if (EMBER_ROUTING_ROUTER_SERVICE) {
moduleFor('Router Service - isActive', class extends RouterTestCase {
['@test RouterService#isActive returns true for simple route'](assert) {
assert.expect(1);

return this.visit('/')
.then(() => {
return this.routerService.transitionTo('parent.child');
})
.then(() => {
return this.routerService.transitionTo('parent.sister');
})
.then(() => {
assert.ok(this.routerService.isActive('parent.sister'));
});
}

['@test RouterService#isActive returns true for simple route with dynamic segments'](assert) {
assert.expect(1);

let dynamicModel = { id: 1 };

return this.visit('/')
.then(() => {
return this.routerService.transitionTo('dynamic', dynamicModel);
})
.then(() => {
assert.ok(this.routerService.isActive('dynamic', dynamicModel));
});
}

['@test RouterService#isActive does not eagerly instantiate controller for query params'](assert) {
assert.expect(1);

let queryParams = this.buildQueryParams({ sort: 'ASC' });

this.add('controller:parent.sister', Controller.extend({
queryParams: ['sort'],
sort: 'ASC',

init() {
assert.ok(false, 'should never create');
this._super(...arguments);
}
}));

return this.visit('/')
.then(() => {
return this.routerService.transitionTo('parent.brother');
})
.then(() => {
assert.notOk(this.routerService.isActive('parent.sister', queryParams));
});
}

['@test RouterService#isActive is correct for simple route with basic query params'](assert) {
assert.expect(2);

let queryParams = this.buildQueryParams({ sort: 'ASC' });

this.add('controller:parent.child', Controller.extend({
queryParams: ['sort'],
sort: 'ASC'
})
);

return this.visit('/')
.then(() => {
return this.routerService.transitionTo('parent.child', queryParams);
})
.then(() => {
assert.ok(this.routerService.isActive('parent.child', queryParams));
assert.notOk(this.routerService.isActive('parent.child', this.buildQueryParams({ sort: 'DESC' })));
});
}

['@test RouterService#isActive for simple route with array as query params'](assert) {
assert.expect(1);

let queryParams = this.buildQueryParams({ sort: ['ascending'] });

return this.visit('/')
.then(() => {
return this.routerService.transitionTo('parent.child', queryParams);
})
.then(() => {
assert.notOk(this.routerService.isActive('parent.child', this.buildQueryParams({ sort: 'descending' })));
});
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
moduleFor
} from 'internal-test-helpers';
import { Transition } from 'router';
import { Controller } from 'ember-runtime';

import { EMBER_ROUTING_ROUTER_SERVICE } from 'ember/features';

Expand Down Expand Up @@ -105,5 +106,30 @@ if (EMBER_ROUTING_ROUTER_SERVICE) {
assert.deepEqual(this.state, ['/', '/child', '/sister', '/sister']);
});
}

['@test RouterService#replaceWith with basic query params does not remove query param defaults'](assert) {
assert.expect(1);

this.add('controller:parent.child', Controller.extend({
queryParams: ['sort'],
sort: 'ASC'
}));

let queryParams = this.buildQueryParams({ sort: 'ASC' });

return this.visit('/')
.then(() => {
return this.routerService.transitionTo('parent.brother');
})
.then(() => {
return this.routerService.replaceWith('parent.sister');
})
.then(() => {
return this.routerService.replaceWith('parent.child', queryParams);
})
.then(() => {
assert.deepEqual(this.state, ['/', '/child?sort=ASC']);
});
}
});
}
Loading

0 comments on commit 2d9e3aa

Please sign in to comment.