mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(devtools): add debug router APIs (#64411)
This is a patch backport of #63081 PR Close #64411
This commit is contained in:
parent
5f4f624477
commit
43cb583f70
4 changed files with 74 additions and 10 deletions
|
|
@ -59,8 +59,10 @@ export const GLOBAL_PUBLISH_EXPANDO_KEY = 'ng';
|
|||
// Typing for externally published global util functions
|
||||
// Ideally we should be able to use `NgGlobalPublishUtils` using declaration merging but that doesn't work with API extractor yet.
|
||||
// Have included the typings to have type safety when working with editors that support it (VSCode).
|
||||
interface NgGlobalPublishUtils {
|
||||
export interface ExternalGlobalUtils {
|
||||
ɵgetLoadedRoutes(route: any): any;
|
||||
ɵnavigateByUrl(router: any, url: string): any;
|
||||
ɵgetRouterInstance(injector: any): any;
|
||||
}
|
||||
|
||||
const globalUtilsFunctions = {
|
||||
|
|
@ -93,7 +95,6 @@ const globalUtilsFunctions = {
|
|||
'enableProfiling': enableProfiling,
|
||||
};
|
||||
type CoreGlobalUtilsFunctions = keyof typeof globalUtilsFunctions;
|
||||
type ExternalGlobalUtilsFunctions = keyof NgGlobalPublishUtils;
|
||||
|
||||
let _published = false;
|
||||
/**
|
||||
|
|
@ -148,15 +149,15 @@ export type FrameworkAgnosticGlobalUtils = Omit<
|
|||
'getDirectiveMetadata'
|
||||
> & {
|
||||
getDirectiveMetadata(directiveOrComponentInstance: any): DirectiveDebugMetadata | null;
|
||||
};
|
||||
} & ExternalGlobalUtils;
|
||||
|
||||
/**
|
||||
* Publishes the given function to `window.ng` from package other than @angular/core
|
||||
* So that it can be used from the browser console when an application is not in production.
|
||||
*/
|
||||
export function publishExternalGlobalUtil<K extends ExternalGlobalUtilsFunctions>(
|
||||
export function publishExternalGlobalUtil<K extends keyof ExternalGlobalUtils>(
|
||||
name: K,
|
||||
fn: NgGlobalPublishUtils[K],
|
||||
fn: ExternalGlobalUtils[K],
|
||||
): void {
|
||||
publishUtil(name, fn);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,12 +29,13 @@ import {
|
|||
Type,
|
||||
ɵperformanceMarkFeature as performanceMarkFeature,
|
||||
ɵIS_ENABLED_BLOCKING_INITIAL_NAVIGATION as IS_ENABLED_BLOCKING_INITIAL_NAVIGATION,
|
||||
ɵpublishExternalGlobalUtil,
|
||||
} from '@angular/core';
|
||||
import {of, Subject} from 'rxjs';
|
||||
|
||||
import {INPUT_BINDER, RoutedComponentInputBinder} from './directives/router_outlet';
|
||||
import {Event, NavigationError, stringifyEvent} from './events';
|
||||
import {RedirectCommand, Routes} from './models';
|
||||
import {RedirectCommand, Route, Routes} from './models';
|
||||
import {NAVIGATION_ERROR_HANDLER, NavigationTransitions} from './navigation_transition';
|
||||
import {Router} from './router';
|
||||
import {InMemoryScrollingOptions, ROUTER_CONFIGURATION, RouterConfigOptions} from './router_config';
|
||||
|
|
@ -50,6 +51,7 @@ import {
|
|||
VIEW_TRANSITION_OPTIONS,
|
||||
ViewTransitionsFeatureOptions,
|
||||
} from './utils/view_transition';
|
||||
import {getLoadedRoutes, getRouterInstance, navigateByUrl} from './router_devtools';
|
||||
|
||||
/**
|
||||
* Sets up providers necessary to enable `Router` functionality for the application.
|
||||
|
|
@ -88,6 +90,13 @@ import {
|
|||
* @returns A set of providers to setup a Router.
|
||||
*/
|
||||
export function provideRouter(routes: Routes, ...features: RouterFeatures[]): EnvironmentProviders {
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
// Publish this util when the router is provided so that the devtools can use it.
|
||||
ɵpublishExternalGlobalUtil('ɵgetLoadedRoutes', getLoadedRoutes);
|
||||
ɵpublishExternalGlobalUtil('ɵgetRouterInstance', getRouterInstance);
|
||||
ɵpublishExternalGlobalUtil('ɵnavigateByUrl', navigateByUrl);
|
||||
}
|
||||
|
||||
return makeEnvironmentProviders([
|
||||
{provide: ROUTES, multi: true, useValue: routes},
|
||||
typeof ngDevMode === 'undefined' || ngDevMode
|
||||
|
|
|
|||
|
|
@ -6,11 +6,31 @@
|
|||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {ɵpublishExternalGlobalUtil} from '@angular/core';
|
||||
import {Injector} from '@angular/core';
|
||||
import {Router} from './router';
|
||||
import {Route} from './models';
|
||||
|
||||
/**
|
||||
* Returns the loaded routes for a given route.
|
||||
*/
|
||||
export function getLoadedRoutes(route: Route): Route[] | undefined {
|
||||
return route._loadedRoutes;
|
||||
}
|
||||
|
||||
ɵpublishExternalGlobalUtil('ɵgetLoadedRoutes', getLoadedRoutes);
|
||||
/**
|
||||
* Returns the Router instance from the given injector, or null if not available.
|
||||
*/
|
||||
export function getRouterInstance(injector: Injector): Router | null {
|
||||
return injector.get(Router, null, {optional: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates the given router to the specified URL.
|
||||
* Throws if the provided router is not an Angular Router.
|
||||
*/
|
||||
export function navigateByUrl(router: Router, url: string): Promise<boolean> {
|
||||
if (!(router instanceof Router)) {
|
||||
throw new Error('The provided router is not an Angular Router.');
|
||||
}
|
||||
return router.navigateByUrl(url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {Component, Injector} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {Router, RouterModule} from '../index';
|
||||
import {getLoadedRoutes} from '../src/router_devtools';
|
||||
import {getLoadedRoutes, getRouterInstance, navigateByUrl} from '../src/router_devtools';
|
||||
|
||||
@Component({template: '<div>simple standalone</div>'})
|
||||
export class SimpleStandaloneComponent {}
|
||||
|
|
@ -71,4 +71,38 @@ describe('router_devtools', () => {
|
|||
const loadedPath = loadedRoutes && loadedRoutes[0].path;
|
||||
expect(loadedPath).toEqual(undefined);
|
||||
});
|
||||
|
||||
describe('getRouterInstance', () => {
|
||||
it('should return the Router instance from the injector', () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterModule.forRoot([])],
|
||||
});
|
||||
const injector = TestBed.inject(Injector);
|
||||
const router = TestBed.inject(Router);
|
||||
expect(getRouterInstance(injector)).toBe(router);
|
||||
});
|
||||
|
||||
it('should return null if Router is not provided', () => {
|
||||
const injector = Injector.create({providers: []});
|
||||
expect(getRouterInstance(injector)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigateByUrl', () => {
|
||||
it('should navigate to the given url', async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterModule.forRoot([{path: 'foo', component: SimpleStandaloneComponent}])],
|
||||
});
|
||||
const router = TestBed.inject(Router);
|
||||
const result = await navigateByUrl(router, '/foo');
|
||||
expect(result).toBeTrue();
|
||||
expect(router.url).toBe('/foo');
|
||||
});
|
||||
|
||||
it('should throw if not given a Router instance', async () => {
|
||||
expect(() => navigateByUrl({} as unknown as Router, '/foo')).toThrowError(
|
||||
'The provided router is not an Angular Router.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue