Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE routing-router-service-refresh] Add a refresh method to the router service #19471

Merged
merged 1 commit into from
May 24, 2021
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
36 changes: 36 additions & 0 deletions packages/@ember/-internals/routing/lib/services/router.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { getOwner, Owner } from '@ember/-internals/owner';
import { Evented } from '@ember/-internals/runtime';
import { symbol } from '@ember/-internals/utils';
import { EMBER_ROUTING_ROUTER_SERVICE_REFRESH } from '@ember/canary-features';
import { assert } from '@ember/debug';
import { readOnly } from '@ember/object/computed';
import { assign } from '@ember/polyfills';
import Service from '@ember/service';
import { consumeTag, tagFor } from '@glimmer/validator';
import Route from '../system/route';
import EmberRouter, { QueryParam } from '../system/router';
import { extractRouteArgs, resemblesURL, shallowEqual } from '../utils';

Expand Down Expand Up @@ -474,6 +476,40 @@ export default class RouterService extends Service {
*/
}

if (EMBER_ROUTING_ROUTER_SERVICE_REFRESH) {
RouterService.reopen({
/**
* Refreshes all currently active routes, doing a full transition.
* If a route name is provided and refers to a currently active route,
* it will refresh only that route and its descendents.
* Returns a promise that will be resolved once the refresh is complete.
* All resetController, beforeModel, model, afterModel, redirect, and setupController
* hooks will be called again. You will get new data from the model hook.
*
* @method refresh
* @param {String} [routeName] the route to refresh (along with all child routes)
* @return Transition
* @category EMBER_ROUTING_ROUTER_SERVICE_REFRESH
* @public
*/
refresh(pivotRouteName?: string) {
if (!pivotRouteName) {
return this._router._routerMicrolib.refresh();
}

assert(`The route "${pivotRouteName}" was not found`, this._router.hasRoute(pivotRouteName));
assert(
`The route "${pivotRouteName}" is currently not active`,
this.isActive(pivotRouteName)
);

let pivotRoute = getOwner(this).lookup(`route:${pivotRouteName}`) as Route;

return this._router._routerMicrolib.refresh(pivotRoute);
},
});
}

RouterService.reopen(Evented, {
/**
Name of the current route.
Expand Down
4 changes: 4 additions & 0 deletions packages/@ember/canary-features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const DEFAULT_FEATURES = {
EMBER_MODERNIZED_BUILT_IN_COMPONENTS: true,
EMBER_STRICT_MODE: true,
EMBER_DYNAMIC_HELPERS_AND_MODIFIERS: true,
EMBER_ROUTING_ROUTER_SERVICE_REFRESH: null,
};

/**
Expand Down Expand Up @@ -81,3 +82,6 @@ export const EMBER_STRICT_MODE = featureValue(FEATURES.EMBER_STRICT_MODE);
export const EMBER_DYNAMIC_HELPERS_AND_MODIFIERS = featureValue(
FEATURES.EMBER_DYNAMIC_HELPERS_AND_MODIFIERS
);
export const EMBER_ROUTING_ROUTER_SERVICE_REFRESH = featureValue(
FEATURES.EMBER_ROUTING_ROUTER_SERVICE_REFRESH
);
100 changes: 100 additions & 0 deletions packages/ember/tests/routing/router_service_test/refresh_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Route } from '@ember/-internals/routing';
import { EMBER_ROUTING_ROUTER_SERVICE_REFRESH } from '@ember/canary-features';
import { RouterTestCase, moduleFor } from 'internal-test-helpers';

if (EMBER_ROUTING_ROUTER_SERVICE_REFRESH) {
moduleFor(
'Router Service - refresh',
class extends RouterTestCase {
async ['@test RouterService#refresh can be used to re-run the model hooks of active routes'](
assert
) {
let parentCounter = 0;
this.add(
'route:parent',
class extends Route {
model() {
++parentCounter;
}
}
);

let childCounter = 0;
this.add(
'route:parent.child',
class extends Route {
model() {
++childCounter;
}
}
);

let sisterCounter = 0;
this.add(
'route:parent.sister',
class extends Route {
model() {
++sisterCounter;
}
}
);

await this.visit('/');
assert.equal(parentCounter, 1);
assert.equal(childCounter, 0);
assert.equal(sisterCounter, 0);

await this.routerService.refresh();
assert.equal(parentCounter, 2);
assert.equal(childCounter, 0);
assert.equal(sisterCounter, 0);

await this.routerService.refresh('application');
assert.equal(parentCounter, 3);
assert.equal(childCounter, 0);
assert.equal(sisterCounter, 0);

await this.routerService.transitionTo('parent.child');
assert.equal(parentCounter, 3);
assert.equal(childCounter, 1);
assert.equal(sisterCounter, 0);

await this.routerService.refresh('parent.child');
assert.equal(parentCounter, 3);
assert.equal(childCounter, 2);
assert.equal(sisterCounter, 0);

await this.routerService.refresh('parent');
assert.equal(parentCounter, 4);
assert.equal(childCounter, 3);
assert.equal(sisterCounter, 0);

await this.routerService.transitionTo('parent.sister');
assert.equal(parentCounter, 4);
assert.equal(childCounter, 3);
assert.equal(sisterCounter, 1);

await this.routerService.refresh();
assert.equal(parentCounter, 5);
assert.equal(childCounter, 3);
assert.equal(sisterCounter, 2);
}

async ['@test RouterService#refresh verifies that the provided route exists']() {
await this.visit('/');

expectAssertion(() => {
this.routerService.refresh('this-route-does-not-exist');
}, 'The route "this-route-does-not-exist" was not found');
}

async ['@test RouterService#refresh verifies that the provided route is active']() {
await this.visit('/');

expectAssertion(() => {
this.routerService.refresh('parent.child');
}, 'The route "parent.child" is currently not active');
}
}
);
}