refactor(router): misc refactoring (#13330)
This commit is contained in:
parent
56c361ff6a
commit
b5c4bf1c59
|
@ -9,8 +9,6 @@
|
|||
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
||||
import {AppView} from './view';
|
||||
|
||||
const _UNDEFINED = new Object();
|
||||
|
||||
export class ElementInjector extends Injector {
|
||||
constructor(private _view: AppView<any>, private _nodeIndex: number) { super(); }
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import {ApplicationRef} from '../application_ref';
|
||||
import {ChangeDetectorRef, ChangeDetectorStatus} from '../change_detection/change_detection';
|
||||
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile';
|
||||
import {DirectRenderer, RenderComponentType, RenderDebugInfo, Renderer} from '../render/api';
|
||||
|
|
|
@ -18,7 +18,7 @@ import {map} from 'rxjs/operator/map';
|
|||
import {mergeMap} from 'rxjs/operator/mergeMap';
|
||||
import {EmptyError} from 'rxjs/util/EmptyError';
|
||||
|
||||
import {Route, Routes, UrlMatchResult} from './config';
|
||||
import {Route, Routes} from './config';
|
||||
import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader';
|
||||
import {NavigationCancelingError, PRIMARY_OUTLET, Params, defaultUrlMatcher} from './shared';
|
||||
import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
|
||||
|
@ -54,7 +54,6 @@ function canLoadFails(route: Route): Observable<LoadedRouterConfig> {
|
|||
`Cannot load children because the guard of the route "path: '${route.path}'" returned false`)));
|
||||
}
|
||||
|
||||
|
||||
export function applyRedirects(
|
||||
injector: Injector, configLoader: RouterConfigLoader, urlSerializer: UrlSerializer,
|
||||
urlTree: UrlTree, config: Routes): Observable<UrlTree> {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {ActivatedRoute} from './router_state';
|
||||
import {PRIMARY_OUTLET, Params} from './shared';
|
||||
import {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
|
||||
import {forEach, shallowEqual} from './utils/collection';
|
||||
import {forEach, shallowEqual, last} from './utils/collection';
|
||||
|
||||
export function createUrlTree(
|
||||
route: ActivatedRoute, urlTree: UrlTree, commands: any[], queryParams: Params,
|
||||
|
@ -18,36 +18,24 @@ export function createUrlTree(
|
|||
return tree(urlTree.root, urlTree.root, urlTree, queryParams, fragment);
|
||||
}
|
||||
|
||||
const normalizedCommands = normalizeCommands(commands);
|
||||
validateCommands(normalizedCommands);
|
||||
const nav = computeNavigation(commands);
|
||||
|
||||
if (navigateToRoot(normalizedCommands)) {
|
||||
if (nav.toRoot()) {
|
||||
return tree(urlTree.root, new UrlSegmentGroup([], {}), urlTree, queryParams, fragment);
|
||||
}
|
||||
|
||||
const startingPosition = findStartingPosition(normalizedCommands, urlTree, route);
|
||||
const startingPosition = findStartingPosition(nav, urlTree, route);
|
||||
|
||||
const segmentGroup = startingPosition.processChildren ?
|
||||
updateSegmentGroupChildren(
|
||||
startingPosition.segmentGroup, startingPosition.index, normalizedCommands.commands) :
|
||||
startingPosition.segmentGroup, startingPosition.index, nav.commands) :
|
||||
updateSegmentGroup(
|
||||
startingPosition.segmentGroup, startingPosition.index, normalizedCommands.commands);
|
||||
startingPosition.segmentGroup, startingPosition.index, nav.commands);
|
||||
return tree(startingPosition.segmentGroup, segmentGroup, urlTree, queryParams, fragment);
|
||||
}
|
||||
|
||||
function validateCommands(n: NormalizedNavigationCommands): void {
|
||||
if (n.isAbsolute && n.commands.length > 0 && isMatrixParams(n.commands[0])) {
|
||||
throw new Error('Root segment cannot have matrix parameters');
|
||||
}
|
||||
|
||||
const c = n.commands.filter(c => typeof c === 'object' && c.outlets !== undefined);
|
||||
if (c.length > 0 && c[0] !== n.commands[n.commands.length - 1]) {
|
||||
throw new Error('{outlets:{}} has to be the last command');
|
||||
}
|
||||
}
|
||||
|
||||
function isMatrixParams(command: any): boolean {
|
||||
return typeof command === 'object' && command.outlets === undefined &&
|
||||
command.segmentPath === undefined;
|
||||
return typeof command === 'object' && !command.outlets && !command.segmentPath;
|
||||
}
|
||||
|
||||
function tree(
|
||||
|
@ -55,11 +43,11 @@ function tree(
|
|||
queryParams: Params, fragment: string): UrlTree {
|
||||
if (urlTree.root === oldSegmentGroup) {
|
||||
return new UrlTree(newSegmentGroup, stringify(queryParams), fragment);
|
||||
} else {
|
||||
}
|
||||
|
||||
return new UrlTree(
|
||||
replaceSegment(urlTree.root, oldSegmentGroup, newSegmentGroup), stringify(queryParams),
|
||||
fragment);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceSegment(
|
||||
|
@ -76,72 +64,71 @@ function replaceSegment(
|
|||
return new UrlSegmentGroup(current.segments, children);
|
||||
}
|
||||
|
||||
function navigateToRoot(normalizedChange: NormalizedNavigationCommands): boolean {
|
||||
return normalizedChange.isAbsolute && normalizedChange.commands.length === 1 &&
|
||||
normalizedChange.commands[0] == '/';
|
||||
class Navigation {
|
||||
constructor(public isAbsolute: boolean, public numberOfDoubleDots: number, public commands: any[]) {
|
||||
if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
|
||||
throw new Error('Root segment cannot have matrix parameters');
|
||||
}
|
||||
|
||||
const cmdWithOutlet = commands.find(c => typeof c === 'object' && c.outlets);
|
||||
if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
|
||||
throw new Error('{outlets:{}} has to be the last command');
|
||||
}
|
||||
}
|
||||
|
||||
public toRoot(): boolean {
|
||||
return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
|
||||
}
|
||||
}
|
||||
|
||||
class NormalizedNavigationCommands {
|
||||
constructor(
|
||||
public isAbsolute: boolean, public numberOfDoubleDots: number, public commands: any[]) {}
|
||||
}
|
||||
|
||||
function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
|
||||
if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] == '/') {
|
||||
return new NormalizedNavigationCommands(true, 0, commands);
|
||||
/** Transforms commands to a normalized `Navigation` */
|
||||
function computeNavigation(commands: any[]): Navigation {
|
||||
if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
|
||||
return new Navigation(true, 0, commands);
|
||||
}
|
||||
|
||||
let numberOfDoubleDots = 0;
|
||||
let isAbsolute = false;
|
||||
const res: any[] = [];
|
||||
|
||||
for (let i = 0; i < commands.length; ++i) {
|
||||
const c = commands[i];
|
||||
const res: any[] = commands.reduce((res, cmd, cmdIdx) => {
|
||||
if (typeof cmd === 'object') {
|
||||
if (cmd.outlets) {
|
||||
const outlets: {[k: string]: any} = {};
|
||||
forEach(cmd.outlets, (commands: any, name: string) => {
|
||||
outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
|
||||
});
|
||||
return [...res, {outlets}];
|
||||
}
|
||||
|
||||
if (typeof c === 'object' && c.outlets !== undefined) {
|
||||
const r: {[k: string]: any} = {};
|
||||
forEach(c.outlets, (commands: any, name: string) => {
|
||||
if (typeof commands === 'string') {
|
||||
r[name] = commands.split('/');
|
||||
} else {
|
||||
r[name] = commands;
|
||||
if (cmd.segmentPath) {
|
||||
return [...res, cmd.segmentPath];
|
||||
}
|
||||
}
|
||||
|
||||
if (!(typeof cmd === 'string')) {
|
||||
return [...res, cmd];
|
||||
}
|
||||
|
||||
if (cmdIdx === 0) {
|
||||
cmd.split('/').forEach((urlPart, partIndex) => {
|
||||
if (partIndex == 0 && urlPart === '.') {
|
||||
// skip './a'
|
||||
} else if (partIndex == 0 && urlPart === '') { // '/a'
|
||||
isAbsolute = true;
|
||||
} else if (urlPart === '..') { // '../a'
|
||||
numberOfDoubleDots++;
|
||||
} else if (urlPart != '') {
|
||||
res.push(urlPart);
|
||||
}
|
||||
});
|
||||
res.push({outlets: r});
|
||||
continue;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if (typeof c === 'object' && c.segmentPath !== undefined) {
|
||||
res.push(c.segmentPath);
|
||||
continue;
|
||||
}
|
||||
return [...res, cmd];
|
||||
}, []);
|
||||
|
||||
if (!(typeof c === 'string')) {
|
||||
res.push(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
const parts = c.split('/');
|
||||
for (let j = 0; j < parts.length; ++j) {
|
||||
const cc = parts[j];
|
||||
|
||||
if (j == 0 && cc == '.') { // './a'
|
||||
// skip it
|
||||
} else if (j == 0 && cc == '') { // '/a'
|
||||
isAbsolute = true;
|
||||
} else if (cc == '..') { // '../a'
|
||||
numberOfDoubleDots++;
|
||||
} else if (cc != '') {
|
||||
res.push(cc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
return new NormalizedNavigationCommands(isAbsolute, numberOfDoubleDots, res);
|
||||
return new Navigation(isAbsolute, numberOfDoubleDots, res);
|
||||
}
|
||||
|
||||
class Position {
|
||||
|
@ -150,19 +137,19 @@ class Position {
|
|||
}
|
||||
}
|
||||
|
||||
function findStartingPosition(
|
||||
normalizedChange: NormalizedNavigationCommands, urlTree: UrlTree,
|
||||
route: ActivatedRoute): Position {
|
||||
if (normalizedChange.isAbsolute) {
|
||||
return new Position(urlTree.root, true, 0);
|
||||
} else if (route.snapshot._lastPathIndex === -1) {
|
||||
return new Position(route.snapshot._urlSegment, true, 0);
|
||||
} else {
|
||||
const modifier = isMatrixParams(normalizedChange.commands[0]) ? 0 : 1;
|
||||
const index = route.snapshot._lastPathIndex + modifier;
|
||||
return createPositionApplyingDoubleDots(
|
||||
route.snapshot._urlSegment, index, normalizedChange.numberOfDoubleDots);
|
||||
function findStartingPosition(nav: Navigation, tree: UrlTree, route: ActivatedRoute): Position {
|
||||
if (nav.isAbsolute) {
|
||||
return new Position(tree.root, true, 0);
|
||||
}
|
||||
|
||||
if (route.snapshot._lastPathIndex === -1) {
|
||||
return new Position(route.snapshot._urlSegment, true, 0);
|
||||
}
|
||||
|
||||
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
|
||||
const index = route.snapshot._lastPathIndex + modifier;
|
||||
return createPositionApplyingDoubleDots(route.snapshot._urlSegment, index, nav.numberOfDoubleDots);
|
||||
|
||||
}
|
||||
|
||||
function createPositionApplyingDoubleDots(
|
||||
|
@ -276,7 +263,7 @@ function createNewSegmentGroup(
|
|||
let i = 0;
|
||||
while (i < commands.length) {
|
||||
if (typeof commands[i] === 'object' && commands[i].outlets !== undefined) {
|
||||
const children = createNewSegmentChldren(commands[i].outlets);
|
||||
const children = createNewSegmentChildren(commands[i].outlets);
|
||||
return new UrlSegmentGroup(paths, children);
|
||||
}
|
||||
|
||||
|
@ -301,7 +288,7 @@ function createNewSegmentGroup(
|
|||
return new UrlSegmentGroup(paths, {});
|
||||
}
|
||||
|
||||
function createNewSegmentChldren(outlets: {[name: string]: any}): any {
|
||||
function createNewSegmentChildren(outlets: {[name: string]: any}): any {
|
||||
const children: {[key: string]: UrlSegmentGroup} = {};
|
||||
forEach(outlets, (commands: any, outlet: string) => {
|
||||
if (commands !== null) {
|
||||
|
|
|
@ -12,9 +12,12 @@ import {ROUTES} from './router_config_loader';
|
|||
import {ROUTER_PROVIDERS} from './router_module';
|
||||
import {flatten} from './utils/collection';
|
||||
|
||||
export var __router_private__:
|
||||
{ROUTER_PROVIDERS: typeof ROUTER_PROVIDERS; ROUTES: typeof ROUTES; flatten: typeof flatten;} = {
|
||||
export const __router_private__: {
|
||||
ROUTER_PROVIDERS: typeof ROUTER_PROVIDERS,
|
||||
ROUTES: typeof ROUTES,
|
||||
flatten: typeof flatten,
|
||||
} = {
|
||||
ROUTER_PROVIDERS: ROUTER_PROVIDERS,
|
||||
ROUTES: ROUTES,
|
||||
flatten: flatten
|
||||
};
|
||||
flatten: flatten,
|
||||
};
|
||||
|
|
|
@ -7,4 +7,4 @@
|
|||
*/
|
||||
import {__platform_browser_private__ as r} from '@angular/platform-browser';
|
||||
|
||||
export var getDOM: typeof r.getDOM = r.getDOM;
|
||||
export const getDOM: typeof r.getDOM = r.getDOM;
|
||||
|
|
|
@ -11,9 +11,9 @@ import {Observable} from 'rxjs/Observable';
|
|||
import {Observer} from 'rxjs/Observer';
|
||||
import {of } from 'rxjs/observable/of';
|
||||
|
||||
import {Data, ResolveData, Route, Routes, UrlMatchResult} from './config';
|
||||
import {Data, ResolveData, Route, Routes} from './config';
|
||||
import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from './router_state';
|
||||
import {PRIMARY_OUTLET, Params, defaultUrlMatcher} from './shared';
|
||||
import {PRIMARY_OUTLET, defaultUrlMatcher} from './shared';
|
||||
import {UrlSegment, UrlSegmentGroup, UrlTree, mapChildrenIntoArray} from './url_tree';
|
||||
import {forEach, last, merge} from './utils/collection';
|
||||
import {TreeNode} from './utils/tree';
|
||||
|
|
|
@ -11,7 +11,6 @@ import {ComponentRef} from '@angular/core';
|
|||
import {ActivatedRoute, ActivatedRouteSnapshot} from './router_state';
|
||||
import {TreeNode} from './utils/tree';
|
||||
|
||||
|
||||
/**
|
||||
* @whatItDoes Represents the detached route tree.
|
||||
*
|
||||
|
@ -22,43 +21,30 @@ import {TreeNode} from './utils/tree';
|
|||
*/
|
||||
export type DetachedRouteHandle = {};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
export type DetachedRouteHandleInternal = {
|
||||
componentRef: ComponentRef<any>,
|
||||
route: TreeNode<ActivatedRoute>
|
||||
route: TreeNode<ActivatedRoute>,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @whatItDoes Provides a way to customize when activated routes get reused.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class RouteReuseStrategy {
|
||||
/**
|
||||
* Determines if this route (and its subtree) should be detached to be reused later.
|
||||
*/
|
||||
/** Determines if this route (and its subtree) should be detached to be reused later */
|
||||
abstract shouldDetach(route: ActivatedRouteSnapshot): boolean;
|
||||
|
||||
/**
|
||||
* Stores the detached route.
|
||||
*/
|
||||
/** Stores the detached route */
|
||||
abstract store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void;
|
||||
|
||||
/**
|
||||
* Determines if this route (and its subtree) should be reattached.
|
||||
*/
|
||||
/** Determines if this route (and its subtree) should be reattached */
|
||||
abstract shouldAttach(route: ActivatedRouteSnapshot): boolean;
|
||||
|
||||
/**
|
||||
* Retrieves the previously stored route.
|
||||
*/
|
||||
/** Retrieves the previously stored route */
|
||||
abstract retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle;
|
||||
|
||||
/**
|
||||
* Determines if a route should be reused.
|
||||
*/
|
||||
/** Determines if a route should be reused */
|
||||
abstract shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean;
|
||||
}
|
|
@ -13,7 +13,6 @@ import {Observable} from 'rxjs/Observable';
|
|||
import {Subject} from 'rxjs/Subject';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {from} from 'rxjs/observable/from';
|
||||
import {fromPromise} from 'rxjs/observable/fromPromise';
|
||||
import {of } from 'rxjs/observable/of';
|
||||
import {concatMap} from 'rxjs/operator/concatMap';
|
||||
import {every} from 'rxjs/operator/every';
|
||||
|
@ -148,7 +147,6 @@ export class NavigationStart {
|
|||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public id: number,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public url: string) {}
|
||||
|
||||
|
@ -166,10 +164,8 @@ export class NavigationEnd {
|
|||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public id: number,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public url: string,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public urlAfterRedirects: string) {}
|
||||
|
||||
|
@ -189,10 +185,8 @@ export class NavigationCancel {
|
|||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public id: number,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public url: string,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public reason: string) {}
|
||||
|
||||
|
@ -210,10 +204,8 @@ export class NavigationError {
|
|||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public id: number,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public url: string,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public error: any) {}
|
||||
|
||||
|
@ -233,7 +225,6 @@ export class RoutesRecognized {
|
|||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public id: number,
|
||||
|
||||
/** @docsNotRequired */
|
||||
public url: string,
|
||||
/** @docsNotRequired */
|
||||
|
@ -282,7 +273,7 @@ type NavigationParams = {
|
|||
resolve: any,
|
||||
reject: any,
|
||||
promise: Promise<boolean>,
|
||||
imperative: boolean
|
||||
imperative: boolean,
|
||||
};
|
||||
|
||||
|
||||
|
@ -312,9 +303,8 @@ export class Router {
|
|||
private currentUrlTree: UrlTree;
|
||||
private rawUrlTree: UrlTree;
|
||||
|
||||
private navigations: BehaviorSubject<NavigationParams> =
|
||||
new BehaviorSubject<NavigationParams>(null);
|
||||
private routerEvents: Subject<Event> = new Subject<Event>();
|
||||
private navigations = new BehaviorSubject<NavigationParams>(null);
|
||||
private routerEvents = new Subject<Event>();
|
||||
|
||||
private currentRouterState: RouterState;
|
||||
private locationSubscription: Subscription;
|
||||
|
@ -353,7 +343,6 @@ export class Router {
|
|||
this.rawUrlTree = this.currentUrlTree;
|
||||
this.configLoader = new RouterConfigLoader(loader, compiler);
|
||||
this.currentRouterState = createEmptyState(this.currentUrlTree, this.rootComponentType);
|
||||
|
||||
this.processNavigations();
|
||||
}
|
||||
|
||||
|
@ -403,19 +392,13 @@ export class Router {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current route state.
|
||||
*/
|
||||
/** The current route state */
|
||||
get routerState(): RouterState { return this.currentRouterState; }
|
||||
|
||||
/**
|
||||
* Returns the current url.
|
||||
*/
|
||||
/** The current url */
|
||||
get url(): string { return this.serializeUrl(this.currentUrlTree); }
|
||||
|
||||
/**
|
||||
* Returns an observable of route events
|
||||
*/
|
||||
/** An observable of router events */
|
||||
get events(): Observable<Event> { return this.routerEvents; }
|
||||
|
||||
/**
|
||||
|
@ -428,7 +411,7 @@ export class Router {
|
|||
* { path: 'team/:id', component: TeamCmp, children: [
|
||||
* { path: 'simple', component: SimpleCmp },
|
||||
* { path: 'user/:name', component: UserCmp }
|
||||
* ] }
|
||||
* ]}
|
||||
* ]);
|
||||
* ```
|
||||
*/
|
||||
|
@ -437,14 +420,10 @@ export class Router {
|
|||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @docsNotRequired
|
||||
*/
|
||||
/** @docsNotRequired */
|
||||
ngOnDestroy() { this.dispose(); }
|
||||
|
||||
/**
|
||||
* Disposes of the router.
|
||||
*/
|
||||
/** Disposes of the router */
|
||||
dispose(): void {
|
||||
if (this.locationSubscription) {
|
||||
this.locationSubscription.unsubscribe();
|
||||
|
@ -496,7 +475,7 @@ export class Router {
|
|||
createUrlTree(
|
||||
commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams,
|
||||
preserveFragment}: NavigationExtras = {}): UrlTree {
|
||||
const a = relativeTo ? relativeTo : this.routerState.root;
|
||||
const a = relativeTo || this.routerState.root;
|
||||
const q = preserveQueryParams ? this.currentUrlTree.queryParams : queryParams;
|
||||
const f = preserveFragment ? this.currentUrlTree.fragment : fragment;
|
||||
return createUrlTree(a, this.currentUrlTree, commands, q, f);
|
||||
|
@ -506,9 +485,9 @@ export class Router {
|
|||
* Navigate based on the provided url. This navigation is always absolute.
|
||||
*
|
||||
* Returns a promise that:
|
||||
* - is resolved with 'true' when navigation succeeds
|
||||
* - is resolved with 'false' when navigation fails
|
||||
* - is rejected when an error happens
|
||||
* - resolves to 'true' when navigation succeeds,
|
||||
* - resolves to 'false' when navigation fails,
|
||||
* - is rejected when an error happens.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
|
@ -527,21 +506,21 @@ export class Router {
|
|||
if (url instanceof UrlTree) {
|
||||
return this.scheduleNavigation(
|
||||
this.urlHandlingStrategy.merge(url, this.rawUrlTree), true, extras);
|
||||
} else {
|
||||
}
|
||||
|
||||
const urlTree = this.urlSerializer.parse(url);
|
||||
return this.scheduleNavigation(
|
||||
this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree), true, extras);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate based on the provided array of commands and a starting point.
|
||||
* If no starting route is provided, the navigation is absolute.
|
||||
*
|
||||
* Returns a promise that:
|
||||
* - is resolved with 'true' when navigation succeeds
|
||||
* - is resolved with 'false' when navigation fails
|
||||
* - is rejected when an error happens
|
||||
* - resolves to 'true' when navigation succeeds,
|
||||
* - resolves to 'false' when navigation fails,
|
||||
* - is rejected when an error happens.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
|
@ -549,11 +528,11 @@ export class Router {
|
|||
* router.navigate(['team', 33, 'user', 11], {relativeTo: route});
|
||||
*
|
||||
* // Navigate without updating the URL
|
||||
* router.navigate(['team', 33, 'user', 11], {relativeTo: route, skipLocationChange: true });
|
||||
* router.navigate(['team', 33, 'user', 11], {relativeTo: route, skipLocationChange: true});
|
||||
* ```
|
||||
*
|
||||
* In opposite to `navigateByUrl`, `navigate` always takes a delta
|
||||
* that is applied to the current URL.
|
||||
* In opposite to `navigateByUrl`, `navigate` always takes a delta that is applied to the current
|
||||
* URL.
|
||||
*/
|
||||
navigate(commands: any[], extras: NavigationExtras = {skipLocationChange: false}):
|
||||
Promise<boolean> {
|
||||
|
@ -563,19 +542,13 @@ export class Router {
|
|||
return this.navigateByUrl(this.createUrlTree(commands, extras), extras);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a {@link UrlTree} into a string.
|
||||
*/
|
||||
/** Serializes a {@link UrlTree} into a string */
|
||||
serializeUrl(url: UrlTree): string { return this.urlSerializer.serialize(url); }
|
||||
|
||||
/**
|
||||
* Parses a string into a {@link UrlTree}.
|
||||
*/
|
||||
/** Parses a string into a {@link UrlTree} */
|
||||
parseUrl(url: string): UrlTree { return this.urlSerializer.parse(url); }
|
||||
|
||||
/**
|
||||
* Returns if the url is activated or not.
|
||||
*/
|
||||
/** Returns whether the url is activated */
|
||||
isActive(url: string|UrlTree, exact: boolean): boolean {
|
||||
if (url instanceof UrlTree) {
|
||||
return containsTree(this.currentUrlTree, url, exact);
|
||||
|
|
|
@ -16,7 +16,6 @@ import {UrlSegment, UrlSegmentGroup, UrlTree, equalSegments} from './url_tree';
|
|||
import {merge, shallowEqual, shallowEqualArrays} from './utils/collection';
|
||||
import {Tree, TreeNode} from './utils/tree';
|
||||
|
||||
|
||||
/**
|
||||
* @whatItDoes Represents the state of the router.
|
||||
*
|
||||
|
@ -45,14 +44,10 @@ import {Tree, TreeNode} from './utils/tree';
|
|||
* @stable
|
||||
*/
|
||||
export class RouterState extends Tree<ActivatedRoute> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
constructor(
|
||||
root: TreeNode<ActivatedRoute>,
|
||||
/**
|
||||
* The current snapshot of the router state.
|
||||
*/
|
||||
/** The current snapshot of the router state */
|
||||
public snapshot: RouterStateSnapshot) {
|
||||
super(root);
|
||||
setRouterStateSnapshot<RouterState, ActivatedRoute>(this, root);
|
||||
|
@ -90,17 +85,18 @@ export function createEmptyStateSnapshot(
|
|||
/**
|
||||
* @whatItDoes Contains the information about a route associated with a component loaded in an
|
||||
* outlet.
|
||||
* ActivatedRoute can also be used to traverse the router state tree.
|
||||
* An `ActivatedRoute` can also be used to traverse the router state tree.
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* ```
|
||||
* @Component({templateUrl:'./my-component.html'})
|
||||
* @Component({...})
|
||||
* class MyComponent {
|
||||
* constructor(route: ActivatedRoute) {
|
||||
* const id: Observable<string> = route.params.map(p => p.id);
|
||||
* const url: Observable<string> = route.url.map(s => s.join(''));
|
||||
* const user = route.data.map(d => d.user); //includes `data` and `resolve`
|
||||
* const url: Observable<string> = route.url.map(segments => segments.join(''));
|
||||
* // route.data includes both `data` and `resolve`
|
||||
* const user = route.data.map(d => d.user);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
|
@ -108,112 +104,64 @@ export function createEmptyStateSnapshot(
|
|||
* @stable
|
||||
*/
|
||||
export class ActivatedRoute {
|
||||
/** The current snapshot of this route */
|
||||
snapshot: ActivatedRouteSnapshot;
|
||||
/** @internal */
|
||||
_futureSnapshot: ActivatedRouteSnapshot;
|
||||
|
||||
/**
|
||||
* The current snapshot of this route.
|
||||
*/
|
||||
snapshot: ActivatedRouteSnapshot;
|
||||
|
||||
/** @internal */
|
||||
_routerState: RouterState;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
constructor(
|
||||
/**
|
||||
* The URL segments matched by this route. The observable will emit a new value when
|
||||
* the array of segments changes.
|
||||
*/
|
||||
/** An observable of the URL segments matched by this route */
|
||||
public url: Observable<UrlSegment[]>,
|
||||
|
||||
/**
|
||||
* The matrix parameters scoped to this route. The observable will emit a new value when
|
||||
* the set of the parameters changes.
|
||||
*/
|
||||
/** An observable of the matrix parameters scoped to this route */
|
||||
public params: Observable<Params>,
|
||||
|
||||
/**
|
||||
* The query parameters shared by all the routes. The observable will emit a new value when
|
||||
* the set of the parameters changes.
|
||||
*/
|
||||
/** An observable of the query parameters shared by all the routes */
|
||||
public queryParams: Observable<Params>,
|
||||
|
||||
/**
|
||||
* The URL fragment shared by all the routes. The observable will emit a new value when
|
||||
* the URL fragment changes.
|
||||
*/
|
||||
/** An observable of the URL fragment shared by all the routes */
|
||||
public fragment: Observable<string>,
|
||||
|
||||
/**
|
||||
* The static and resolved data of this route. The observable will emit a new value when
|
||||
* any of the resolvers returns a new object.
|
||||
*/
|
||||
/** An observable of the static and resolved data of this route. */
|
||||
public data: Observable<Data>,
|
||||
|
||||
/**
|
||||
* The outlet name of the route. It's a constant.
|
||||
*/
|
||||
/** The outlet name of the route. It's a constant */
|
||||
public outlet: string,
|
||||
|
||||
/**
|
||||
* The component of the route. It's a constant.
|
||||
*/
|
||||
public component: Type<any>|string, // TODO: vsavkin: remove |string
|
||||
futureSnapshot: ActivatedRouteSnapshot) {
|
||||
/** The component of the route. It's a constant */
|
||||
// TODO(vsavkin): remove |string
|
||||
public component: Type<any>|string, futureSnapshot: ActivatedRouteSnapshot) {
|
||||
this._futureSnapshot = futureSnapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* The configuration used to match this route.
|
||||
*/
|
||||
/** The configuration used to match this route */
|
||||
get routeConfig(): Route { return this._futureSnapshot.routeConfig; }
|
||||
|
||||
/**
|
||||
* The root of the router state.
|
||||
*/
|
||||
/** The root of the router state */
|
||||
get root(): ActivatedRoute { return this._routerState.root; }
|
||||
|
||||
/**
|
||||
* The parent of this route in the router state tree.
|
||||
*/
|
||||
/** The parent of this route in the router state tree */
|
||||
get parent(): ActivatedRoute { return this._routerState.parent(this); }
|
||||
|
||||
/**
|
||||
* The first child of this route in the router state tree.
|
||||
*/
|
||||
/** The first child of this route in the router state tree */
|
||||
get firstChild(): ActivatedRoute { return this._routerState.firstChild(this); }
|
||||
|
||||
/**
|
||||
* The children of this route in the router state tree.
|
||||
*/
|
||||
/** The children of this route in the router state tree */
|
||||
get children(): ActivatedRoute[] { return this._routerState.children(this); }
|
||||
|
||||
/**
|
||||
* The path from the root of the router state tree to this route.
|
||||
*/
|
||||
/** The path from the root of the router state tree to this route */
|
||||
get pathFromRoot(): ActivatedRoute[] { return this._routerState.pathFromRoot(this); }
|
||||
|
||||
/**
|
||||
* @docsNotRequired
|
||||
*/
|
||||
toString(): string {
|
||||
return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
export type Inherited = {
|
||||
params: Params; data: Data; resolve: Data;
|
||||
params: Params,
|
||||
data: Data,
|
||||
resolve: Data,
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
export function
|
||||
inheritedParamsDataResolve(route: ActivatedRouteSnapshot):
|
||||
Inherited {
|
||||
|
@ -269,59 +217,32 @@ inheritedParamsDataResolve(route: ActivatedRouteSnapshot):
|
|||
export class ActivatedRouteSnapshot {
|
||||
/** @internal **/
|
||||
_routeConfig: Route;
|
||||
|
||||
/** @internal **/
|
||||
_urlSegment: UrlSegmentGroup;
|
||||
|
||||
/** @internal */
|
||||
_lastPathIndex: number;
|
||||
|
||||
/** @internal */
|
||||
_resolve: ResolveData;
|
||||
|
||||
/** @internal */
|
||||
_resolvedData: Data;
|
||||
|
||||
/** @internal */
|
||||
_routerState: RouterStateSnapshot;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
constructor(
|
||||
/**
|
||||
* The URL segments matched by this route.
|
||||
*/
|
||||
/** The URL segments matched by this route */
|
||||
public url: UrlSegment[],
|
||||
|
||||
/**
|
||||
* The matrix parameters scoped to this route.
|
||||
*/
|
||||
/** The matrix parameters scoped to this route */
|
||||
public params: Params,
|
||||
|
||||
/**
|
||||
* The query parameters shared by all the routes.
|
||||
*/
|
||||
/** The query parameters shared by all the routes */
|
||||
public queryParams: Params,
|
||||
|
||||
/**
|
||||
* The URL fragment shared by all the routes.
|
||||
*/
|
||||
/** The URL fragment shared by all the routes */
|
||||
public fragment: string,
|
||||
|
||||
/**
|
||||
* The static and resolved data of this route.
|
||||
*/
|
||||
/** The static and resolved data of this route */
|
||||
public data: Data,
|
||||
|
||||
/**
|
||||
* The outlet name of the route.
|
||||
*/
|
||||
/** The outlet name of the route */
|
||||
public outlet: string,
|
||||
|
||||
/**
|
||||
* The component of the route.
|
||||
*/
|
||||
/** The component of the route */
|
||||
public component: Type<any>|string, routeConfig: Route, urlSegment: UrlSegmentGroup,
|
||||
lastPathIndex: number, resolve: ResolveData) {
|
||||
this._routeConfig = routeConfig;
|
||||
|
@ -330,41 +251,26 @@ export class ActivatedRouteSnapshot {
|
|||
this._resolve = resolve;
|
||||
}
|
||||
|
||||
/**
|
||||
* The configuration used to match this route.
|
||||
*/
|
||||
/** The configuration used to match this route */
|
||||
get routeConfig(): Route { return this._routeConfig; }
|
||||
|
||||
/**
|
||||
* The root of the router state.
|
||||
*/
|
||||
/** The root of the router state */
|
||||
get root(): ActivatedRouteSnapshot { return this._routerState.root; }
|
||||
|
||||
/**
|
||||
* The parent of this route in the router state tree.
|
||||
*/
|
||||
/** The parent of this route in the router state tree */
|
||||
get parent(): ActivatedRouteSnapshot { return this._routerState.parent(this); }
|
||||
|
||||
/**
|
||||
* The first child of this route in the router state tree.
|
||||
*/
|
||||
/** The first child of this route in the router state tree */
|
||||
get firstChild(): ActivatedRouteSnapshot { return this._routerState.firstChild(this); }
|
||||
|
||||
/**
|
||||
* The children of this route in the router state tree.
|
||||
*/
|
||||
/** The children of this route in the router state tree */
|
||||
get children(): ActivatedRouteSnapshot[] { return this._routerState.children(this); }
|
||||
|
||||
/**
|
||||
* The path from the root of the router state tree to this route.
|
||||
*/
|
||||
/** The path from the root of the router state tree to this route */
|
||||
get pathFromRoot(): ActivatedRouteSnapshot[] { return this._routerState.pathFromRoot(this); }
|
||||
|
||||
/**
|
||||
* @docsNotRequired
|
||||
*/
|
||||
toString(): string {
|
||||
const url = this.url.map(s => s.toString()).join('/');
|
||||
const url = this.url.map(segment => segment.toString()).join('/');
|
||||
const matched = this._routeConfig ? this._routeConfig.path : '';
|
||||
return `Route(url:'${url}', path:'${matched}')`;
|
||||
}
|
||||
|
@ -396,11 +302,8 @@ export class ActivatedRouteSnapshot {
|
|||
* @stable
|
||||
*/
|
||||
export class RouterStateSnapshot extends Tree<ActivatedRouteSnapshot> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
constructor(
|
||||
|
||||
/** The url from which this snapshot was created */
|
||||
public url: string, root: TreeNode<ActivatedRouteSnapshot>) {
|
||||
super(root);
|
||||
|
|
|
@ -17,10 +17,10 @@ export function containsTree(container: UrlTree, containee: UrlTree, exact: bool
|
|||
if (exact) {
|
||||
return equalQueryParams(container.queryParams, containee.queryParams) &&
|
||||
equalSegmentGroups(container.root, containee.root);
|
||||
} else {
|
||||
}
|
||||
|
||||
return containsQueryParams(container.queryParams, containee.queryParams) &&
|
||||
containsSegmentGroup(container.root, containee.root);
|
||||
}
|
||||
}
|
||||
|
||||
function equalQueryParams(
|
||||
|
@ -103,79 +103,49 @@ function containsSegmentGroupHelper(
|
|||
* @stable
|
||||
*/
|
||||
export class UrlTree {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
constructor(
|
||||
/**
|
||||
* The root segment group of the URL tree.
|
||||
*/
|
||||
/** The root segment group of the URL tree */
|
||||
public root: UrlSegmentGroup,
|
||||
/**
|
||||
* The query params of the URL.
|
||||
*/
|
||||
/** The query params of the URL */
|
||||
public queryParams: {[key: string]: string},
|
||||
/**
|
||||
* The fragment of the URL.
|
||||
*/
|
||||
/** The fragment of the URL */
|
||||
public fragment: string) {}
|
||||
|
||||
/**
|
||||
* @docsNotRequired
|
||||
*/
|
||||
/** @docsNotRequired */
|
||||
toString(): string { return new DefaultUrlSerializer().serialize(this); }
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Represents the parsed URL segment.
|
||||
* @whatItDoes Represents the parsed URL segment group.
|
||||
*
|
||||
* See {@link UrlTree} for more information.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export class UrlSegmentGroup {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
_sourceSegment: UrlSegmentGroup;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
_segmentIndexShift: number;
|
||||
|
||||
/**
|
||||
* The parent node in the url tree.
|
||||
*/
|
||||
public parent: UrlSegmentGroup = null;
|
||||
/** The parent node in the url tree */
|
||||
parent: UrlSegmentGroup = null;
|
||||
|
||||
constructor(
|
||||
/**
|
||||
* The URL segments of this group. See {@link UrlSegment} for more information.
|
||||
*/
|
||||
/** The URL segments of this group. See {@link UrlSegment} for more information */
|
||||
public segments: UrlSegment[],
|
||||
/**
|
||||
* The list of children of this group.
|
||||
*/
|
||||
public children: {[key: string]: UrlSegmentGroup}
|
||||
|
||||
) {
|
||||
/** The list of children of this group */
|
||||
public children: {[key: string]: UrlSegmentGroup}) {
|
||||
forEach(children, (v: any, k: any) => v.parent = this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the segment has child segments
|
||||
*/
|
||||
/** Wether the segment has child segments */
|
||||
hasChildren(): boolean { return this.numberOfChildren > 0; }
|
||||
|
||||
/**
|
||||
* Returns the number of child sements.
|
||||
*/
|
||||
/** Number of child segments */
|
||||
get numberOfChildren(): number { return Object.keys(this.children).length; }
|
||||
|
||||
/**
|
||||
* @docsNotRequired
|
||||
*/
|
||||
/** @docsNotRequired */
|
||||
toString(): string { return serializePaths(this); }
|
||||
}
|
||||
|
||||
|
@ -200,26 +170,20 @@ export class UrlSegmentGroup {
|
|||
*
|
||||
* @description
|
||||
*
|
||||
* A UrlSegment is a part of a URL between the two slashes. It contains a path and
|
||||
* the matrix parameters associated with the segment.
|
||||
* A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
|
||||
* parameters associated with the segment.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export class UrlSegment {
|
||||
constructor(
|
||||
/**
|
||||
* The path part of a URL segment.
|
||||
*/
|
||||
/** The path part of a URL segment */
|
||||
public path: string,
|
||||
|
||||
/**
|
||||
* The matrix parameters associated with a segment.
|
||||
*/
|
||||
public parameters: {[key: string]: string}) {}
|
||||
/** The matrix parameters associated with a segment */
|
||||
public parameters: {[name: string]: string}) {}
|
||||
|
||||
/**
|
||||
* @docsNotRequired
|
||||
*/
|
||||
/** @docsNotRequired */
|
||||
toString(): string { return serializePath(this); }
|
||||
}
|
||||
|
||||
|
@ -268,14 +232,10 @@ export function mapChildrenIntoArray<T>(
|
|||
* @stable
|
||||
*/
|
||||
export abstract class UrlSerializer {
|
||||
/**
|
||||
* Parse a url into a {@link UrlTree}.
|
||||
*/
|
||||
/** Parse a url into a {@link UrlTree} */
|
||||
abstract parse(url: string): UrlTree;
|
||||
|
||||
/**
|
||||
* Converts a {@link UrlTree} into a url.
|
||||
*/
|
||||
/** Converts a {@link UrlTree} into a url */
|
||||
abstract serialize(tree: UrlTree): string;
|
||||
}
|
||||
|
||||
|
@ -298,17 +258,13 @@ export abstract class UrlSerializer {
|
|||
* @stable
|
||||
*/
|
||||
export class DefaultUrlSerializer implements UrlSerializer {
|
||||
/**
|
||||
* Parse a url into a {@link UrlTree}.
|
||||
*/
|
||||
/** Parses a url into a {@link UrlTree} */
|
||||
parse(url: string): UrlTree {
|
||||
const p = new UrlParser(url);
|
||||
return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link UrlTree} into a url.
|
||||
*/
|
||||
/** Converts a {@link UrlTree} into a url */
|
||||
serialize(tree: UrlTree): string {
|
||||
const segment = `/${serializeSegment(tree.root, true)}`;
|
||||
const query = serializeQueryParams(tree.queryParams);
|
||||
|
@ -383,6 +339,7 @@ function serializeQueryParams(params: {[key: string]: any}): string {
|
|||
class Pair<A, B> {
|
||||
constructor(public first: A, public second: B) {}
|
||||
}
|
||||
|
||||
function pairs<T>(obj: {[key: string]: T}): Pair<string, T>[] {
|
||||
const res: Pair<string, T>[] = [];
|
||||
for (const prop in obj) {
|
||||
|
@ -436,9 +393,9 @@ class UrlParser {
|
|||
|
||||
if (this.remaining === '' || this.remaining.startsWith('?') || this.remaining.startsWith('#')) {
|
||||
return new UrlSegmentGroup([], {});
|
||||
} else {
|
||||
return new UrlSegmentGroup([], this.parseChildren());
|
||||
}
|
||||
|
||||
return new UrlSegmentGroup([], this.parseChildren());
|
||||
}
|
||||
|
||||
parseChildren(): {[key: string]: UrlSegmentGroup} {
|
||||
|
@ -508,9 +465,9 @@ class UrlParser {
|
|||
parseFragment(): string {
|
||||
if (this.peekStartsWith('#')) {
|
||||
return decodeURI(this.remaining.substring(1));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
parseMatrixParams(): {[key: string]: any} {
|
||||
|
|
|
@ -60,7 +60,7 @@ export function last<T>(a: T[]): T {
|
|||
}
|
||||
|
||||
export function and(bools: boolean[]): boolean {
|
||||
return bools.reduce((a, b) => a && b, true);
|
||||
return !bools.some(v => !v);
|
||||
}
|
||||
|
||||
export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
|
||||
|
@ -81,8 +81,7 @@ export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key:
|
|||
return m;
|
||||
}
|
||||
|
||||
export function forEach<K, V>(
|
||||
map: {[key: string]: V}, callback: /*(V, K) => void*/ Function): void {
|
||||
export function forEach<K, V>(map: {[key: string]: V}, callback: (v: V, k: string) => void): void {
|
||||
for (const prop in map) {
|
||||
if (map.hasOwnProperty(prop)) {
|
||||
callback(map[prop], prop);
|
||||
|
@ -117,9 +116,9 @@ export function waitForMap<A, B>(
|
|||
const concatted$ = concatAll.call(of (...waitFor));
|
||||
const last$ = l.last.call(concatted$);
|
||||
return map.call(last$, () => res);
|
||||
} else {
|
||||
return of (res);
|
||||
}
|
||||
|
||||
return of (res);
|
||||
}
|
||||
|
||||
export function andObservables(observables: Observable<Observable<any>>): Observable<boolean> {
|
||||
|
@ -130,9 +129,11 @@ export function andObservables(observables: Observable<Observable<any>>): Observ
|
|||
export function wrapIntoObservable<T>(value: T | Promise<T>| Observable<T>): Observable<T> {
|
||||
if (value instanceof Observable) {
|
||||
return value;
|
||||
} else if (value instanceof Promise) {
|
||||
return fromPromise(value);
|
||||
} else {
|
||||
return of (value);
|
||||
}
|
||||
|
||||
if (value instanceof Promise) {
|
||||
return fromPromise(value);
|
||||
}
|
||||
|
||||
return of (value);
|
||||
}
|
|
@ -366,13 +366,13 @@ export declare abstract class UrlHandlingStrategy {
|
|||
/** @stable */
|
||||
export declare class UrlSegment {
|
||||
parameters: {
|
||||
[key: string]: string;
|
||||
[name: string]: string;
|
||||
};
|
||||
path: string;
|
||||
constructor(
|
||||
path: string,
|
||||
parameters: {
|
||||
[key: string]: string;
|
||||
[name: string]: string;
|
||||
});
|
||||
toString(): string;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue