mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
refactor(navigation): async component loading (aka lazy loading)
async component loading (aka lazy loading)
This commit is contained in:
@ -1,7 +1,9 @@
|
|||||||
|
import { ComponentFactory, ComponentFactoryResolver } from '@angular/core';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
import { App } from '../components/app/app';
|
import { App } from '../components/app/app';
|
||||||
import { convertToViews, isNav, isTab, isTabs, NavSegment, DIRECTION_BACK } from './nav-util';
|
import { convertToViews, isNav, isTab, isTabs, NavLink, NavSegment, DIRECTION_BACK } from './nav-util';
|
||||||
|
import { ModuleLoader } from '../util/module-loader';
|
||||||
import { isArray, isPresent } from '../util/util';
|
import { isArray, isPresent } from '../util/util';
|
||||||
import { Nav } from '../components/nav/nav';
|
import { Nav } from '../components/nav/nav';
|
||||||
import { NavController } from './nav-controller';
|
import { NavController } from './nav-controller';
|
||||||
@ -117,20 +119,23 @@ import { ViewController } from './view-controller';
|
|||||||
*/
|
*/
|
||||||
export class DeepLinker {
|
export class DeepLinker {
|
||||||
|
|
||||||
/**
|
/** @internal */
|
||||||
* @internal
|
_segments: NavSegment[] = [];
|
||||||
*/
|
/** @internal */
|
||||||
segments: NavSegment[] = [];
|
_history: string[] = [];
|
||||||
/**
|
/** @internal */
|
||||||
* @internal
|
_indexAliasUrl: string;
|
||||||
*/
|
/** @internal */
|
||||||
history: string[] = [];
|
_cfrMap = new Map<any, ComponentFactoryResolver>();
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
indexAliasUrl: string;
|
|
||||||
|
|
||||||
constructor(public _app: App, public _serializer: UrlSerializer, public _location: Location) { }
|
|
||||||
|
constructor(
|
||||||
|
public _app: App,
|
||||||
|
public _serializer: UrlSerializer,
|
||||||
|
public _location: Location,
|
||||||
|
public _moduleLoader: ModuleLoader,
|
||||||
|
public _baseCfr: ComponentFactoryResolver
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -141,14 +146,14 @@ export class DeepLinker {
|
|||||||
console.debug(`DeepLinker, init load: ${browserUrl}`);
|
console.debug(`DeepLinker, init load: ${browserUrl}`);
|
||||||
|
|
||||||
// update the Path from the browser URL
|
// update the Path from the browser URL
|
||||||
this.segments = this._serializer.parse(browserUrl);
|
this._segments = this._serializer.parse(browserUrl);
|
||||||
|
|
||||||
// remember this URL in our internal history stack
|
// remember this URL in our internal history stack
|
||||||
this.historyPush(browserUrl);
|
this._historyPush(browserUrl);
|
||||||
|
|
||||||
// listen for browser URL changes
|
// listen for browser URL changes
|
||||||
this._location.subscribe((locationChg: { url: string }) => {
|
this._location.subscribe((locationChg: { url: string }) => {
|
||||||
this.urlChange(normalizeUrl(locationChg.url));
|
this._urlChange(normalizeUrl(locationChg.url));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,23 +161,23 @@ export class DeepLinker {
|
|||||||
* The browser's location has been updated somehow.
|
* The browser's location has been updated somehow.
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
urlChange(browserUrl: string) {
|
_urlChange(browserUrl: string) {
|
||||||
// do nothing if this url is the same as the current one
|
// do nothing if this url is the same as the current one
|
||||||
if (!this.isCurrentUrl(browserUrl)) {
|
if (!this._isCurrentUrl(browserUrl)) {
|
||||||
|
|
||||||
if (this.isBackUrl(browserUrl)) {
|
if (this._isBackUrl(browserUrl)) {
|
||||||
// scenario 2: user clicked the browser back button
|
// scenario 2: user clicked the browser back button
|
||||||
// scenario 4: user changed the browser URL to what was the back url was
|
// scenario 4: user changed the browser URL to what was the back url was
|
||||||
// scenario 5: user clicked a link href that was the back url
|
// scenario 5: user clicked a link href that was the back url
|
||||||
console.debug(`DeepLinker, browser urlChange, back to: ${browserUrl}`);
|
console.debug(`DeepLinker, browser urlChange, back to: ${browserUrl}`);
|
||||||
this.historyPop();
|
this._historyPop();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// scenario 3: user click forward button
|
// scenario 3: user click forward button
|
||||||
// scenario 4: user changed browser URL that wasn't the back url
|
// scenario 4: user changed browser URL that wasn't the back url
|
||||||
// scenario 5: user clicked a link href that wasn't the back url
|
// scenario 5: user clicked a link href that wasn't the back url
|
||||||
console.debug(`DeepLinker, browser urlChange, forward to: ${browserUrl}`);
|
console.debug(`DeepLinker, browser urlChange, forward to: ${browserUrl}`);
|
||||||
this.historyPush(browserUrl);
|
this._historyPush(browserUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the app's root nav
|
// get the app's root nav
|
||||||
@ -180,10 +185,10 @@ export class DeepLinker {
|
|||||||
if (appRootNav) {
|
if (appRootNav) {
|
||||||
if (browserUrl === '/') {
|
if (browserUrl === '/') {
|
||||||
// a url change to the index url
|
// a url change to the index url
|
||||||
if (isPresent(this.indexAliasUrl)) {
|
if (isPresent(this._indexAliasUrl)) {
|
||||||
// we already know the indexAliasUrl
|
// we already know the indexAliasUrl
|
||||||
// update the url to use the know alias
|
// update the url to use the know alias
|
||||||
browserUrl = this.indexAliasUrl;
|
browserUrl = this._indexAliasUrl;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// the url change is to the root but we don't
|
// the url change is to the root but we don't
|
||||||
@ -198,8 +203,8 @@ export class DeepLinker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// normal url
|
// normal url
|
||||||
this.segments = this._serializer.parse(browserUrl);
|
this._segments = this._serializer.parse(browserUrl);
|
||||||
this.loadNavFromPath(appRootNav);
|
this._loadNavFromPath(appRootNav);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,13 +221,13 @@ export class DeepLinker {
|
|||||||
if (activeNav) {
|
if (activeNav) {
|
||||||
|
|
||||||
// build up the segments of all the navs from the lowest level
|
// build up the segments of all the navs from the lowest level
|
||||||
this.segments = this.pathFromNavs(activeNav);
|
this._segments = this._pathFromNavs(activeNav);
|
||||||
|
|
||||||
// build a string URL out of the Path
|
// build a string URL out of the Path
|
||||||
const browserUrl = this._serializer.serialize(this.segments);
|
const browserUrl = this._serializer.serialize(this._segments);
|
||||||
|
|
||||||
// update the browser's location
|
// update the browser's location
|
||||||
this.updateLocation(browserUrl, direction);
|
this._updateLocation(browserUrl, direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,35 +235,70 @@ export class DeepLinker {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
updateLocation(browserUrl: string, direction: string) {
|
_updateLocation(browserUrl: string, direction: string) {
|
||||||
if (this.indexAliasUrl === browserUrl) {
|
if (this._indexAliasUrl === browserUrl) {
|
||||||
browserUrl = '/';
|
browserUrl = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (direction === DIRECTION_BACK && this.isBackUrl(browserUrl)) {
|
if (direction === DIRECTION_BACK && this._isBackUrl(browserUrl)) {
|
||||||
// this URL is exactly the same as the back URL
|
// this URL is exactly the same as the back URL
|
||||||
// it's safe to use the browser's location.back()
|
// it's safe to use the browser's location.back()
|
||||||
console.debug(`DeepLinker, location.back(), url: '${browserUrl}'`);
|
console.debug(`DeepLinker, location.back(), url: '${browserUrl}'`);
|
||||||
this.historyPop();
|
this._historyPop();
|
||||||
this._location.back();
|
this._location.back();
|
||||||
|
|
||||||
} else if (!this.isCurrentUrl(browserUrl)) {
|
} else if (!this._isCurrentUrl(browserUrl)) {
|
||||||
// probably navigating forward
|
// probably navigating forward
|
||||||
console.debug(`DeepLinker, location.go('${browserUrl}')`);
|
console.debug(`DeepLinker, location.go('${browserUrl}')`);
|
||||||
this.historyPush(browserUrl);
|
this._historyPush(browserUrl);
|
||||||
this._location.go(browserUrl);
|
this._location.go(browserUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getComponentFromName(componentName: string): Promise<any> {
|
||||||
|
const link = this._serializer.getLinkFromName(componentName);
|
||||||
|
if (link) {
|
||||||
|
// cool, we found the right link for this component name
|
||||||
|
return this.getNavLinkComponent(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
// umm, idk
|
||||||
|
return Promise.reject(`invalid link: ${componentName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getNavLinkComponent(link: NavLink) {
|
||||||
|
if (link.component) {
|
||||||
|
// sweet, we're already got a component loaded for this link
|
||||||
|
return Promise.resolve(link.component);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link.loadChildren) {
|
||||||
|
// awesome, looks like we'll lazy load this component
|
||||||
|
// using loadChildren as the URL to request
|
||||||
|
return this._moduleLoader.load(link.loadChildren).then(loadedModule => {
|
||||||
|
// kerpow!! we just lazy loaded a component!!
|
||||||
|
// update the existing link with the loaded component
|
||||||
|
link.component = loadedModule.component;
|
||||||
|
this._cfrMap.set(link.component, loadedModule.componentFactoryResolver);
|
||||||
|
return link.component;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(`invalid link component: ${link.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getComponentFromName(componentName: any): any {
|
resolveComponent(component: any): ComponentFactory<any> {
|
||||||
const segment = this._serializer.createSegmentFromName(componentName);
|
let cfr = this._cfrMap.get(component);
|
||||||
if (segment && segment.component) {
|
if (!cfr) {
|
||||||
return segment.component;
|
cfr = this._baseCfr;
|
||||||
}
|
}
|
||||||
return null;
|
return cfr.resolveComponentFactory(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,7 +308,7 @@ export class DeepLinker {
|
|||||||
// create a segment out of just the passed in name
|
// create a segment out of just the passed in name
|
||||||
const segment = this._serializer.createSegmentFromName(nameOrComponent);
|
const segment = this._serializer.createSegmentFromName(nameOrComponent);
|
||||||
if (segment) {
|
if (segment) {
|
||||||
const path = this.pathFromNavs(nav, segment.component, data);
|
const path = this._pathFromNavs(nav, segment.component, data);
|
||||||
// serialize the segments into a browser URL
|
// serialize the segments into a browser URL
|
||||||
// and prepare the URL with the location and return
|
// and prepare the URL with the location and return
|
||||||
const url = this._serializer.serialize(path);
|
const url = this._serializer.serialize(path);
|
||||||
@ -284,7 +324,7 @@ export class DeepLinker {
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
pathFromNavs(nav: NavController, component?: any, data?: any): NavSegment[] {
|
_pathFromNavs(nav: NavController, component?: any, data?: any): NavSegment[] {
|
||||||
const segments: NavSegment[] = [];
|
const segments: NavSegment[] = [];
|
||||||
let view: ViewController;
|
let view: ViewController;
|
||||||
let segment: NavSegment;
|
let segment: NavSegment;
|
||||||
@ -321,7 +361,7 @@ export class DeepLinker {
|
|||||||
if (isTab(nav)) {
|
if (isTab(nav)) {
|
||||||
// this nav is a Tab, which is a child of Tabs
|
// this nav is a Tab, which is a child of Tabs
|
||||||
// add a segment to represent which Tab is the selected one
|
// add a segment to represent which Tab is the selected one
|
||||||
tabSelector = this.getTabSelector(<any>nav);
|
tabSelector = this._getTabSelector(<any>nav);
|
||||||
segments.push({
|
segments.push({
|
||||||
id: tabSelector,
|
id: tabSelector,
|
||||||
name: tabSelector,
|
name: tabSelector,
|
||||||
@ -347,7 +387,7 @@ export class DeepLinker {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getTabSelector(tab: Tab): string {
|
_getTabSelector(tab: Tab): string {
|
||||||
if (isPresent(tab.tabUrlPath)) {
|
if (isPresent(tab.tabUrlPath)) {
|
||||||
return tab.tabUrlPath;
|
return tab.tabUrlPath;
|
||||||
}
|
}
|
||||||
@ -385,7 +425,7 @@ export class DeepLinker {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
initNav(nav: any): NavSegment {
|
initNav(nav: any): NavSegment {
|
||||||
const path = this.segments;
|
const path = this._segments;
|
||||||
|
|
||||||
if (nav && path.length) {
|
if (nav && path.length) {
|
||||||
if (!nav.parent) {
|
if (!nav.parent) {
|
||||||
@ -408,22 +448,18 @@ export class DeepLinker {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
initViews(segment: NavSegment): ViewController[] {
|
initViews(segment: NavSegment) {
|
||||||
let views: ViewController[];
|
|
||||||
|
|
||||||
if (isArray(segment.defaultHistory)) {
|
|
||||||
views = convertToViews(this, segment.defaultHistory);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
views = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const view = new ViewController(segment.component, segment.data);
|
const view = new ViewController(segment.component, segment.data);
|
||||||
view.id = segment.id;
|
view.id = segment.id;
|
||||||
|
|
||||||
views.push(view);
|
if (isArray(segment.defaultHistory)) {
|
||||||
|
return convertToViews(this, segment.defaultHistory).then(views => {
|
||||||
|
views.push(view);
|
||||||
|
return views;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return views;
|
return Promise.resolve([view]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -436,13 +472,13 @@ export class DeepLinker {
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
loadNavFromPath(nav: NavController, done?: Function) {
|
_loadNavFromPath(nav: NavController, done?: Function) {
|
||||||
if (!nav) {
|
if (!nav) {
|
||||||
done && done();
|
done && done();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.loadViewFromSegment(nav, () => {
|
this._loadViewFromSegment(nav, () => {
|
||||||
this.loadNavFromPath(nav.getActiveChildNav(), done);
|
this._loadNavFromPath(nav.getActiveChildNav(), done);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,7 +486,7 @@ export class DeepLinker {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
loadViewFromSegment(navInstance: any, done: Function) {
|
_loadViewFromSegment(navInstance: any, done: Function) {
|
||||||
// load up which nav ids belong to its nav segment
|
// load up which nav ids belong to its nav segment
|
||||||
let segment = this.initNav(navInstance);
|
let segment = this.initNav(navInstance);
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
@ -509,25 +545,25 @@ export class DeepLinker {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
isBackUrl(browserUrl: string) {
|
_isBackUrl(browserUrl: string) {
|
||||||
return (browserUrl === this.history[this.history.length - 2]);
|
return (browserUrl === this._history[this._history.length - 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
isCurrentUrl(browserUrl: string) {
|
_isCurrentUrl(browserUrl: string) {
|
||||||
return (browserUrl === this.history[this.history.length - 1]);
|
return (browserUrl === this._history[this._history.length - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
historyPush(browserUrl: string) {
|
_historyPush(browserUrl: string) {
|
||||||
if (!this.isCurrentUrl(browserUrl)) {
|
if (!this._isCurrentUrl(browserUrl)) {
|
||||||
this.history.push(browserUrl);
|
this._history.push(browserUrl);
|
||||||
if (this.history.length > 30) {
|
if (this._history.length > 30) {
|
||||||
this.history.shift();
|
this._history.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,18 +571,18 @@ export class DeepLinker {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
historyPop() {
|
_historyPop() {
|
||||||
this.history.pop();
|
this._history.pop();
|
||||||
if (!this.history.length) {
|
if (!this._history.length) {
|
||||||
this.historyPush(this._location.path());
|
this._historyPush(this._location.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function setupDeepLinker(app: App, serializer: UrlSerializer, location: Location) {
|
export function setupDeepLinker(app: App, serializer: UrlSerializer, location: Location, moduleLoader: ModuleLoader, cfr: ComponentFactoryResolver) {
|
||||||
const deepLinker = new DeepLinker(app, serializer, location);
|
const deepLinker = new DeepLinker(app, serializer, location, moduleLoader, cfr);
|
||||||
deepLinker.init();
|
deepLinker.init();
|
||||||
return deepLinker;
|
return deepLinker;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { AnimationOptions } from '../animations/animation';
|
|||||||
import { App } from '../components/app/app';
|
import { App } from '../components/app/app';
|
||||||
import { Config } from '../config/config';
|
import { Config } from '../config/config';
|
||||||
import { convertToView, convertToViews, NavOptions, DIRECTION_BACK, DIRECTION_FORWARD, INIT_ZINDEX,
|
import { convertToView, convertToViews, NavOptions, DIRECTION_BACK, DIRECTION_FORWARD, INIT_ZINDEX,
|
||||||
TransitionResolveFn, TransitionInstruction, ViewState } from './nav-util';
|
TransitionResolveFn, TransitionInstruction, STATE_INITIALIZED, STATE_LOADED, STATE_PRE_RENDERED } from './nav-util';
|
||||||
import { setZIndex } from './nav-util';
|
import { setZIndex } from './nav-util';
|
||||||
import { DeepLinker } from './deep-linker';
|
import { DeepLinker } from './deep-linker';
|
||||||
import { DomController } from '../platform/dom-controller';
|
import { DomController } from '../platform/dom-controller';
|
||||||
@ -72,27 +72,36 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
push(page: any, params?: any, opts?: NavOptions, done?: Function): Promise<any> {
|
push(page: any, params?: any, opts?: NavOptions, done?: Function): Promise<any> {
|
||||||
return this._queueTrns({
|
return convertToView(this._linker, page, params).then(viewController => {
|
||||||
insertStart: -1,
|
return this._queueTrns({
|
||||||
insertViews: [convertToView(this._linker, page, params)],
|
insertStart: -1,
|
||||||
opts: opts,
|
insertViews: [viewController],
|
||||||
}, done);
|
opts: opts,
|
||||||
|
}, done);
|
||||||
|
}).catch((err: Error) => {
|
||||||
|
console.error('Failed to navigate: ', err.message);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, done?: Function): Promise<any> {
|
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, done?: Function): Promise<any> {
|
||||||
return this._queueTrns({
|
return convertToView(this._linker, page, params).then(viewController => {
|
||||||
insertStart: insertIndex,
|
return this._queueTrns({
|
||||||
insertViews: [convertToView(this._linker, page, params)],
|
insertStart: insertIndex,
|
||||||
opts: opts,
|
insertViews: [viewController],
|
||||||
}, done);
|
opts: opts,
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: Function): Promise<any> {
|
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: Function): Promise<any> {
|
||||||
return this._queueTrns({
|
return convertToViews(this._linker, insertPages).then(viewControllers => {
|
||||||
insertStart: insertIndex,
|
return this._queueTrns({
|
||||||
insertViews: convertToViews(this._linker, insertPages),
|
insertStart: insertIndex,
|
||||||
opts: opts,
|
insertViews: viewControllers,
|
||||||
}, done);
|
opts: opts,
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pop(opts?: NavOptions, done?: Function): Promise<any> {
|
pop(opts?: NavOptions, done?: Function): Promise<any> {
|
||||||
@ -152,13 +161,15 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: Function): Promise<any> {
|
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: Function): Promise<any> {
|
||||||
const viewControllers = [convertToView(this._linker, pageOrViewCtrl, params)];
|
return convertToView(this._linker, pageOrViewCtrl, params).then((viewController) => {
|
||||||
return this._setPages(viewControllers, opts, done);
|
return this._setPages([viewController], opts, done);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setPages(pages: any[], opts?: NavOptions, done?: Function): Promise<any> {
|
setPages(pages: any[], opts?: NavOptions, done?: Function): Promise<any> {
|
||||||
const viewControllers = convertToViews(this._linker, pages);
|
return convertToViews(this._linker, pages).then(viewControllers => {
|
||||||
return this._setPages(viewControllers, opts, done);
|
return this._setPages(viewControllers, opts, done);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_setPages(viewControllers: ViewController[], opts?: NavOptions, done?: Function): Promise<any> {
|
_setPages(viewControllers: ViewController[], opts?: NavOptions, done?: Function): Promise<any> {
|
||||||
@ -220,7 +231,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
this._queue.length = 0;
|
this._queue.length = 0;
|
||||||
|
|
||||||
while (trns) {
|
while (trns) {
|
||||||
if (trns.enteringView && (trns.enteringView._state !== ViewState.LOADED)) {
|
if (trns.enteringView && (trns.enteringView._state !== STATE_LOADED)) {
|
||||||
// destroy the entering views and all of their hopes and dreams
|
// destroy the entering views and all of their hopes and dreams
|
||||||
this._destroyView(trns.enteringView);
|
this._destroyView(trns.enteringView);
|
||||||
}
|
}
|
||||||
@ -483,17 +494,17 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
{ provide: ViewController, useValue: enteringView },
|
{ provide: ViewController, useValue: enteringView },
|
||||||
{ provide: NavParams, useValue: enteringView.getNavParams() }
|
{ provide: NavParams, useValue: enteringView.getNavParams() }
|
||||||
]);
|
]);
|
||||||
const componentFactory = this._cfr.resolveComponentFactory(enteringView.component);
|
const componentFactory = this._linker.resolveComponent(enteringView.component);
|
||||||
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, this._viewport.parentInjector);
|
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, this._viewport.parentInjector);
|
||||||
|
|
||||||
// create ComponentRef and set it to the entering view
|
// create ComponentRef and set it to the entering view
|
||||||
enteringView.init(componentFactory.create(childInjector, []));
|
enteringView.init(componentFactory.create(childInjector, []));
|
||||||
enteringView._state = ViewState.INITIALIZED;
|
enteringView._state = STATE_INITIALIZED;
|
||||||
this._preLoad(enteringView);
|
this._preLoad(enteringView);
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewAttachToDOM(view: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
_viewAttachToDOM(view: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
||||||
assert(view._state === ViewState.INITIALIZED, 'view state must be INITIALIZED');
|
assert(view._state === STATE_INITIALIZED, 'view state must be INITIALIZED');
|
||||||
|
|
||||||
// fire willLoad before change detection runs
|
// fire willLoad before change detection runs
|
||||||
this._willLoad(view);
|
this._willLoad(view);
|
||||||
@ -501,7 +512,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
// render the component ref instance to the DOM
|
// render the component ref instance to the DOM
|
||||||
// ******** DOM WRITE ****************
|
// ******** DOM WRITE ****************
|
||||||
viewport.insert(componentRef.hostView, viewport.length);
|
viewport.insert(componentRef.hostView, viewport.length);
|
||||||
view._state = ViewState.PRE_RENDERED;
|
view._state = STATE_PRE_RENDERED;
|
||||||
|
|
||||||
if (view._cssClass) {
|
if (view._cssClass) {
|
||||||
// the ElementRef of the actual ion-page created
|
// the ElementRef of the actual ion-page created
|
||||||
@ -604,7 +615,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (enteringView && enteringView._state === ViewState.INITIALIZED) {
|
if (enteringView && enteringView._state === STATE_INITIALIZED) {
|
||||||
// render the entering component in the DOM
|
// render the entering component in the DOM
|
||||||
// this would also render new child navs/views
|
// this would also render new child navs/views
|
||||||
// which may have their very own async canEnter/Leave tests
|
// which may have their very own async canEnter/Leave tests
|
||||||
|
@ -7,33 +7,38 @@ import { NavControllerBase } from './nav-controller-base';
|
|||||||
import { Transition } from '../transitions/transition';
|
import { Transition } from '../transitions/transition';
|
||||||
|
|
||||||
|
|
||||||
export function getComponent(linker: DeepLinker, nameOrPageOrView: any): any {
|
export function getComponent(linker: DeepLinker, nameOrPageOrView: any, params?: any) {
|
||||||
if (typeof nameOrPageOrView === 'function') {
|
if (typeof nameOrPageOrView === 'function') {
|
||||||
return nameOrPageOrView;
|
return Promise.resolve(
|
||||||
|
new ViewController(nameOrPageOrView, params)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof nameOrPageOrView === 'string') {
|
if (typeof nameOrPageOrView === 'string') {
|
||||||
return linker.getComponentFromName(nameOrPageOrView);
|
return linker.getComponentFromName(nameOrPageOrView).then((component) => {
|
||||||
|
return new ViewController(component, params);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToView(linker: DeepLinker, nameOrPageOrView: any, params: any): ViewController {
|
export function convertToView(linker: DeepLinker, nameOrPageOrView: any, params: any) {
|
||||||
if (nameOrPageOrView) {
|
if (nameOrPageOrView) {
|
||||||
if (isViewController(nameOrPageOrView)) {
|
if (isViewController(nameOrPageOrView)) {
|
||||||
// is already a ViewController
|
// is already a ViewController
|
||||||
return nameOrPageOrView;
|
return Promise.resolve(<ViewController>nameOrPageOrView);
|
||||||
}
|
|
||||||
let component = getComponent(linker, nameOrPageOrView);
|
|
||||||
if (component) {
|
|
||||||
return new ViewController(component, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return getComponent(linker, nameOrPageOrView, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(`invalid page component: ${nameOrPageOrView}`);
|
console.error(`invalid page component: ${nameOrPageOrView}`);
|
||||||
return null;
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToViews(linker: DeepLinker, pages: any[]): ViewController[] {
|
export function convertToViews(linker: DeepLinker, pages: any[]) {
|
||||||
const views: ViewController[] = [];
|
const views: Promise<ViewController>[] = [];
|
||||||
if (isArray(pages)) {
|
if (isArray(pages)) {
|
||||||
for (var i = 0; i < pages.length; i++) {
|
for (var i = 0; i < pages.length; i++) {
|
||||||
var page = pages[i];
|
var page = pages[i];
|
||||||
@ -50,7 +55,7 @@ export function convertToViews(linker: DeepLinker, pages: any[]): ViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return views;
|
return Promise.all(views);
|
||||||
}
|
}
|
||||||
|
|
||||||
let portalZindex = 9999;
|
let portalZindex = 9999;
|
||||||
@ -98,19 +103,21 @@ export function isNav(nav: any): boolean {
|
|||||||
|
|
||||||
// public link interface
|
// public link interface
|
||||||
export interface DeepLinkMetadataType {
|
export interface DeepLinkMetadataType {
|
||||||
name: string;
|
name?: string;
|
||||||
segment?: string;
|
segment?: string;
|
||||||
defaultHistory?: any[];
|
defaultHistory?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export class DeepLinkMetadata implements DeepLinkMetadataType {
|
export class DeepLinkMetadata implements DeepLinkMetadataType {
|
||||||
component: any;
|
component?: any;
|
||||||
name: string;
|
viewFactoryFunction?: string;
|
||||||
|
loadChildren?: string;
|
||||||
|
name?: string;
|
||||||
segment?: string;
|
segment?: string;
|
||||||
defaultHistory?: any[];
|
defaultHistory?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeepLinkDecorator extends TypeDecorator {}
|
export interface DeepLinkDecorator extends TypeDecorator {}
|
||||||
@ -134,7 +141,8 @@ export interface DeepLinkConfig {
|
|||||||
|
|
||||||
// internal link interface, not exposed publicly
|
// internal link interface, not exposed publicly
|
||||||
export interface NavLink {
|
export interface NavLink {
|
||||||
component: any;
|
component?: any;
|
||||||
|
loadChildren?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
segment?: string;
|
segment?: string;
|
||||||
parts?: string[];
|
parts?: string[];
|
||||||
@ -148,7 +156,8 @@ export interface NavLink {
|
|||||||
export interface NavSegment {
|
export interface NavSegment {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
component: any;
|
component?: any;
|
||||||
|
loadChildren?: string;
|
||||||
data: any;
|
data: any;
|
||||||
navId?: string;
|
navId?: string;
|
||||||
defaultHistory?: NavSegment[];
|
defaultHistory?: NavSegment[];
|
||||||
@ -192,11 +201,10 @@ export interface TransitionInstruction {
|
|||||||
requiresTransition?: boolean;
|
requiresTransition?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ViewState {
|
|
||||||
INITIALIZED,
|
export const STATE_INITIALIZED = 1;
|
||||||
PRE_RENDERED,
|
export const STATE_PRE_RENDERED = 2;
|
||||||
LOADED,
|
export const STATE_LOADED = 3;
|
||||||
}
|
|
||||||
|
|
||||||
export const INIT_ZINDEX = 100;
|
export const INIT_ZINDEX = 100;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { swipeShouldReset } from '../util/util';
|
import { swipeShouldReset } from '../util/util';
|
||||||
import { DomController } from '../platform/dom-controller';
|
import { DomController } from '../platform/dom-controller';
|
||||||
import { GestureController, GesturePriority, GESTURE_GO_BACK_SWIPE } from '../gestures/gesture-controller';
|
import { GestureController, GESTURE_PRIORITY_GO_BACK_SWIPE, GESTURE_GO_BACK_SWIPE } from '../gestures/gesture-controller';
|
||||||
import { NavControllerBase } from './nav-controller-base';
|
import { NavControllerBase } from './nav-controller-base';
|
||||||
import { Platform } from '../platform/platform';
|
import { Platform } from '../platform/platform';
|
||||||
import { SlideData } from '../gestures/slide-gesture';
|
import { SlideData } from '../gestures/slide-gesture';
|
||||||
@ -26,7 +26,7 @@ export class SwipeBackGesture extends SlideEdgeGesture {
|
|||||||
domController: domCtrl,
|
domController: domCtrl,
|
||||||
gesture: gestureCtlr.createGesture({
|
gesture: gestureCtlr.createGesture({
|
||||||
name: GESTURE_GO_BACK_SWIPE,
|
name: GESTURE_GO_BACK_SWIPE,
|
||||||
priority: GesturePriority.GoBackSwipe,
|
priority: GESTURE_PRIORITY_GO_BACK_SWIPE,
|
||||||
disableScroll: true
|
disableScroll: true
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -35,21 +35,25 @@ export class UrlSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createSegmentFromName(nameOrComponent: any): NavSegment {
|
createSegmentFromName(nameOrComponent: any): NavSegment {
|
||||||
const configLink = this.links.find((link: NavLink) => {
|
const configLink = this.getLinkFromName(nameOrComponent);
|
||||||
return (link.component === nameOrComponent) ||
|
|
||||||
(link.name === nameOrComponent) ||
|
|
||||||
(link.component.name === nameOrComponent);
|
|
||||||
});
|
|
||||||
|
|
||||||
return configLink ? {
|
return configLink ? {
|
||||||
id: configLink.name,
|
id: configLink.name,
|
||||||
name: configLink.name,
|
name: configLink.name,
|
||||||
component: configLink.component,
|
component: configLink.component,
|
||||||
|
loadChildren: configLink.loadChildren,
|
||||||
data: null,
|
data: null,
|
||||||
defaultHistory: configLink.defaultHistory
|
defaultHistory: configLink.defaultHistory
|
||||||
} : null;
|
} : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLinkFromName(nameOrComponent: any) {
|
||||||
|
return this.links.find(link => {
|
||||||
|
return (link.component === nameOrComponent) ||
|
||||||
|
(link.name === nameOrComponent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize a path, which is made up of multiple NavSegments,
|
* Serialize a path, which is made up of multiple NavSegments,
|
||||||
* into a URL string. Turn each segment into a string and concat them to a URL.
|
* into a URL string. Turn each segment into a string and concat them to a URL.
|
||||||
@ -65,13 +69,14 @@ export class UrlSerializer {
|
|||||||
if (component) {
|
if (component) {
|
||||||
const link = findLinkByComponentData(this.links, component, data);
|
const link = findLinkByComponentData(this.links, component, data);
|
||||||
if (link) {
|
if (link) {
|
||||||
return this.createSegment(link, data);
|
return this._createSegment(link, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
createSegment(configLink: NavLink, data: any): NavSegment {
|
/** @internal */
|
||||||
|
_createSegment(configLink: NavLink, data: any): NavSegment {
|
||||||
let urlParts = configLink.parts;
|
let urlParts = configLink.parts;
|
||||||
|
|
||||||
if (isPresent(data)) {
|
if (isPresent(data)) {
|
||||||
@ -101,6 +106,7 @@ export class UrlSerializer {
|
|||||||
id: urlParts.join('/'),
|
id: urlParts.join('/'),
|
||||||
name: configLink.name,
|
name: configLink.name,
|
||||||
component: configLink.component,
|
component: configLink.component,
|
||||||
|
loadChildren: configLink.loadChildren,
|
||||||
data: data,
|
data: data,
|
||||||
defaultHistory: configLink.defaultHistory
|
defaultHistory: configLink.defaultHistory
|
||||||
};
|
};
|
||||||
@ -151,6 +157,7 @@ export const parseUrlParts = (urlParts: string[], configLinks: NavLink[]): NavSe
|
|||||||
id: urlParts[i],
|
id: urlParts[i],
|
||||||
name: urlParts[i],
|
name: urlParts[i],
|
||||||
component: null,
|
component: null,
|
||||||
|
loadChildren: null,
|
||||||
data: null
|
data: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -181,6 +188,7 @@ export const fillMatchedUrlParts = (segments: NavSegment[], urlParts: string[],
|
|||||||
id: matchedUrlParts.join('/'),
|
id: matchedUrlParts.join('/'),
|
||||||
name: configLink.name,
|
name: configLink.name,
|
||||||
component: configLink.component,
|
component: configLink.component,
|
||||||
|
loadChildren: configLink.loadChildren,
|
||||||
data: createMatchedData(matchedUrlParts, configLink),
|
data: createMatchedData(matchedUrlParts, configLink),
|
||||||
defaultHistory: configLink.defaultHistory
|
defaultHistory: configLink.defaultHistory
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { ComponentRef, ElementRef, EventEmitter, Output, Renderer } from '@angular/core';
|
import { ComponentRef, ElementRef, EventEmitter, Output, Renderer } from '@angular/core';
|
||||||
|
|
||||||
import { Footer, Header } from '../components/toolbar/toolbar';
|
import { Footer } from '../components/toolbar/toolbar-footer';
|
||||||
|
import { Header } from '../components/toolbar/toolbar-header';
|
||||||
import { isPresent } from '../util/util';
|
import { isPresent } from '../util/util';
|
||||||
import { Navbar } from '../components/navbar/navbar';
|
import { Navbar } from '../components/navbar/navbar';
|
||||||
import { NavController } from './nav-controller';
|
import { NavController } from './nav-controller';
|
||||||
import { NavOptions, ViewState } from './nav-util';
|
import { NavOptions } from './nav-util';
|
||||||
import { NavParams } from './nav-params';
|
import { NavParams } from './nav-params';
|
||||||
import { Content } from '../components/content/content';
|
import { Content } from '../components/content/content';
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ export class ViewController {
|
|||||||
_cmp: ComponentRef<any>;
|
_cmp: ComponentRef<any>;
|
||||||
_nav: NavController;
|
_nav: NavController;
|
||||||
_zIndex: number;
|
_zIndex: number;
|
||||||
_state: ViewState;
|
_state: number;
|
||||||
_cssClass: string;
|
_cssClass: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user