Skip to content

Commit

Permalink
feat(router): Make router directives standalone
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.
  • Loading branch information
atscott committed Jul 14, 2022
1 parent 336598a commit d532054
Show file tree
Hide file tree
Showing 13 changed files with 49 additions and 25 deletions.
18 changes: 13 additions & 5 deletions goldens/public-api/router/index.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
14 changes: 5 additions & 9 deletions packages/router/src/router_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ export const ROUTER_FORROOT_GUARD = new InjectionToken<void>(
const ROUTER_PRELOADER = new InjectionToken<RouterPreloader>(NG_DEV_MODE ? 'router preloader' : '');

export const ROUTER_PROVIDERS: Provider[] = [
Location,
{provide: UrlSerializer, useClass: DefaultUrlSerializer},
{
provide: Router,
useFactory: setupRouter,
Expand All @@ -69,11 +67,13 @@ export const ROUTER_PROVIDERS: Provider[] = [
[UrlHandlingStrategy, new Optional()], [RouteReuseStrategy, new Optional()]
]
},
ChildrenOutletContexts,
{provide: ActivatedRoute, useFactory: rootRoute, deps: [Router]},
RouterConfigLoader,
];

export function rootRoute(router: Router): ActivatedRoute {
return router.routerState.root;
}

export function routerNgProbeToken() {
return new NgProbeToken('Router', Router);
}
Expand All @@ -100,7 +100,7 @@ export function routerNgProbeToken() {
* @publicApi
*/
@NgModule({
declarations: ROUTER_DIRECTIVES,
imports: ROUTER_DIRECTIVES,
exports: ROUTER_DIRECTIVES,
})
export class RouterModule {
Expand Down Expand Up @@ -512,10 +512,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 d532054

Please sign in to comment.