Skip to content

Commit

Permalink
feat(router): Create APIs for using Router without RouterModule
Browse files Browse the repository at this point in the history
This commit creates and exposes the APIs required to use the Angular Router without importing `RouterModule`.

The newly added APIs are tree-shakable and you can add features using special functions rather than using `ExtraOptions` to control the providers via an internal switch in Router code.

```
const appRoutes: Routes = [];
bootstrapApplication(AppComponent,
  {
    providers: [
      provideRouter(appRoutes,
        withDebugTracing(),     // enables debug tracing feature
        withInMemoryScrolling() // enables scrolling feature
    ]
  }
);
```

This "features" pattern allows for router behavior to evolve in a backwards compatible and tree-shakable way in the future. This approach also makes features more discoverable.

The newly added APIs can be used in any application today (doesn't require an application to be bootstrapped using standalone-based APIs).

Note: APIs added in this commit are released in the "Developer Preview" mode, read more about this mode in Angular docs: https://angular.io/guide/releases#developer-preview
  • Loading branch information
atscott authored and AndrewKushnir committed Aug 12, 2022
1 parent 6277dd7 commit 87b1b3e
Show file tree
Hide file tree
Showing 32 changed files with 1,066 additions and 708 deletions.
75 changes: 68 additions & 7 deletions goldens/public-api/router/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ export type Data = {
[key: string | symbol]: any;
};

// @public
export type DebugTracingFeature = RouterFeature<RouterFeatureKind.DebugTracingFeature>;

// @public
export class DefaultTitleStrategy extends TitleStrategy {
constructor(title: Title);
Expand All @@ -234,6 +237,12 @@ export class DefaultUrlSerializer implements UrlSerializer {
// @public
export type DetachedRouteHandle = {};

// @public
export type DisabledInitialNavigationFeature = RouterFeature<RouterFeatureKind.DisabledInitialNavigationFeature>;

// @public
export type EnabledBlockingInitialNavigationFeature = RouterFeature<RouterFeatureKind.EnabledBlockingInitialNavigationFeature>;

// @public
type Event_2 = RouterEvent | NavigationStart | NavigationEnd | NavigationCancel | NavigationError | RoutesRecognized | GuardsCheckStart | GuardsCheckEnd | RouteConfigLoadStart | RouteConfigLoadEnd | ChildActivationStart | ChildActivationEnd | ActivationStart | ActivationEnd | Scroll | ResolveStart | ResolveEnd;
export { Event_2 as Event }
Expand Down Expand Up @@ -275,21 +284,15 @@ export const enum EventType {
}

// @public
export interface ExtraOptions {
anchorScrolling?: 'disabled' | 'enabled';
canceledNavigationResolution?: 'replace' | 'computed';
export interface ExtraOptions extends InMemoryScrollingOptions, RouterConfigOptions {
enableTracing?: boolean;
errorHandler?: ErrorHandler;
initialNavigation?: InitialNavigation;
malformedUriErrorHandler?: (error: URIError, urlSerializer: UrlSerializer, url: string) => UrlTree;
onSameUrlNavigation?: 'reload' | 'ignore';
paramsInheritanceStrategy?: 'emptyOnly' | 'always';
preloadingStrategy?: any;
// @deprecated
relativeLinkResolution?: 'legacy' | 'corrected';
scrollOffset?: [number, number] | (() => [number, number]);
scrollPositionRestoration?: 'disabled' | 'enabled' | 'top';
urlUpdateStrategy?: 'deferred' | 'eager';
useHash?: boolean;
}

Expand Down Expand Up @@ -333,6 +336,18 @@ export class GuardsCheckStart extends RouterEvent {
// @public
export type InitialNavigation = 'disabled' | 'enabledBlocking' | 'enabledNonBlocking';

// @public
export type InitialNavigationFeature = EnabledBlockingInitialNavigationFeature | DisabledInitialNavigationFeature;

// @public
export type InMemoryScrollingFeature = RouterFeature<RouterFeatureKind.InMemoryScrollingFeature>;

// @public
export interface InMemoryScrollingOptions {
anchorScrolling?: 'disabled' | 'enabled';
scrollPositionRestoration?: 'disabled' | 'enabled' | 'top';
}

// @public
export interface IsActiveMatchOptions {
fragment: 'exact' | 'ignored';
Expand Down Expand Up @@ -494,6 +509,9 @@ export class PreloadAllModules implements PreloadingStrategy {
static ɵprov: i0.ɵɵInjectableDeclaration<PreloadAllModules>;
}

// @public
export type PreloadingFeature = RouterFeature<RouterFeatureKind.PreloadingFeature>;

// @public
export abstract class PreloadingStrategy {
// (undocumented)
Expand All @@ -503,6 +521,9 @@ export abstract class PreloadingStrategy {
// @public
export const PRIMARY_OUTLET = "primary";

// @public
export function provideRouter(routes: Routes, ...features: RouterFeatures[]): Provider[];

// @public
export function provideRoutes(routes: Routes): Provider[];

Expand Down Expand Up @@ -651,6 +672,17 @@ export const ROUTER_CONFIGURATION: InjectionToken<ExtraOptions>;
// @public
export const ROUTER_INITIALIZER: InjectionToken<(compRef: ComponentRef<any>) => void>;

// @public
export interface RouterConfigOptions {
canceledNavigationResolution?: 'replace' | 'computed';
onSameUrlNavigation?: 'reload' | 'ignore';
paramsInheritanceStrategy?: 'emptyOnly' | 'always';
urlUpdateStrategy?: 'deferred' | 'eager';
}

// @public
export type RouterConfigurationFeature = RouterFeature<RouterFeatureKind.RouterConfigurationFeature>;

// @public
export abstract class RouteReuseStrategy {
abstract retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null;
Expand All @@ -669,6 +701,17 @@ export class RouterEvent {
url: string;
}

// @public
export interface RouterFeature<FeatureKind extends RouterFeatureKind> {
// (undocumented)
ɵkind: FeatureKind;
// (undocumented)
ɵproviders: Provider[];
}

// @public
export type RouterFeatures = PreloadingFeature | DebugTracingFeature | InitialNavigationFeature | InMemoryScrollingFeature | RouterConfigurationFeature;

// @public
export class RouterLink implements OnChanges {
constructor(router: Router, route: ActivatedRoute, tabIndexAttribute: string | null | undefined, renderer: Renderer2, el: ElementRef);
Expand Down Expand Up @@ -1002,6 +1045,24 @@ export class UrlTree {
// @public (undocumented)
export const VERSION: Version;

// @public
export function withDebugTracing(): DebugTracingFeature;

// @public
export function withDisabledInitialNavigation(): DisabledInitialNavigationFeature;

// @public
export function withEnabledBlockingInitialNavigation(): EnabledBlockingInitialNavigationFeature;

// @public
export function withInMemoryScrolling(options?: InMemoryScrollingOptions): InMemoryScrollingFeature;

// @public
export function withPreloading(preloadingStrategy: Type<PreloadingStrategy>): PreloadingFeature;

// @public
export function withRouterConfig(options: RouterConfigOptions): RouterConfigurationFeature;

// (No @packageDocumentation comment for this package)

```
10 changes: 5 additions & 5 deletions goldens/size-tracking/integration-payloads.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@
"cli-hello-world-ivy-i18n": {
"uncompressed": {
"runtime": 926,
"main": 124910,
"polyfills": 35252
"main": 124860,
"polyfills": 35246
}
},
"cli-hello-world-lazy": {
"uncompressed": {
"runtime": 2835,
"main": 238737,
"main": 226893,
"polyfills": 33842,
"src_app_lazy_lazy_module_ts": 780
"src_app_lazy_lazy_routes_ts": 487
}
},
"forms": {
Expand All @@ -48,7 +48,7 @@
"animations": {
"uncompressed": {
"runtime": 1070,
"main": 157064,
"main": 156816,
"polyfills": 33814
}
},
Expand Down
11 changes: 0 additions & 11 deletions integration/cli-hello-world-lazy/src/app/app-routing.module.ts

This file was deleted.

Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ describe('AppComponent', () => {
beforeEach(waitForAsync(() => {
TestBed
.configureTestingModule({
imports: [RouterTestingModule],
declarations: [AppComponent],
imports: [AppComponent],
})
.compileComponents();
}));
Expand Down
7 changes: 5 additions & 2 deletions integration/cli-hello-world-lazy/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Component } from '@angular/core';
import {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {RouterOutlet} from '@angular/router';

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, CommonModule],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'cli-hello-world-lazy';
Expand Down
18 changes: 0 additions & 18 deletions integration/cli-hello-world-lazy/src/app/app.module.ts

This file was deleted.

7 changes: 7 additions & 0 deletions integration/cli-hello-world-lazy/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {Routes} from '@angular/router';


export const appRoutes: Routes = [{
path: 'lazy',
loadChildren: () => import('./lazy/lazy.routes').then(routes => routes.lazyRoutes),
}];

This file was deleted.

Empty file.

This file was deleted.

14 changes: 4 additions & 10 deletions integration/cli-hello-world-lazy/src/app/lazy/lazy.component.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { Component, OnInit } from '@angular/core';
import {Component} from '@angular/core';

@Component({
standalone: true,
selector: 'app-lazy',
templateUrl: './lazy.component.html',
styleUrls: ['./lazy.component.css']
template: '<p>lazy works!</p>',
})
export class LazyComponent implements OnInit {

constructor() { }

ngOnInit() {
}

export class LazyComponent {
}
15 changes: 0 additions & 15 deletions integration/cli-hello-world-lazy/src/app/lazy/lazy.module.ts

This file was deleted.

5 changes: 5 additions & 0 deletions integration/cli-hello-world-lazy/src/app/lazy/lazy.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {Routes} from '@angular/router';

import {LazyComponent} from './lazy.component';

export const lazyRoutes: Routes = [{path: '', component: LazyComponent}];
18 changes: 12 additions & 6 deletions integration/cli-hello-world-lazy/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import {enableProdMode} from '@angular/core';
import {bootstrapApplication, provideProtractorTestingSupport} from '@angular/platform-browser';
import {provideRouter} from '@angular/router';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import {AppComponent} from './app/app.component';
import {appRoutes} from './app/app.routes';
import {environment} from './environments/environment';

if (environment.production) {
enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
bootstrapApplication(AppComponent, {
providers: [
provideRouter(appRoutes),
provideProtractorTestingSupport(),
]
}).catch(console.error);

0 comments on commit 87b1b3e

Please sign in to comment.