/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {BehaviorSubject} from 'rxjs'; import {DetachedRouteHandleInternal, RouteReuseStrategy} from './route_reuse_strategy'; import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot} from './router_state'; import {TreeNode} from './utils/tree'; export function createRouterState( routeReuseStrategy: RouteReuseStrategy, curr: RouterStateSnapshot, prevState: RouterState): RouterState { const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined); return new RouterState(root, curr); } function createNode( routeReuseStrategy: RouteReuseStrategy, curr: TreeNode, prevState?: TreeNode): TreeNode { // reuse an activated route that is currently displayed on the screen if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) { const value = prevState.value; value._futureSnapshot = curr.value; const children = createOrReuseChildren(routeReuseStrategy, curr, prevState); return new TreeNode(value, children); } else { if (routeReuseStrategy.shouldAttach(curr.value)) { // retrieve an activated route that is used to be displayed, but is not currently displayed const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value); if (detachedRouteHandle !== null) { const tree = (detachedRouteHandle as DetachedRouteHandleInternal).route; setFutureSnapshotsOfActivatedRoutes(curr, tree); return tree; } } const value = createActivatedRoute(curr.value); const children = curr.children.map(c => createNode(routeReuseStrategy, c)); return new TreeNode(value, children); } } function setFutureSnapshotsOfActivatedRoutes( curr: TreeNode, result: TreeNode): void { if (curr.value.routeConfig !== result.value.routeConfig) { throw new Error('Cannot reattach ActivatedRouteSnapshot created from a different route'); } if (curr.children.length !== result.children.length) { throw new Error('Cannot reattach ActivatedRouteSnapshot with a different number of children'); } result.value._futureSnapshot = curr.value; for (let i = 0; i < curr.children.length; ++i) { setFutureSnapshotsOfActivatedRoutes(curr.children[i], result.children[i]); } } function createOrReuseChildren( routeReuseStrategy: RouteReuseStrategy, curr: TreeNode, prevState: TreeNode) { return curr.children.map(child => { for (const p of prevState.children) { if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) { return createNode(routeReuseStrategy, child, p); } } return createNode(routeReuseStrategy, child); }); } function createActivatedRoute(c: ActivatedRouteSnapshot) { return new ActivatedRoute( new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams), new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c); }