Skip to content

Commit

Permalink
feat(router): Make router directives standalone (#46758)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
atscott authored and thePunderWoman committed Jul 14, 2022
1 parent 4a81fe7 commit 26ea976
Show file tree
Hide file tree
Showing 13 changed files with 49 additions and 21 deletions.
18 changes: 13 additions & 5 deletions goldens/public-api/router/index.md
Expand Up @@ -179,6 +179,10 @@ export class ChildrenOutletContexts {
onOutletDeactivated(): Map<string, OutletContext>;
// (undocumented)
onOutletReAttached(contexts: Map<string, OutletContext>): void;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<ChildrenOutletContexts, never>;
// (undocumented)
static ɵprov: i0.ɵɵInjectableDeclaration<ChildrenOutletContexts>;
}

// @public
Expand Down Expand Up @@ -666,7 +670,7 @@ export class RouterLink implements OnChanges {
// (undocumented)
get urlTree(): UrlTree | null;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterLink, ":not(a):not(area)[routerLink]", never, { "queryParams": "queryParams"; "fragment": "fragment"; "queryParamsHandling": "queryParamsHandling"; "preserveFragment": "preserveFragment"; "skipLocationChange": "skipLocationChange"; "replaceUrl": "replaceUrl"; "state": "state"; "relativeTo": "relativeTo"; "routerLink": "routerLink"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterLink, ":not(a):not(area)[routerLink]", never, { "queryParams": "queryParams"; "fragment": "fragment"; "queryParamsHandling": "queryParamsHandling"; "preserveFragment": "preserveFragment"; "skipLocationChange": "skipLocationChange"; "replaceUrl": "replaceUrl"; "state": "state"; "relativeTo": "relativeTo"; "routerLink": "routerLink"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<RouterLink, [null, null, { attribute: "tabindex"; }, null, null]>;
}
Expand Down Expand Up @@ -694,7 +698,7 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit
exact: boolean;
} | IsActiveMatchOptions;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterLinkActive, "[routerLinkActive]", ["routerLinkActive"], { "routerLinkActiveOptions": "routerLinkActiveOptions"; "ariaCurrentWhenActive": "ariaCurrentWhenActive"; "routerLinkActive": "routerLinkActive"; }, { "isActiveChange": "isActiveChange"; }, ["links", "linksWithHrefs"], never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterLinkActive, "[routerLinkActive]", ["routerLinkActive"], { "routerLinkActiveOptions": "routerLinkActiveOptions"; "ariaCurrentWhenActive": "ariaCurrentWhenActive"; "routerLinkActive": "routerLinkActive"; }, { "isActiveChange": "isActiveChange"; }, ["links", "linksWithHrefs"], never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<RouterLinkActive, [null, null, null, null, { optional: true; }, { optional: true; }]>;
}
Expand Down Expand Up @@ -726,7 +730,7 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
// (undocumented)
get urlTree(): UrlTree | null;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterLinkWithHref, "a[routerLink],area[routerLink]", never, { "target": "target"; "queryParams": "queryParams"; "fragment": "fragment"; "queryParamsHandling": "queryParamsHandling"; "preserveFragment": "preserveFragment"; "skipLocationChange": "skipLocationChange"; "replaceUrl": "replaceUrl"; "state": "state"; "relativeTo": "relativeTo"; "routerLink": "routerLink"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterLinkWithHref, "a[routerLink],area[routerLink]", never, { "target": "target"; "queryParams": "queryParams"; "fragment": "fragment"; "queryParamsHandling": "queryParamsHandling"; "preserveFragment": "preserveFragment"; "skipLocationChange": "skipLocationChange"; "replaceUrl": "replaceUrl"; "state": "state"; "relativeTo": "relativeTo"; "routerLink": "routerLink"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<RouterLinkWithHref, never>;
}
Expand All @@ -741,7 +745,7 @@ export class RouterModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration<RouterModule>;
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<RouterModule, [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i2.RouterLinkWithHref, typeof i3.RouterLinkActive, typeof i4EmptyOutletComponent], never, [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i2.RouterLinkWithHref, typeof i3.RouterLinkActive, typeof i4EmptyOutletComponent]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<RouterModule, never, [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i2.RouterLinkWithHref, typeof i3.RouterLinkActive, typeof i4EmptyOutletComponent], [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i2.RouterLinkWithHref, typeof i3.RouterLinkActive, typeof i4EmptyOutletComponent]>;
}

// @public
Expand Down Expand Up @@ -772,7 +776,7 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {
// (undocumented)
ngOnInit(): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterOutlet, "router-outlet", ["outlet"], {}, { "activateEvents": "activate"; "deactivateEvents": "deactivate"; "attachEvents": "attach"; "detachEvents": "detach"; }, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterOutlet, "router-outlet", ["outlet"], {}, { "activateEvents": "activate"; "deactivateEvents": "deactivate"; "attachEvents": "attach"; "detachEvents": "detach"; }, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<RouterOutlet, [null, null, { attribute: "name"; }, null, null]>;
}
Expand Down Expand Up @@ -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<UrlSerializer, never>;
// (undocumented)
static ɵprov: i0.ɵɵInjectableDeclaration<UrlSerializer>;
}

// @public
Expand Down
2 changes: 1 addition & 1 deletion goldens/size-tracking/aio-payloads.json
Expand Up @@ -12,7 +12,7 @@
"aio-local": {
"uncompressed": {
"runtime": 4325,
"main": 461893,
"main": 462546,
"polyfills": 33922,
"styles": 73640,
"light-theme": 78276,
Expand Down
2 changes: 1 addition & 1 deletion goldens/size-tracking/integration-payloads.json
Expand Up @@ -33,7 +33,7 @@
"cli-hello-world-lazy": {
"uncompressed": {
"runtime": 2835,
"main": 237313,
"main": 238214,
"polyfills": 33842,
"src_app_lazy_lazy_module_ts": 780
}
Expand Down
6 changes: 6 additions & 0 deletions packages/core/test/bundling/router/bundle.golden_symbols.json
Expand Up @@ -620,6 +620,9 @@
{
"name": "SkipSelf"
},
{
"name": "StandaloneService"
},
{
"name": "Subject"
},
Expand Down Expand Up @@ -1970,6 +1973,9 @@
{
"name": "ɵɵNgOnChangesFeature"
},
{
"name": "ɵɵStandaloneFeature"
},
{
"name": "ɵɵattribute"
},
Expand Down
8 changes: 7 additions & 1 deletion packages/router/src/components/empty_outlet.ts
Expand Up @@ -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:
Expand All @@ -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: `<router-outlet></router-outlet>`})
@Component({
template: `<router-outlet></router-outlet>`,
imports: [RouterOutlet],
standalone: true,
})
export class ɵEmptyOutletComponent {
}

Expand Down
7 changes: 5 additions & 2 deletions packages/router/src/directives/router_link.ts
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions packages/router/src/directives/router_link_active.ts
Expand Up @@ -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<RouterLink>;
Expand Down
6 changes: 5 additions & 1 deletion packages/router/src/directives/router_outlet.ts
Expand Up @@ -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<any>|null = null;
private _activatedRoute: ActivatedRoute|null = null;
Expand Down
2 changes: 1 addition & 1 deletion packages/router/src/router_config_loader.ts
Expand Up @@ -31,7 +31,7 @@ export const ROUTES = new InjectionToken<Route[][]>('ROUTES');

type ComponentLoader = Observable<Type<unknown>>;

@Injectable()
@Injectable({providedIn: 'root'})
export class RouterConfigLoader {
private componentLoaders = new WeakMap<Route, ComponentLoader>();
private childrenLoaders = new WeakMap<Route, Observable<LoadedRouterConfig>>();
Expand Down
10 changes: 5 additions & 5 deletions packages/router/src/router_module.ts
Expand Up @@ -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);
}
Expand All @@ -100,7 +104,7 @@ export function routerNgProbeToken() {
* @publicApi
*/
@NgModule({
declarations: ROUTER_DIRECTIVES,
imports: ROUTER_DIRECTIVES,
exports: ROUTER_DIRECTIVES,
})
export class RouterModule {
Expand Down Expand Up @@ -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<unknown>) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/router/src/router_outlet_context.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -35,6 +35,7 @@ export class OutletContext {
*
* @publicApi
*/
@Injectable({providedIn: 'root'})
export class ChildrenOutletContexts {
// contexts for child outlets, by name.
private contexts = new Map<string, OutletContext>();
Expand Down
2 changes: 0 additions & 2 deletions packages/router/src/router_state.ts
Expand Up @@ -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.
*
Expand Down
3 changes: 2 additions & 1 deletion packages/router/src/url_tree.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -356,6 +356,7 @@ export function mapChildrenIntoArray<T>(
*
* @publicApi
*/
@Injectable({providedIn: 'root', useFactory: () => new DefaultUrlSerializer()})
export abstract class UrlSerializer {
/** Parse a url into a `UrlTree` */
abstract parse(url: string): UrlTree;
Expand Down

0 comments on commit 26ea976

Please sign in to comment.