From 26ea97688cba5850588d8da83abaf8b539577241 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 8 Jul 2022 12:24:26 -0700 Subject: [PATCH] feat(router): Make router directives standalone (#46758) This commit makes the router directives standalone and refactors some of the Router injectables to be `providedIn: 'root'` along with factory functions for initialization. PR Close #46758 --- goldens/public-api/router/index.md | 18 +++++++++++++----- goldens/size-tracking/aio-payloads.json | 2 +- .../size-tracking/integration-payloads.json | 2 +- .../bundling/router/bundle.golden_symbols.json | 6 ++++++ packages/router/src/components/empty_outlet.ts | 8 +++++++- packages/router/src/directives/router_link.ts | 7 +++++-- .../src/directives/router_link_active.ts | 1 + .../router/src/directives/router_outlet.ts | 6 +++++- packages/router/src/router_config_loader.ts | 2 +- packages/router/src/router_module.ts | 10 +++++----- packages/router/src/router_outlet_context.ts | 3 ++- packages/router/src/router_state.ts | 2 -- packages/router/src/url_tree.ts | 3 ++- 13 files changed, 49 insertions(+), 21 deletions(-) diff --git a/goldens/public-api/router/index.md b/goldens/public-api/router/index.md index ca068bebfd4..3382c0864e1 100644 --- a/goldens/public-api/router/index.md +++ b/goldens/public-api/router/index.md @@ -179,6 +179,10 @@ export class ChildrenOutletContexts { onOutletDeactivated(): Map; // (undocumented) onOutletReAttached(contexts: Map): void; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; + // (undocumented) + static ɵprov: i0.ɵɵInjectableDeclaration; } // @public @@ -666,7 +670,7 @@ export class RouterLink implements OnChanges { // (undocumented) get urlTree(): UrlTree | null; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } @@ -694,7 +698,7 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit exact: boolean; } | IsActiveMatchOptions; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } @@ -726,7 +730,7 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy { // (undocumented) get urlTree(): UrlTree | null; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } @@ -741,7 +745,7 @@ export class RouterModule { // (undocumented) static ɵinj: i0.ɵɵInjectorDeclaration; // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public @@ -772,7 +776,7 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract { // (undocumented) ngOnInit(): void; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } @@ -942,6 +946,10 @@ export class UrlSegmentGroup { export abstract class UrlSerializer { abstract parse(url: string): UrlTree; abstract serialize(tree: UrlTree): string; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; + // (undocumented) + static ɵprov: i0.ɵɵInjectableDeclaration; } // @public diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index 4a112d94603..200f2dda138 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -12,7 +12,7 @@ "aio-local": { "uncompressed": { "runtime": 4325, - "main": 461893, + "main": 462546, "polyfills": 33922, "styles": 73640, "light-theme": 78276, diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 74020cd991e..e5320b0fa0d 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -33,7 +33,7 @@ "cli-hello-world-lazy": { "uncompressed": { "runtime": 2835, - "main": 237313, + "main": 238214, "polyfills": 33842, "src_app_lazy_lazy_module_ts": 780 } diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 51989fcfa50..e5e5f4b4fff 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -620,6 +620,9 @@ { "name": "SkipSelf" }, + { + "name": "StandaloneService" + }, { "name": "Subject" }, @@ -1970,6 +1973,9 @@ { "name": "ɵɵNgOnChangesFeature" }, + { + "name": "ɵɵStandaloneFeature" + }, { "name": "ɵɵattribute" }, diff --git a/packages/router/src/components/empty_outlet.ts b/packages/router/src/components/empty_outlet.ts index 8f0f511e95d..25f75675633 100644 --- a/packages/router/src/components/empty_outlet.ts +++ b/packages/router/src/components/empty_outlet.ts @@ -8,6 +8,8 @@ import {Component} from '@angular/core'; +import {RouterOutlet} from '../directives/router_outlet'; + /** * This component is used internally within the router to be a placeholder when an empty * router-outlet is needed. For example, with a config such as: @@ -17,7 +19,11 @@ import {Component} from '@angular/core'; * In order to render, there needs to be a component on this config, which will default * to this `EmptyOutletComponent`. */ -@Component({template: ``}) +@Component({ + template: ``, + imports: [RouterOutlet], + standalone: true, +}) export class ɵEmptyOutletComponent { } diff --git a/packages/router/src/directives/router_link.ts b/packages/router/src/directives/router_link.ts index eb126af0b29..c032d01ba65 100644 --- a/packages/router/src/directives/router_link.ts +++ b/packages/router/src/directives/router_link.ts @@ -115,7 +115,10 @@ import {UrlTree} from '../url_tree'; * * @publicApi */ -@Directive({selector: ':not(a):not(area)[routerLink]'}) +@Directive({ + selector: ':not(a):not(area)[routerLink]', + standalone: true, +}) export class RouterLink implements OnChanges { /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the @@ -277,7 +280,7 @@ export class RouterLink implements OnChanges { * * @publicApi */ -@Directive({selector: 'a[routerLink],area[routerLink]'}) +@Directive({selector: 'a[routerLink],area[routerLink]', standalone: true}) export class RouterLinkWithHref implements OnChanges, OnDestroy { // TODO(issue/24571): remove '!'. @HostBinding('attr.target') @Input() target!: string; diff --git a/packages/router/src/directives/router_link_active.ts b/packages/router/src/directives/router_link_active.ts index faddf1b3557..ce39bfdac69 100644 --- a/packages/router/src/directives/router_link_active.ts +++ b/packages/router/src/directives/router_link_active.ts @@ -89,6 +89,7 @@ import {RouterLink, RouterLinkWithHref} from './router_link'; @Directive({ selector: '[routerLinkActive]', exportAs: 'routerLinkActive', + standalone: true, }) export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit { @ContentChildren(RouterLink, {descendants: true}) links!: QueryList; diff --git a/packages/router/src/directives/router_outlet.ts b/packages/router/src/directives/router_outlet.ts index be896b01d56..a582dfeed53 100644 --- a/packages/router/src/directives/router_outlet.ts +++ b/packages/router/src/directives/router_outlet.ts @@ -157,7 +157,11 @@ export interface RouterOutletContract { * * @publicApi */ -@Directive({selector: 'router-outlet', exportAs: 'outlet'}) +@Directive({ + selector: 'router-outlet', + exportAs: 'outlet', + standalone: true, +}) export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract { private activated: ComponentRef|null = null; private _activatedRoute: ActivatedRoute|null = null; diff --git a/packages/router/src/router_config_loader.ts b/packages/router/src/router_config_loader.ts index 0bd4e4c47d0..1b25c3b72dd 100644 --- a/packages/router/src/router_config_loader.ts +++ b/packages/router/src/router_config_loader.ts @@ -31,7 +31,7 @@ export const ROUTES = new InjectionToken('ROUTES'); type ComponentLoader = Observable>; -@Injectable() +@Injectable({providedIn: 'root'}) export class RouterConfigLoader { private componentLoaders = new WeakMap(); private childrenLoaders = new WeakMap>(); diff --git a/packages/router/src/router_module.ts b/packages/router/src/router_module.ts index c02ca03e21f..564bbed18eb 100644 --- a/packages/router/src/router_module.ts +++ b/packages/router/src/router_module.ts @@ -74,6 +74,10 @@ export const ROUTER_PROVIDERS: Provider[] = [ RouterConfigLoader, ]; +export function rootRoute(router: Router): ActivatedRoute { + return router.routerState.root; +} + export function routerNgProbeToken() { return new NgProbeToken('Router', Router); } @@ -100,7 +104,7 @@ export function routerNgProbeToken() { * @publicApi */ @NgModule({ - declarations: ROUTER_DIRECTIVES, + imports: ROUTER_DIRECTIVES, exports: ROUTER_DIRECTIVES, }) export class RouterModule { @@ -512,10 +516,6 @@ export function assignExtraOptionsToRouter(opts: ExtraOptions, router: Router): } } -export function rootRoute(router: Router): ActivatedRoute { - return router.routerState.root; -} - export function getBootstrapListener() { const injector = inject(Injector); return (bootstrappedComponentRef: ComponentRef) => { diff --git a/packages/router/src/router_outlet_context.ts b/packages/router/src/router_outlet_context.ts index 7b0792f26f7..514e0feb7b5 100644 --- a/packages/router/src/router_outlet_context.ts +++ b/packages/router/src/router_outlet_context.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ComponentFactoryResolver, ComponentRef, EnvironmentInjector} from '@angular/core'; +import {ComponentFactoryResolver, ComponentRef, EnvironmentInjector, Injectable} from '@angular/core'; import {RouterOutletContract} from './directives/router_outlet'; import {ActivatedRoute} from './router_state'; @@ -35,6 +35,7 @@ export class OutletContext { * * @publicApi */ +@Injectable({providedIn: 'root'}) export class ChildrenOutletContexts { // contexts for child outlets, by name. private contexts = new Map(); diff --git a/packages/router/src/router_state.ts b/packages/router/src/router_state.ts index 1c7abab083e..8cc8d446690 100644 --- a/packages/router/src/router_state.ts +++ b/packages/router/src/router_state.ts @@ -16,8 +16,6 @@ import {equalSegments, UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree'; import {shallowEqual, shallowEqualArrays} from './utils/collection'; import {Tree, TreeNode} from './utils/tree'; - - /** * Represents the state of the router as a tree of activated routes. * diff --git a/packages/router/src/url_tree.ts b/packages/router/src/url_tree.ts index b4395bafc8d..77e986b7d74 100644 --- a/packages/router/src/url_tree.ts +++ b/packages/router/src/url_tree.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵRuntimeError as RuntimeError} from '@angular/core'; +import {Injectable, ɵRuntimeError as RuntimeError} from '@angular/core'; import {RuntimeErrorCode} from './errors'; import {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared'; @@ -356,6 +356,7 @@ export function mapChildrenIntoArray( * * @publicApi */ +@Injectable({providedIn: 'root', useFactory: () => new DefaultUrlSerializer()}) export abstract class UrlSerializer { /** Parse a url into a `UrlTree` */ abstract parse(url: string): UrlTree;