mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 18:54:11 +08:00
fix(angular): tune routerLink default behaviour (#16405)
refactor: use ng7 features for direction detection
This commit is contained in:
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user