fix(router): fixes navChanged()

This commit is contained in:
Manu Mtz.-Almeida
2018-03-26 22:07:00 +02:00
parent 076b7e55bd
commit dddaee1719
17 changed files with 120 additions and 111 deletions

View File

@ -95,6 +95,7 @@ import {
} from './components/nav/view-controller'; } from './components/nav/view-controller';
import { import {
RouteID, RouteID,
RouterDirection,
RouterEventDetail, RouterEventDetail,
RouteWrite, RouteWrite,
} from './components/router/utils/interfaces'; } from './components/router/utils/interfaces';
@ -3502,7 +3503,6 @@ declare global {
'canGoBack': (view?: ViewController) => boolean; 'canGoBack': (view?: ViewController) => boolean;
'delegate': FrameworkDelegate; 'delegate': FrameworkDelegate;
'getActive': () => ViewController; 'getActive': () => ViewController;
'getAllChildNavs': () => any[];
'getByIndex': (index: number) => ViewController; 'getByIndex': (index: number) => ViewController;
'getPrevious': (view?: ViewController) => ViewController; 'getPrevious': (view?: ViewController) => ViewController;
'getRouteId': () => RouteID; 'getRouteId': () => RouteID;
@ -3546,7 +3546,7 @@ declare global {
export interface IonNavAttributes extends HTMLAttributes { export interface IonNavAttributes extends HTMLAttributes {
'animated'?: boolean; 'animated'?: boolean;
'delegate'?: FrameworkDelegate; 'delegate'?: FrameworkDelegate;
'onIonNavChanged'?: (event: CustomEvent) => void; 'onIonNavChanged'?: (event: CustomEvent<void>) => void;
'root'?: any; 'root'?: any;
'rootParams'?: any; 'rootParams'?: any;
'swipeBackEnabled'?: boolean; 'swipeBackEnabled'?: boolean;
@ -4733,8 +4733,8 @@ declare global {
declare global { declare global {
interface HTMLIonRouterElement extends HTMLStencilElement { interface HTMLIonRouterElement extends HTMLStencilElement {
'base': string; 'base': string;
'navChanged': (isPop: boolean) => Promise<boolean>; 'navChanged': (direction: RouterDirection) => Promise<boolean>;
'push': (url: string, backDirection?: boolean) => Promise<boolean>; 'push': (url: string, direction?: RouterDirection) => Promise<boolean>;
'useHash': boolean; 'useHash': boolean;
} }
var HTMLIonRouterElement: { var HTMLIonRouterElement: {
@ -5977,7 +5977,7 @@ declare global {
* Emitted when the tab changes. * Emitted when the tab changes.
*/ */
'onIonChange'?: (event: CustomEvent) => void; 'onIonChange'?: (event: CustomEvent) => void;
'onIonNavChanged'?: (event: CustomEvent<any>) => void; 'onIonNavChanged'?: (event: CustomEvent<void>) => void;
'scrollable'?: boolean; 'scrollable'?: boolean;
/** /**
* If true, the tabbar * If true, the tabbar

View File

@ -1,6 +1,7 @@
import { Component, Element, Prop } from '@stencil/core'; import { Component, Element, Prop } from '@stencil/core';
import { Config } from '../../index'; import { Config } from '../../index';
import { openURL } from '../../utils/theme'; import { openURL } from '../../utils/theme';
import { RouterDirection } from '../router/utils/interfaces';
@Component({ @Component({
tag: 'ion-back-button', tag: 'ion-back-button',
@ -41,7 +42,7 @@ export class BackButton {
ev.preventDefault(); ev.preventDefault();
nav.pop(); nav.pop();
} else if (this.defaultHref) { } else if (this.defaultHref) {
openURL(this.defaultHref, ev, true); openURL(this.defaultHref, ev, RouterDirection.Back);
} }
} }

View File

@ -38,8 +38,4 @@ export class HideWhen implements DisplayWhen {
} }
}; };
} }
} }

View File

@ -34,8 +34,8 @@ export const enum ViewState {
} }
export const enum NavDirection { export const enum NavDirection {
back = 'back', Back = 'back',
forward = 'forward' Forward = 'forward'
} }
export type NavParams = {[key: string]: any}; export type NavParams = {[key: string]: any};
@ -58,6 +58,7 @@ export interface NavOptions {
keyboardClose?: boolean; keyboardClose?: boolean;
progressAnimation?: boolean; progressAnimation?: boolean;
ev?: any; ev?: any;
updateURL?: boolean;
delegate?: FrameworkDelegate; delegate?: FrameworkDelegate;
viewIsReady?: () => Promise<any>; viewIsReady?: () => Promise<any>;
} }

View File

@ -13,7 +13,7 @@ import {
import { ViewController, isViewController } from './view-controller'; import { ViewController, isViewController } from './view-controller';
import { Animation, Config, DomController, FrameworkDelegate, GestureDetail, NavOutlet } from '../..'; import { Animation, Config, DomController, FrameworkDelegate, GestureDetail, NavOutlet } from '../..';
import { RouteID, RouteWrite } from '../router/utils/interfaces'; import { RouteID, RouteWrite, RouterDirection } from '../router/utils/interfaces';
import { AnimationOptions, ViewLifecycle, lifecycle, transition } from '../../utils/transition'; import { AnimationOptions, ViewLifecycle, lifecycle, transition } from '../../utils/transition';
import { assert } from '../../utils/helpers'; import { assert } from '../../utils/helpers';
@ -25,7 +25,6 @@ import mdTransitionAnimation from './animations/md.transition';
}) })
export class NavControllerBase implements NavOutlet { export class NavControllerBase implements NavOutlet {
private _children: NavControllerBase[] = [];
private _ids = -1; private _ids = -1;
private _init = false; private _init = false;
private _queue: TransitionInstruction[] = []; private _queue: TransitionInstruction[] = [];
@ -64,7 +63,7 @@ export class NavControllerBase implements NavOutlet {
} }
} }
@Event() ionNavChanged: EventEmitter; @Event() ionNavChanged: EventEmitter<void>;
componentWillLoad() { componentWillLoad() {
this.id = 'n' + (++ctrlIds); this.id = 'n' + (++ctrlIds);
@ -209,27 +208,31 @@ export class NavControllerBase implements NavOutlet {
let resolve: (result: RouteWrite) => void; let resolve: (result: RouteWrite) => void;
const promise = new Promise<RouteWrite>((r) => resolve = r); const promise = new Promise<RouteWrite>((r) => resolve = r);
let finish: Promise<boolean>;
const commonOpts: NavOptions = { const commonOpts: NavOptions = {
updateURL: false,
viewIsReady: () => { viewIsReady: () => {
let markVisible; let mark: Function;
const p = new Promise(r => markVisible = r); const p = new Promise(r => mark = r);
resolve({ resolve({
changed: true, changed: true,
element: this.getActive().element, element: this.getActive().element,
markVisible markVisible: async () => {
mark();
await finish;
}
}); });
return p; return p;
} }
}; };
if (viewController) { if (viewController) {
this.popTo(viewController, {...commonOpts, direction: NavDirection.back}); finish = this.popTo(viewController, {...commonOpts, direction: NavDirection.Back});
} else if (direction === 1) { } else if (direction === 1) {
this.push(id, params, commonOpts); finish = this.push(id, params, commonOpts);
} else if (direction === -1) { } else if (direction === -1) {
this.setRoot(id, params, {...commonOpts, direction: NavDirection.back, animate: true}); finish = this.setRoot(id, params, {...commonOpts, direction: NavDirection.Back, animate: true});
} else { } else {
this.setRoot(id, params, commonOpts); finish = this.setRoot(id, params, commonOpts);
} }
return promise; return promise;
} }
@ -244,11 +247,6 @@ export class NavControllerBase implements NavOutlet {
} : undefined; } : undefined;
} }
@Method()
getAllChildNavs(): any[] {
return this._children.slice();
}
@Method() @Method()
canGoBack(view = this.getActive()): boolean { canGoBack(view = this.getActive()): boolean {
return !!(view && this.getPrevious(view)); return !!(view && this.getPrevious(view));
@ -331,13 +329,6 @@ export class NavControllerBase implements NavOutlet {
} }
this._init = true; this._init = true;
const isPop = result.direction === NavDirection.back;
if (this.useRouter) {
const router = document.querySelector('ion-router');
router && router.navChanged(isPop);
}
this.ionNavChanged.emit({isPop});
if (ti.done) { if (ti.done) {
ti.done( ti.done(
result.hasCompleted, result.hasCompleted,
@ -348,6 +339,18 @@ export class NavControllerBase implements NavOutlet {
); );
} }
ti.resolve(result.hasCompleted); ti.resolve(result.hasCompleted);
if (ti.opts.updateURL !== false && this.useRouter) {
const router = document.querySelector('ion-router');
if (router) {
const direction = (result.direction === NavDirection.Back)
? RouterDirection.Back
: RouterDirection.Forward;
router && router.navChanged(direction);
}
}
this.ionNavChanged.emit();
} }
private _failed(rejectReason: any, ti: TransitionInstruction) { private _failed(rejectReason: any, ti: TransitionInstruction) {
@ -528,7 +531,7 @@ export class NavControllerBase implements NavOutlet {
} }
} }
// default the direction to "back" // default the direction to "back"
opts.direction = opts.direction || NavDirection.back; opts.direction = opts.direction || NavDirection.Back;
} }
const finalBalance = this._views.length + (insertViews ? insertViews.length : 0) - (removeCount ? removeCount : 0); const finalBalance = this._views.length + (insertViews ? insertViews.length : 0) - (removeCount ? removeCount : 0);
@ -556,7 +559,7 @@ export class NavControllerBase implements NavOutlet {
if (ti.enteringRequiresTransition) { if (ti.enteringRequiresTransition) {
// default to forward if not already set // default to forward if not already set
opts.direction = opts.direction || NavDirection.forward; opts.direction = opts.direction || NavDirection.Forward;
} }
} }
@ -757,7 +760,7 @@ export class NavControllerBase implements NavOutlet {
// default the direction to "back"; // default the direction to "back";
const opts: NavOptions = { const opts: NavOptions = {
direction: NavDirection.back, direction: NavDirection.Back,
progressAnimation: true progressAnimation: true
}; };
@ -809,7 +812,6 @@ export class NavControllerBase implements NavOutlet {
canSwipeBack(): boolean { canSwipeBack(): boolean {
return ( return (
this.swipeBackEnabled && this.swipeBackEnabled &&
this._children.length === 0 &&
!this.isTransitioning && !this.isTransitioning &&
this.canGoBack() this.canGoBack()
); );

View File

@ -72,9 +72,6 @@ boolean
#### getActive() #### getActive()
#### getAllChildNavs()
#### getByIndex() #### getByIndex()

View File

@ -82,7 +82,7 @@ export class RouterOutlet implements NavOutlet {
async setRouteId(id: string, data: any, direction: number): Promise<RouteWrite> { async setRouteId(id: string, data: any, direction: number): Promise<RouteWrite> {
const changed = await this.setRoot(id, data, { const changed = await this.setRoot(id, data, {
duration: direction === 0 ? 0 : undefined, duration: direction === 0 ? 0 : undefined,
direction: direction === -1 ? NavDirection.back : NavDirection.forward, direction: direction === -1 ? NavDirection.Back : NavDirection.Forward,
}); });
return { return {
changed, changed,

View File

@ -1,9 +1,9 @@
import { Build, Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core'; import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
import { Config, DomController } from '../../index'; import { Config, DomController } from '../../index';
import { flattenRouterTree, readRedirects, readRoutes } from './utils/parser'; import { flattenRouterTree, readRedirects, readRoutes } from './utils/parser';
import { readNavState, writeNavState } from './utils/dom'; import { readNavState, writeNavState } from './utils/dom';
import { chainToPath, generatePath, parsePath, readPath, writePath } from './utils/path'; import { chainToPath, generatePath, parsePath, readPath, writePath } from './utils/path';
import { RouteChain, RouteRedirect, RouterEventDetail } from './utils/interfaces'; import { RouteChain, RouteRedirect, RouterDirection, RouterEventDetail } from './utils/interfaces';
import { routeRedirect, routerIDsToChain, routerPathToChain } from './utils/matching'; import { routeRedirect, routerIDsToChain, routerPathToChain } from './utils/matching';
import { printRoutes } from './utils/debug'; import { printRoutes } from './utils/debug';
@ -53,9 +53,7 @@ export class Router {
const tree = readRoutes(this.el); const tree = readRoutes(this.el);
this.routes = flattenRouterTree(tree); this.routes = flattenRouterTree(tree);
if (Build.isDev) {
printRoutes(this.routes); printRoutes(this.routes);
}
// schedule write // schedule write
if (this.timer) { if (this.timer) {
@ -75,17 +73,21 @@ export class Router {
this.state++; this.state++;
window.history.replaceState(this.state, document.title, document.location.href); window.history.replaceState(this.state, document.title, document.location.href);
} }
const direction = window.history.state >= this.state ? 1 : -1; const direction = window.history.state >= this.state
this.writeNavStateRoot(this.getPath(), direction); ? RouterDirection.Forward
: RouterDirection.Back;
const path = this.getPath();
console.debug('[ion-router] URL changed -> update nav', path, direction);
this.writeNavStateRoot(path, direction);
} }
@Method() @Method()
async navChanged(isPop: boolean): Promise<boolean> { async navChanged(direction: RouterDirection): Promise<boolean> {
if (this.busy) { if (this.busy) {
return false; return false;
} }
console.debug('[IN] nav changed -> update URL'); const { ids, outlet } = readNavState(document.body);
const { ids, pivot } = readNavState(document.body);
const chain = routerIDsToChain(ids, this.routes); const chain = routerIDsToChain(ids, this.routes);
if (!chain) { if (!chain) {
console.warn('no matching URL for ', ids.map(i => i.id)); console.warn('no matching URL for ', ids.map(i => i.id));
@ -93,31 +95,38 @@ export class Router {
} }
const path = chainToPath(chain); const path = chainToPath(chain);
this.setPath(path, isPop); if (!path) {
console.warn('router could not match path because some required param is missing');
return false;
}
if (chain.length > ids.length) { console.debug('[ion-router] nav changed -> update URL', ids, path);
await this.writeNavState(pivot, chain.slice(ids.length), 0); this.setPath(path, direction);
if (outlet) {
console.debug('[ion-router] updating nested outlet', outlet);
await this.writeNavState(outlet, chain, RouterDirection.None, ids.length);
} }
this.emitRouteChange(path, null); this.emitRouteChange(path, null);
return true; return true;
} }
@Method() @Method()
push(url: string, backDirection = false) { push(url: string, direction = RouterDirection.Forward) {
const path = parsePath(url); const path = parsePath(url);
this.setPath(path, backDirection); this.setPath(path, direction);
return this.writeNavStateRoot(path, backDirection ? -1 : 1); console.debug('[ion-router] URL pushed -> updating nav', url, direction);
return this.writeNavStateRoot(path, direction);
} }
private async writeNavStateRoot(path: string[], direction: number): Promise<boolean> { private async writeNavStateRoot(path: string[], direction: RouterDirection): Promise<boolean> {
if (this.busy) { if (this.busy) {
return false; return false;
} }
const redirect = routeRedirect(path, this.redirects); const redirect = routeRedirect(path, this.redirects);
let redirectFrom: string[] = null; let redirectFrom: string[] = null;
if (redirect) { if (redirect) {
this.setPath(redirect.to, true); this.setPath(redirect.to, direction);
redirectFrom = redirect.from; redirectFrom = redirect.from;
path = redirect.to; path = redirect.to;
} }
@ -129,24 +138,25 @@ export class Router {
return changed; return changed;
} }
private async writeNavState(node: any, chain: RouteChain, direction: number): Promise<boolean> { private async writeNavState(node: any, chain: RouteChain, direction: RouterDirection, index = 0): Promise<boolean> {
if (this.busy) { if (this.busy) {
return false; return false;
} }
try { try {
this.busy = true; this.busy = true;
const changed = await writeNavState(node, chain, 0, direction); const changed = await writeNavState(node, chain, direction, index);
this.busy = false; this.busy = false;
return changed; return changed;
} catch (e) { } catch (e) {
this.busy = false; this.busy = false;
throw e; console.error(e);
return false;
} }
} }
private setPath(path: string[], isPop: boolean) { private setPath(path: string[], direction: RouterDirection) {
this.state++; this.state++;
writePath(window.history, this.base, this.useHash, path, isPop, this.state); writePath(window.history, this.base, this.useHash, path, direction, this.state);
} }
private getPath(): string[] | null { private getPath(): string[] | null {
@ -154,6 +164,7 @@ export class Router {
} }
private emitRouteChange(path: string[], redirectPath: string[]|null) { private emitRouteChange(path: string[], redirectPath: string[]|null) {
console.debug('[ion-router] route changed', path);
const from = this.previousPath; const from = this.previousPath;
const redirectedFrom = redirectPath ? generatePath(redirectPath) : null; const redirectedFrom = redirectPath ? generatePath(redirectPath) : null;
const to = generatePath(path); const to = generatePath(path);

View File

@ -20,12 +20,12 @@ describe('readRoutes', () => {
r4.appendChild(r6); r4.appendChild(r6);
const expected: RouteTree = [ const expected: RouteTree = [
{ path: [''], id: 'main-page', children: [], params: {router: root} }, { path: [''], id: 'main-page', children: [], params: undefined },
{ path: ['one-page'], id: 'one-page', children: [], params: {router: root} }, { path: ['one-page'], id: 'one-page', children: [], params: undefined },
{ path: ['secondpage'], id: 'second-page', params: {router: root}, children: [ { path: ['secondpage'], id: 'second-page', params: undefined, children: [
{ path: ['5', 'hola'], id: '4', params: {router: root}, children: [ { path: ['5', 'hola'], id: '4', params: undefined, children: [
{ path: ['path', 'to', 'five'], id: '5', children: [], params: {router: root} }, { path: ['path', 'to', 'five'], id: '5', children: [], params: undefined },
{ path: ['path', 'to', 'five2'], id: '6', children: [], params: {router: root} } { path: ['path', 'to', 'five2'], id: '6', children: [], params: undefined }
] } ] }
] } ] }
]; ];

View File

@ -78,7 +78,7 @@ describe('chainToPath', () => {
{ id: '3', path: ['segment'], params: undefined }, { id: '3', path: ['segment'], params: undefined },
{ id: '8', path: [':name'], params: undefined }, { id: '8', path: [':name'], params: undefined },
]; ];
expect(() => chainToPath(chain)).toThrowError('missing param name'); expect(chainToPath(chain)).toBeNull();
}); });
it('should raise an exception 2', () => { it('should raise an exception 2', () => {
@ -86,7 +86,7 @@ describe('chainToPath', () => {
{ id: '3', path: ['segment'], params: undefined }, { id: '3', path: ['segment'], params: undefined },
{ id: '8', path: [':name', ':id'], params: {name: 'hey'} }, { id: '8', path: [':name', ':id'], params: {name: 'hey'} },
]; ];
expect(() => chainToPath(chain)).toThrowError('missing param id'); expect(chainToPath(chain)).toBeNull();
}); });
}); });

View File

@ -1,6 +1,6 @@
import { NavOutlet, NavOutletElement, RouteChain, RouteID } from './interfaces'; import { NavOutlet, NavOutletElement, RouteChain, RouteID, RouterDirection } from './interfaces';
export async function writeNavState(root: HTMLElement|undefined, chain: RouteChain|null, index: number, direction: number): Promise<boolean> { export async function writeNavState(root: HTMLElement|undefined, chain: RouteChain|null, direction: RouterDirection, index: number): Promise<boolean> {
// find next navigation outlet in the DOM // find next navigation outlet in the DOM
const outlet = searchNavNode(root); const outlet = searchNavNode(root);
@ -16,11 +16,11 @@ export async function writeNavState(root: HTMLElement|undefined, chain: RouteCha
// if the outlet changed the page, reset navigation to neutral (no direction) // if the outlet changed the page, reset navigation to neutral (no direction)
// this means nested outlets will not animate // this means nested outlets will not animate
if (result.changed) { if (result.changed) {
direction = 0; direction = RouterDirection.None;
} }
// recursivelly set nested outlets // recursivelly set nested outlets
const changed = await writeNavState(result.element, chain, index + 1, direction); const changed = await writeNavState(result.element, chain, direction, index + 1);
// once all nested outlets are visible let's make the parent visible too, // once all nested outlets are visible let's make the parent visible too,
// using markVisible prevents flickering // using markVisible prevents flickering
@ -30,14 +30,14 @@ export async function writeNavState(root: HTMLElement|undefined, chain: RouteCha
return changed; return changed;
} }
export function readNavState(root: HTMLElement) { export function readNavState(root: HTMLElement | null) {
const ids: RouteID[] = []; const ids: RouteID[] = [];
let pivot: NavOutlet|null; let outlet: NavOutlet|null;
let node: HTMLElement|undefined = root; let node: HTMLElement|null = root;
while (true) { while (true) {
pivot = searchNavNode(node); outlet = searchNavNode(node);
if (pivot) { if (outlet) {
const id = pivot.getRouteId(); const id = outlet.getRouteId();
if (id) { if (id) {
node = id.element; node = id.element;
id.element = undefined; id.element = undefined;
@ -49,7 +49,7 @@ export function readNavState(root: HTMLElement) {
break; break;
} }
} }
return {ids, pivot}; return {ids, outlet};
} }
const QUERY = ':not([no-router]) ion-nav,:not([no-router]) ion-tabs, :not([no-router]) ion-router-outlet'; const QUERY = ':not([no-router]) ion-nav,:not([no-router]) ion-tabs, :not([no-router]) ion-router-outlet';

View File

@ -10,6 +10,12 @@ export interface RouterEventDetail {
to: string; to: string;
} }
export const enum RouterDirection {
None = 0,
Forward = 1,
Back = -1,
}
export interface RouteRedirect { export interface RouteRedirect {
from: string[]; from: string[];
to: string[]|undefined; to: string[]|undefined;

View File

@ -1,6 +1,5 @@
import { RouteChain, RouteNode, RouteRedirect, RouteTree } from './interfaces'; import { RouteChain, RouteNode, RouteRedirect, RouteTree } from './interfaces';
import { parsePath } from './path'; import { parsePath } from './path';
import { mergeParams } from './matching';
export function readRedirects(root: Element): RouteRedirect[] { export function readRedirects(root: Element): RouteRedirect[] {
@ -16,16 +15,13 @@ export function readRedirects(root: Element): RouteRedirect[] {
} }
export function readRoutes(root: Element, node = root): RouteTree { export function readRoutes(root: Element, node = root): RouteTree {
const commonParams = {
router: root
};
return (Array.from(node.children) as HTMLIonRouteElement[]) return (Array.from(node.children) as HTMLIonRouteElement[])
.filter(el => el.tagName === 'ION-ROUTE' && el.component) .filter(el => el.tagName === 'ION-ROUTE' && el.component)
.map(el => { .map(el => {
return { return {
path: parsePath(readProp(el, 'url')), path: parsePath(readProp(el, 'url')),
id: readProp(el, 'component').toLowerCase(), id: readProp(el, 'component').toLowerCase(),
params: mergeParams(commonParams, el.componentProps), params: el.componentProps,
children: readRoutes(root, el) children: readRoutes(root, el)
}; };
}); });

View File

@ -1,4 +1,4 @@
import { RouteChain } from './interfaces'; import { RouteChain, RouterDirection } from './interfaces';
export function generatePath(segments: string[]): string { export function generatePath(segments: string[]): string {
const path = segments const path = segments
@ -8,14 +8,14 @@ export function generatePath(segments: string[]): string {
return '/' + path; return '/' + path;
} }
export function chainToPath(chain: RouteChain): string[] { export function chainToPath(chain: RouteChain): string[]|null {
const path = []; const path = [];
for (const route of chain) { for (const route of chain) {
for (const segment of route.path) { for (const segment of route.path) {
if (segment[0] === ':') { if (segment[0] === ':') {
const param = route.params && route.params[segment.slice(1)]; const param = route.params && route.params[segment.slice(1)];
if (!param) { if (!param) {
throw new Error(`missing param ${segment.slice(1)}`); return null;
} }
path.push(param); path.push(param);
} else if (segment !== '') { } else if (segment !== '') {
@ -26,17 +26,16 @@ export function chainToPath(chain: RouteChain): string[] {
return path; return path;
} }
export function writePath(history: History, base: string, usePath: boolean, path: string[], isPop: boolean, state: number) { export function writePath(history: History, base: string, usePath: boolean, path: string[], direction: RouterDirection, state: number) {
path = [base, ...path]; path = [base, ...path];
let url = generatePath(path); let url = generatePath(path);
if (usePath) { if (usePath) {
url = '#' + url; url = '#' + url;
} }
if (isPop) { if (direction === RouterDirection.Forward) {
// history.back();
history.replaceState(state, '', url);
} else {
history.pushState(state, '', url); history.pushState(state, '', url);
} else {
history.replaceState(state, '', url);
} }
} }

View File

@ -1,6 +1,6 @@
import { Build, Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core'; import { Build, Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
import { Config, NavOutlet } from '../../index'; import { Config, NavOutlet } from '../../index';
import { RouteID, RouteWrite } from '../router/utils/interfaces'; import { RouteID, RouteWrite, RouterDirection } from '../router/utils/interfaces';
@Component({ @Component({
@ -68,7 +68,7 @@ export class Tabs implements NavOutlet {
* Emitted when the tab changes. * Emitted when the tab changes.
*/ */
@Event() ionChange: EventEmitter; @Event() ionChange: EventEmitter;
@Event() ionNavChanged: EventEmitter<any>; @Event() ionNavChanged: EventEmitter<void>;
componentWillLoad() { componentWillLoad() {
this.useRouter = !!document.querySelector('ion-router') && !this.el.closest('[no-router]'); this.useRouter = !!document.querySelector('ion-router') && !this.el.closest('[no-router]');
@ -105,7 +105,8 @@ export class Tabs implements NavOutlet {
} }
await this.setActive(selectedTab); await this.setActive(selectedTab);
await this.notifyRouter(); await this.notifyRouter();
return this.tabSwitch(); await this.tabSwitch();
return true;
} }
@Method() @Method()
@ -119,7 +120,7 @@ export class Tabs implements NavOutlet {
return { return {
changed: true, changed: true,
element: this.selectedTab, element: this.selectedTab,
markVisible: () => { this.tabSwitch(); } markVisible: () => this.tabSwitch(),
}; };
} }
@ -220,14 +221,14 @@ export class Tabs implements NavOutlet {
return selectedTab.setActive(); return selectedTab.setActive();
} }
private tabSwitch(): boolean { private tabSwitch() {
const selectedTab = this.selectedTab; const selectedTab = this.selectedTab;
const leavingTab = this.leavingTab; const leavingTab = this.leavingTab;
this.leavingTab = undefined; this.leavingTab = undefined;
this.transitioning = false; this.transitioning = false;
if (!selectedTab) { if (!selectedTab) {
return false; return;
} }
selectedTab.selected = true; selectedTab.selected = true;
@ -236,17 +237,15 @@ export class Tabs implements NavOutlet {
leavingTab.active = false; leavingTab.active = false;
} }
this.ionChange.emit(selectedTab); this.ionChange.emit(selectedTab);
this.ionNavChanged.emit({isPop: false}); this.ionNavChanged.emit();
return true;
} }
return false;
} }
private notifyRouter() { private notifyRouter() {
if (this.useRouter) { if (this.useRouter) {
const router = document.querySelector('ion-router'); const router = document.querySelector('ion-router');
if (router) { if (router) {
return router.navChanged(false); return router.navChanged(RouterDirection.Forward);
} }
} }
return Promise.resolve(false); return Promise.resolve(false);

View File

@ -1,4 +1,5 @@
import { CssClassMap } from '../index'; import { CssClassMap } from '../index';
import { RouterDirection } from '../components/router/utils/interfaces';
/** /**
* Create the mode and color classes for the component based on the classes passed in * Create the mode and color classes for the component based on the classes passed in
@ -65,12 +66,12 @@ export function getClassMap(classes: string | undefined): CssClassMap {
return map; return map;
} }
export function openURL(url: string, ev: Event, isPop = false) { export function openURL(url: string, ev: Event, direction = RouterDirection.Forward) {
if (url && url[0] !== '#' && url.indexOf('://') === -1) { if (url && url[0] !== '#' && url.indexOf('://') === -1) {
const router = document.querySelector('ion-router'); const router = document.querySelector('ion-router');
if (router) { if (router) {
ev && ev.preventDefault(); ev && ev.preventDefault();
return router.componentOnReady().then(() => router.push(url, isPop)); return router.componentOnReady().then(() => router.push(url, direction));
} }
} }
return Promise.resolve(); return Promise.resolve();

View File

@ -121,7 +121,7 @@ function fireDidEvents(enteringEl: HTMLElement, leavingEl: HTMLElement) {
function setZIndex(enteringEl: HTMLElement, leavingEl: HTMLElement, direction: NavDirection) { function setZIndex(enteringEl: HTMLElement, leavingEl: HTMLElement, direction: NavDirection) {
if (enteringEl) { if (enteringEl) {
enteringEl.style.zIndex = (direction === NavDirection.back) enteringEl.style.zIndex = (direction === NavDirection.Back)
? '99' ? '99'
: '101'; : '101';
} }