mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
refactor(router): monkey patch the router instead of extending it
This commit is contained in:
@ -11,7 +11,6 @@ export { VirtualFooter } from './directives/virtual-footer';
|
||||
export { RouterOutlet } from './router/outlet';
|
||||
export { AsyncActivateRoutes } from './router/async-activated-routes';
|
||||
export { OutletInjector } from './router/outlet-injector';
|
||||
export { CustomRouter } from './router/router';
|
||||
export { IonicRouterModule } from './router/router-module';
|
||||
|
||||
/* Providers */
|
||||
|
103
packages/angular/src/router/monkey-patch-router.ts
Normal file
103
packages/angular/src/router/monkey-patch-router.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import {
|
||||
Event,
|
||||
NavigationCancel,
|
||||
NavigationEnd,
|
||||
NavigationError,
|
||||
RouterState,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
|
||||
import { AsyncActivateRoutes } from './async-activated-routes';
|
||||
|
||||
export function monkeyPatchRouter(router: any) {
|
||||
router.activateRoutes = (state: Observable<{appliedUrl: string, state: RouterState, shouldActivate: boolean}>, storedState: RouterState,
|
||||
storedUrl: UrlTree, id: number, url: UrlTree, rawUrl: UrlTree, skipLocationChange: boolean, replaceUrl: boolean, resolvePromise: any, rejectPromise: any) => {
|
||||
|
||||
// applied the new router state
|
||||
// this operation has side effects
|
||||
let navigationIsSuccessful: boolean;
|
||||
|
||||
const routes: AsyncActivateRoutes[] = [];
|
||||
state
|
||||
.forEach(({appliedUrl, state, shouldActivate}: any) => {
|
||||
if (!shouldActivate || id !== router.navigationId) {
|
||||
navigationIsSuccessful = false;
|
||||
return;
|
||||
}
|
||||
|
||||
router.currentUrlTree = appliedUrl;
|
||||
router.rawUrlTree = router.urlHandlingStrategy.merge(router.currentUrlTree, rawUrl);
|
||||
|
||||
(router as{routerState: RouterState}).routerState = state;
|
||||
|
||||
if (!skipLocationChange) {
|
||||
const path = router.urlSerializer.serialize(router.rawUrlTree);
|
||||
if (router.location.isCurrentPathEqualTo(path) || replaceUrl) {
|
||||
router.location.replaceState(path, '');
|
||||
} else {
|
||||
router.location.go(path, '');
|
||||
}
|
||||
}
|
||||
|
||||
routes.push(new AsyncActivateRoutes(router.routeReuseStrategy, state, storedState, (evt: Event) => router.triggerEvent(evt)))
|
||||
|
||||
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
const promises = routes.map(activatedRoute => activatedRoute.activate(router.rootContexts));
|
||||
return Promise.all(promises)
|
||||
.then(
|
||||
() => {
|
||||
navigationIsSuccessful = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
if (navigationIsSuccessful) {
|
||||
router.navigated = true;
|
||||
router.lastSuccessfulId = id;
|
||||
(router.events as Subject<Event>)
|
||||
.next(new NavigationEnd(
|
||||
id, router.serializeUrl(url), router.serializeUrl(router.currentUrlTree)));
|
||||
resolvePromise(true);
|
||||
} else {
|
||||
router.resetUrlToCurrentUrlTree();
|
||||
(router.events as Subject<Event>)
|
||||
.next(new NavigationCancel(id, router.serializeUrl(url), ''));
|
||||
resolvePromise(false);
|
||||
}
|
||||
},
|
||||
(e: any) => {
|
||||
if (isNavigationCancelingError(e)) {
|
||||
router.navigated = true;
|
||||
router.resetStateAndUrl(storedState, storedUrl, rawUrl);
|
||||
(router.events as Subject<Event>)
|
||||
.next(new NavigationCancel(id, router.serializeUrl(url), e.message));
|
||||
|
||||
resolvePromise(false);
|
||||
} else {
|
||||
router.resetStateAndUrl(storedState, storedUrl, rawUrl);
|
||||
(router.events as any as Subject<Event>)
|
||||
.next(new NavigationError(id, router.serializeUrl(url), e));
|
||||
try {
|
||||
resolvePromise(router.errorHandler(e));
|
||||
} catch (ee) {
|
||||
rejectPromise(ee);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
function isNavigationCancelingError(error: any) {
|
||||
return error && (/** @type {?} */ (error))[NAVIGATION_CANCELING_ERROR];
|
||||
}
|
||||
|
||||
const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
|
@ -191,7 +191,6 @@ export function getDataFromObservable(activatedRoute: ActivatedRoute) {
|
||||
export function getDataFromParams(activatedRoute: ActivatedRoute) {
|
||||
return new Promise((resolve) => {
|
||||
activatedRoute.params.subscribe((data: any) => {
|
||||
console.log('data: ', data);
|
||||
resolve(data || {});
|
||||
});
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ import {
|
||||
import { IonicAngularModule } from '../module';
|
||||
|
||||
import { PushPopOutletContexts } from './push-pop-outlet-contexts';
|
||||
import { CustomRouter } from './router';
|
||||
import { monkeyPatchRouter } from './monkey-patch-router';
|
||||
import { RouteEventHandler } from './route-event-handler';
|
||||
import { RouterOutlet } from './outlet';
|
||||
import { flatten } from './router-utils';
|
||||
@ -75,9 +75,12 @@ export function setupRouter(
|
||||
config: Route[][], opts: ExtraOptions = {}, urlHandlingStrategy?: UrlHandlingStrategy,
|
||||
routeReuseStrategy?: RouteReuseStrategy) {
|
||||
|
||||
const router = new CustomRouter(
|
||||
const router = new Router(
|
||||
null, urlSerializer, contexts, location, injector, loader, compiler, flatten(config));
|
||||
|
||||
monkeyPatchRouter(router);
|
||||
|
||||
|
||||
if (urlHandlingStrategy) {
|
||||
router.urlHandlingStrategy = urlHandlingStrategy;
|
||||
}
|
||||
|
@ -1,111 +0,0 @@
|
||||
|
||||
|
||||
import {
|
||||
Event,
|
||||
NavigationCancel,
|
||||
NavigationEnd,
|
||||
NavigationError,
|
||||
Router,
|
||||
RouterState,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
|
||||
import { AsyncActivateRoutes } from './async-activated-routes';
|
||||
|
||||
export class CustomRouter extends Router {
|
||||
|
||||
protected activateRoutes(state: Observable<{appliedUrl: string, state: RouterState, shouldActivate: boolean}>, storedState: RouterState,
|
||||
storedUrl: UrlTree, id: number, url: UrlTree, rawUrl: UrlTree, skipLocationChange: boolean, replaceUrl: boolean, resolvePromise: any, rejectPromise: any) {
|
||||
|
||||
// applied the new router state
|
||||
// this operation has side effects
|
||||
let navigationIsSuccessful: boolean;
|
||||
|
||||
const routes: AsyncActivateRoutes[] = [];
|
||||
state
|
||||
.forEach(({appliedUrl, state, shouldActivate}: any) => {
|
||||
if (!shouldActivate || id !== this.navigationId) {
|
||||
navigationIsSuccessful = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentUrlTree = appliedUrl;
|
||||
this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl);
|
||||
|
||||
(this as{routerState: RouterState}).routerState = state;
|
||||
|
||||
if (!skipLocationChange) {
|
||||
const path = this.urlSerializer.serialize(this.rawUrlTree);
|
||||
if (this.location.isCurrentPathEqualTo(path) || replaceUrl) {
|
||||
this.location.replaceState(path, '');
|
||||
} else {
|
||||
this.location.go(path, '');
|
||||
}
|
||||
}
|
||||
|
||||
routes.push(new AsyncActivateRoutes(this.routeReuseStrategy, state, storedState, (evt: Event) => this.triggerEvent(evt)))
|
||||
|
||||
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
const promises = routes.map(activatedRoute => activatedRoute.activate(this.rootContexts));
|
||||
return Promise.all(promises)
|
||||
.then(
|
||||
() => {
|
||||
navigationIsSuccessful = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
if (navigationIsSuccessful) {
|
||||
this.navigated = true;
|
||||
this.lastSuccessfulId = id;
|
||||
(this.events as Subject<Event>)
|
||||
.next(new NavigationEnd(
|
||||
id, this.serializeUrl(url), this.serializeUrl(this.currentUrlTree)));
|
||||
resolvePromise(true);
|
||||
} else {
|
||||
this.resetUrlToCurrentUrlTree();
|
||||
(this.events as Subject<Event>)
|
||||
.next(new NavigationCancel(id, this.serializeUrl(url), ''));
|
||||
resolvePromise(false);
|
||||
}
|
||||
},
|
||||
(e: any) => {
|
||||
if (isNavigationCancelingError(e)) {
|
||||
this.navigated = true;
|
||||
this.resetStateAndUrl(storedState, storedUrl, rawUrl);
|
||||
(this.events as Subject<Event>)
|
||||
.next(new NavigationCancel(id, this.serializeUrl(url), e.message));
|
||||
|
||||
resolvePromise(false);
|
||||
} else {
|
||||
this.resetStateAndUrl(storedState, storedUrl, rawUrl);
|
||||
(this.events as any as Subject<Event>)
|
||||
.next(new NavigationError(id, this.serializeUrl(url), e));
|
||||
try {
|
||||
resolvePromise(this.errorHandler(e));
|
||||
} catch (ee) {
|
||||
rejectPromise(ee);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
triggerEvent(e: Event): void { (this.events as any as Subject<Event>).next(e); }
|
||||
|
||||
serializeUrl(url: UrlTree): string { return this.urlSerializer.serialize(url); }
|
||||
|
||||
}
|
||||
|
||||
const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
|
||||
|
||||
export function isNavigationCancelingError(error: Error) {
|
||||
return error && (error as any)[NAVIGATION_CANCELING_ERROR];
|
||||
}
|
Reference in New Issue
Block a user