From bff7766db4a393f60c0c7beb2ece528a00ff1fde Mon Sep 17 00:00:00 2001 From: Sam Van Campenhout Date: Fri, 19 Mar 2021 22:28:42 +0100 Subject: [PATCH] [FEATURE routing-router-service-refresh] Add a refresh method to the router service This adds the refresh method to the RouterService as described in RFC 631. RFC PR: https://github.com/emberjs/rfcs/pull/631 --- .../-internals/routing/lib/services/router.ts | 36 +++++++ packages/@ember/canary-features/index.ts | 4 + .../router_service_test/refresh_test.js | 100 ++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 packages/ember/tests/routing/router_service_test/refresh_test.js diff --git a/packages/@ember/-internals/routing/lib/services/router.ts b/packages/@ember/-internals/routing/lib/services/router.ts index 1982ea5e32a..72f45380f7b 100644 --- a/packages/@ember/-internals/routing/lib/services/router.ts +++ b/packages/@ember/-internals/routing/lib/services/router.ts @@ -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'; @@ -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. diff --git a/packages/@ember/canary-features/index.ts b/packages/@ember/canary-features/index.ts index c6afc535d6f..617810e311c 100644 --- a/packages/@ember/canary-features/index.ts +++ b/packages/@ember/canary-features/index.ts @@ -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, }; /** @@ -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 +); diff --git a/packages/ember/tests/routing/router_service_test/refresh_test.js b/packages/ember/tests/routing/router_service_test/refresh_test.js new file mode 100644 index 00000000000..f0c1b47049a --- /dev/null +++ b/packages/ember/tests/routing/router_service_test/refresh_test.js @@ -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'); + } + } + ); +}