fix(angular): tune routerLink default behaviour (#16405)

refactor: use ng7 features for direction detection
This commit is contained in:
Manu MA
2018-11-21 00:08:20 +01:00
committed by GitHub
parent 2884076834
commit 72bc025874
6 changed files with 68 additions and 95 deletions

View File

@ -1,20 +1,15 @@
import { Directive, ElementRef, HostListener, Input, Optional } from '@angular/core'; import { Directive, ElementRef, HostListener, Input, Optional } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NavController, NavIntent } from '../../providers/nav-controller'; import { NavController, NavDirection } from '../../providers/nav-controller';
export type RouterDirection = 'forward' | 'back' | 'root' | 'auto';
@Directive({ @Directive({
selector: '[routerDirection],ion-anchor,ion-button,ion-item' selector: '[routerLink],[routerDirection],ion-anchor,ion-button,ion-item'
}) })
export class HrefDelegate { export class HrefDelegate {
@Input() routerDirection: RouterDirection = 'forward'; @Input() routerLink: any;
@Input() routerDirection: NavDirection = 'forward';
@Input()
set routerLink(_: any) {
this.elementRef.nativeElement.button = true;
}
@Input() @Input()
set href(value: string) { set href(value: string) {
@ -33,19 +28,13 @@ export class HrefDelegate {
@HostListener('click', ['$event']) @HostListener('click', ['$event'])
onClick(ev: Event) { onClick(ev: Event) {
const url = this.href; const url = this.href;
if (this.router && url != null && url[0] !== '#' && url.indexOf('://') === -1) { if (this.routerDirection) {
this.navCtrl.setDirection(this.routerDirection);
}
if (!this.routerLink && this.router && url != null && url[0] !== '#' && url.indexOf('://') === -1) {
ev.preventDefault(); ev.preventDefault();
this.navCtrl.setIntent(textToIntent(this.routerDirection));
this.router.navigateByUrl(url); this.router.navigateByUrl(url);
} }
} }
} }
function textToIntent(direction: RouterDirection) {
switch (direction) {
case 'forward': return NavIntent.Forward;
case 'back': return NavIntent.Back;
case 'root': return NavIntent.Root;
default: return NavIntent.Auto;
}
}

View File

@ -1,6 +1,6 @@
import { Directive, ElementRef, HostListener, Input, Optional } from '@angular/core'; import { Directive, ElementRef, HostListener, Input, Optional } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NavController, NavIntent } from '../../providers/nav-controller'; import { NavController } from '../../providers/nav-controller';
import { IonRouterOutlet } from './ion-router-outlet'; import { IonRouterOutlet } from './ion-router-outlet';
@Directive({ @Directive({
@ -29,8 +29,7 @@ export class IonBackButton {
this.routerOutlet.pop(); this.routerOutlet.pop();
ev.preventDefault(); ev.preventDefault();
} else if (this.router && this.defaultHref != null) { } else if (this.router && this.defaultHref != null) {
this.navCtrl.setIntent(NavIntent.Back); this.navCtrl.navigateBack(this.defaultHref);
this.router.navigateByUrl(this.defaultHref);
ev.preventDefault(); ev.preventDefault();
} }
} }

View File

@ -1,7 +1,7 @@
import { ComponentRef, NgZone } from '@angular/core'; import { ComponentRef, NgZone } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { NavController } from '../../providers/nav-controller'; import { NavController, NavDirection } from '../../providers/nav-controller';
export class StackController { export class StackController {
@ -32,7 +32,7 @@ export class StackController {
return this.views.find(vw => vw.url === activatedUrlKey); return this.views.find(vw => vw.url === activatedUrlKey);
} }
async setActive(enteringView: RouteView, direction: number, animated: boolean) { async setActive(enteringView: RouteView, direction: NavDirection, animated: boolean) {
const leavingView = this.getActive(); const leavingView = this.getActive();
this.insertView(enteringView, direction); this.insertView(enteringView, direction);
await this.transition(enteringView, leavingView, direction, animated, this.canGoBack(1), false); await this.transition(enteringView, leavingView, direction, animated, this.canGoBack(1), false);
@ -54,7 +54,7 @@ export class StackController {
this.transition( this.transition(
this.views[this.views.length - 2], // entering view this.views[this.views.length - 2], // entering view
this.views[this.views.length - 1], // leaving view this.views[this.views.length - 1], // leaving view
-1, 'back',
true, true,
true, true,
true true
@ -69,7 +69,7 @@ export class StackController {
} }
private insertView(enteringView: RouteView, direction: number) { private insertView(enteringView: RouteView, direction: NavDirection) {
// no stack // no stack
if (!this.stack) { if (!this.stack) {
this.views = [enteringView]; this.views = [enteringView];
@ -77,7 +77,7 @@ export class StackController {
} }
// stack setRoot // stack setRoot
if (direction === 0) { if (direction === 'root') {
this.views = [enteringView]; this.views = [enteringView];
return; return;
} }
@ -87,7 +87,7 @@ export class StackController {
if (index >= 0) { if (index >= 0) {
this.views = this.views.slice(0, index + 1); this.views = this.views.slice(0, index + 1);
} else { } else {
if (direction === 1) { if (direction === 'forward') {
this.views.push(enteringView); this.views.push(enteringView);
} else { } else {
this.views = [enteringView]; this.views = [enteringView];
@ -120,7 +120,7 @@ export class StackController {
private async transition( private async transition(
enteringView: RouteView | undefined, enteringView: RouteView | undefined,
leavingView: RouteView | undefined, leavingView: RouteView | undefined,
direction: number, direction: NavDirection,
animated: boolean, animated: boolean,
showGoBack: boolean, showGoBack: boolean,
progressAnimation: boolean progressAnimation: boolean
@ -145,7 +145,7 @@ export class StackController {
await containerEl.componentOnReady(); await containerEl.componentOnReady();
this.runningTransition = containerEl.commit(enteringEl, leavingEl, { this.runningTransition = containerEl.commit(enteringEl, leavingEl, {
duration: !animated ? 0 : undefined, duration: !animated ? 0 : undefined,
direction: direction === 1 ? 'forward' : 'back', direction: direction === 'forward' ? 'forward' : 'back', // TODO: refactor
deepWait: true, deepWait: true,
showGoBack, showGoBack,
progressAnimation progressAnimation

View File

@ -8,7 +8,7 @@ import { NavController } from '../../providers';
}) })
export class TabsDelegate { export class TabsDelegate {
@ContentChildren(TabDelegate) tabs: QueryList<TabDelegate>; @ContentChildren(TabDelegate) tabs!: QueryList<TabDelegate>;
constructor( constructor(
@Optional() private navCtrl: NavController, @Optional() private navCtrl: NavController,
@ -23,9 +23,8 @@ export class TabsDelegate {
return tabDelegate ? tabDelegate.getLastUrl() : undefined; return tabDelegate ? tabDelegate.getLastUrl() : undefined;
} }
@HostListener('ionTabButtonClick', ['$event']) @HostListener('ionTabButtonClick', ['$event.detail'])
onTabbarClick(ev: any) { onTabbarClick(detail: TabButtonClickDetail) {
const detail = ev.detail as TabButtonClickDetail;
const { tab, href, selected } = detail; const { tab, href, selected } = detail;
if (tab && href) { if (tab && href) {
const url = selected const url = selected

View File

@ -20,9 +20,9 @@ import { VirtualContext } from './virtual-utils';
}) })
export class VirtualScroll { export class VirtualScroll {
@ContentChild(VirtualItem) itmTmp: VirtualItem; @ContentChild(VirtualItem) itmTmp!: VirtualItem;
@ContentChild(VirtualHeader) hdrTmp: VirtualHeader; @ContentChild(VirtualHeader) hdrTmp!: VirtualHeader;
@ContentChild(VirtualFooter) ftrTmp: VirtualFooter; @ContentChild(VirtualFooter) ftrTmp!: VirtualFooter;
constructor( constructor(
private el: ElementRef, private el: ElementRef,

View File

@ -1,110 +1,96 @@
import { Injectable, Optional } from '@angular/core'; import { Injectable } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { NavigationExtras, Router, UrlTree } from '@angular/router'; import { NavigationExtras, NavigationStart, Router, UrlTree } from '@angular/router';
import { BackButtonEvent } from '@ionic/core'; import { Platform } from './platform';
export const enum NavIntent { export type NavDirection = 'forward' | 'back' | 'root' | 'auto';
Auto,
Forward,
Back,
Root
}
@Injectable() @Injectable()
export class NavController { export class NavController {
private intent: NavIntent = NavIntent.Root; private direction: NavDirection = DEFAULT_DIRECTION;
private animated = true; private animated = DEFAULT_ANIMATED;
private stack: string[] = []; private guessDirection: NavDirection = 'root';
private lastNavId = -1;
constructor( constructor(
private location: Location, private location: Location,
@Optional() private router?: Router private router: Router,
platform: Platform
) { ) {
window && window.document.addEventListener('ionBackButton', (ev) => { // Subscribe to router events to detect direction
(ev as BackButtonEvent).detail.register(0, () => this.goBack()); router.events.subscribe(ev => {
if (ev instanceof NavigationStart) {
const id = (ev.restoredState) ? ev.restoredState.navigationId : ev.id;
this.guessDirection = id < this.lastNavId ? 'back' : 'forward';
this.lastNavId = id;
}
}); });
// Subscribe to backButton events
platform.backButton.subscribeWithPriority(0, () => this.goBack());
} }
navigateForward(url: string | UrlTree | any[], animated?: boolean, extras?: NavigationExtras) { navigateForward(url: string | UrlTree | any[], animated?: boolean, extras?: NavigationExtras) {
this.setIntent(NavIntent.Forward, animated); this.setDirection('forward', animated);
if (Array.isArray(url)) { if (Array.isArray(url)) {
return this.router!.navigate(url, extras); return this.router.navigate(url, extras);
} else { } else {
return this.router!.navigateByUrl(url, extras); return this.router.navigateByUrl(url, extras);
} }
} }
navigateBack(url: string | UrlTree | any[], animated?: boolean, extras?: NavigationExtras) { navigateBack(url: string | UrlTree | any[], animated?: boolean, extras?: NavigationExtras) {
this.setIntent(NavIntent.Back, animated); this.setDirection('back', animated);
extras = { replaceUrl: true, ...extras }; // extras = { replaceUrl: true, ...extras };
if (Array.isArray(url)) { if (Array.isArray(url)) {
return this.router!.navigate(url, extras); return this.router.navigate(url, extras);
} else { } else {
return this.router!.navigateByUrl(url, extras); return this.router.navigateByUrl(url, extras);
} }
} }
navigateRoot(url: string | UrlTree | any[], animated?: boolean, extras?: NavigationExtras) { navigateRoot(url: string | UrlTree | any[], animated?: boolean, extras?: NavigationExtras) {
this.setIntent(NavIntent.Root, animated); this.setDirection('root', animated);
if (Array.isArray(url)) { if (Array.isArray(url)) {
return this.router!.navigate(url, extras); return this.router.navigate(url, extras);
} else { } else {
return this.router!.navigateByUrl(url, extras); return this.router.navigateByUrl(url, extras);
} }
} }
goBack(animated?: boolean) { goBack(animated?: boolean) {
this.setIntent(NavIntent.Back, animated); this.setDirection('back', animated);
return this.location.back(); return this.location.back();
} }
setIntent(intent: NavIntent, animated?: boolean) { setDirection(direction: NavDirection, animated?: boolean) {
this.intent = intent; this.direction = direction;
this.animated = (animated === undefined) this.animated = (animated === undefined)
? intent !== NavIntent.Root ? direction !== 'root'
: animated; : animated;
} }
consumeTransition() { consumeTransition() {
const guessDirection = this.guessDirection(); let direction: NavDirection = 'root';
let direction = 0;
let animated = false; let animated = false;
if (this.intent === NavIntent.Auto) { if (this.direction === 'auto') {
direction = guessDirection; direction = this.guessDirection;
animated = direction !== 0; animated = direction !== 'root';
} else { } else {
animated = this.animated; animated = this.animated;
direction = intentToDirection(this.intent); direction = this.direction;
} }
this.intent = NavIntent.Root; this.direction = DEFAULT_DIRECTION;
this.animated = false; this.animated = DEFAULT_ANIMATED;
return { return {
direction, direction,
animated animated
}; };
} }
private guessDirection() {
const index = this.stack.indexOf(document.location!.href);
if (index === -1) {
this.stack.push(document.location!.href);
return 1;
} else if (index < this.stack.length - 1) {
this.stack = this.stack.slice(0, index + 1);
return -1;
}
return 0;
}
} }
function intentToDirection(intent: NavIntent): number { const DEFAULT_DIRECTION = 'auto';
switch (intent) { const DEFAULT_ANIMATED = false;
case NavIntent.Forward: return 1;
case NavIntent.Back: return -1;
default: return 0;
}
}