From adae8d4ad1e31c132cd9d62a135d788e1c55dfb0 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Wed, 14 Nov 2018 19:10:27 +0100 Subject: [PATCH] fix(angular): adds tabs stack --- .../navigation/ion-router-outlet.ts | 5 +++ .../src/directives/navigation/tab-delegate.ts | 22 ++++++++++--- .../directives/navigation/tabs-delegate.ts | 33 +++++++++++++------ angular/src/directives/proxies.ts | 3 +- core/src/components.d.ts | 2 ++ .../components/tab-bar/tab-bar-interface.ts | 1 + core/src/components/tab-button/tab-button.tsx | 3 +- core/src/components/tabs/readme.md | 7 ++++ core/src/components/tabs/tabs.tsx | 12 ++++--- 9 files changed, 68 insertions(+), 20 deletions(-) diff --git a/angular/src/directives/navigation/ion-router-outlet.ts b/angular/src/directives/navigation/ion-router-outlet.ts index 4c3a216537..93729991b0 100644 --- a/angular/src/directives/navigation/ion-router-outlet.ts +++ b/angular/src/directives/navigation/ion-router-outlet.ts @@ -184,6 +184,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit { pop(deep = 1) { return this.stackCtrl.pop(deep); } + + getLastUrl() { + const active = this.stackCtrl.getActive(); + return active ? active.fullpath : undefined; + } } function emitEvent(el: HTMLElement) { diff --git a/angular/src/directives/navigation/tab-delegate.ts b/angular/src/directives/navigation/tab-delegate.ts index 12ce53ab4b..f36fe698eb 100644 --- a/angular/src/directives/navigation/tab-delegate.ts +++ b/angular/src/directives/navigation/tab-delegate.ts @@ -1,5 +1,6 @@ -import { ComponentFactoryResolver, Directive, ElementRef, HostListener, Injector, ViewContainerRef } from '@angular/core'; +import { ComponentFactoryResolver, ContentChild, Directive, ElementRef, HostListener, Injector, ViewContainerRef } from '@angular/core'; import { AngularDelegate } from '../../providers/angular-delegate'; +import { IonRouterOutlet } from './ion-router-outlet'; @Directive({ @@ -7,19 +8,32 @@ import { AngularDelegate } from '../../providers/angular-delegate'; }) export class TabDelegate { + @ContentChild(IonRouterOutlet) outlet?: IonRouterOutlet; + + private nativeEl: HTMLIonTabElement; + constructor( - private elementRef: ElementRef, + elementRef: ElementRef, resolver: ComponentFactoryResolver, injector: Injector, angularDelegate: AngularDelegate, location: ViewContainerRef ) { - elementRef.nativeElement.delegate = angularDelegate.create(resolver, injector, location); + this.nativeEl = elementRef.nativeElement; + this.nativeEl.delegate = angularDelegate.create(resolver, injector, location); + } + + get tab() { + return this.nativeEl.tab; + } + + getLastUrl() { + return this.outlet ? this.outlet.getLastUrl() : undefined; } @HostListener('ionRouterOutletActivated', ['$event']) async onNavChanged() { - const tab = this.elementRef.nativeElement as HTMLIonTabElement; + const tab = this.nativeEl; await tab.componentOnReady(); const tabs = tab.closest('ion-tabs'); if (tabs) { diff --git a/angular/src/directives/navigation/tabs-delegate.ts b/angular/src/directives/navigation/tabs-delegate.ts index 7268d00533..406748fc21 100644 --- a/angular/src/directives/navigation/tabs-delegate.ts +++ b/angular/src/directives/navigation/tabs-delegate.ts @@ -1,25 +1,38 @@ -import { Directive, ElementRef, HostListener, Optional } from '@angular/core'; -import { Router } from '@angular/router'; +import { ContentChildren, Directive, ElementRef, HostListener, Optional, QueryList } from '@angular/core'; +import { TabDelegate } from './tab-delegate'; +import { TabButtonClickDetail } from '@ionic/core'; +import { NavController } from '../../providers'; @Directive({ selector: 'ion-tabs' }) export class TabsDelegate { + @ContentChildren(TabDelegate) tabs: QueryList; + constructor( - @Optional() private router: Router, + @Optional() private navCtrl: NavController, elementRef: ElementRef ) { - if (router) { - elementRef.nativeElement.useRouter = true; - } + const nativeEl = elementRef.nativeElement as HTMLIonTabsElement; + nativeEl.useRouter = true; + } + + urlForTab(tab: string) { + const tabDelegate = this.tabs.find(item => item.tab === tab); + return tabDelegate ? tabDelegate.getLastUrl() : undefined; } @HostListener('ionTabButtonClick', ['$event']) - onTabbarClick(ev: UIEvent) { - const tabElm: HTMLIonTabButtonElement = ev.detail as any; - if (this.router && tabElm && tabElm.href) { - this.router.navigateByUrl(tabElm.href); + onTabbarClick(ev: any) { + const detail = ev.detail as TabButtonClickDetail; + const { tab, href, selected } = detail; + if (tab && href) { + const url = selected + ? href + : this.urlForTab(tab) || href; + + this.navCtrl.navigateBack(url, true); } } } diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts index 4ddad2feb0..b72be7432b 100644 --- a/angular/src/directives/proxies.ts +++ b/angular/src/directives/proxies.ts @@ -869,7 +869,7 @@ export class TabButton { } export declare interface Tabs extends StencilComponents<'IonTabs'> {} -@Component({ selector: 'ion-tabs', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '' }) +@Component({ selector: 'ion-tabs', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['useRouter'] }) export class Tabs { ionChange: EventEmitter; ionNavWillLoad: EventEmitter; @@ -880,6 +880,7 @@ export class Tabs { c.detach(); const el = r.nativeElement; proxyMethods(this, el, ['select', 'setRouteId', 'getRouteId', 'getTab', 'getSelected']); + proxyInputs(this, el, ['useRouter']); proxyOutputs(this, el, ['ionChange', 'ionNavWillLoad', 'ionNavWillChange', 'ionNavDidChange']); } } diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 119e403b56..ebc0ff8660 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -4500,6 +4500,7 @@ export namespace Components { */ 'select': (tab: string | HTMLIonTabElement) => Promise; 'setRouteId': (id: string) => Promise; + 'useRouter': boolean; } interface IonTabsAttributes extends StencilHTMLAttributes { /** @@ -4518,6 +4519,7 @@ export namespace Components { * Emitted when the navigation will load a component. */ 'onIonNavWillLoad'?: (event: CustomEvent) => void; + 'useRouter'?: boolean; } interface IonText { diff --git a/core/src/components/tab-bar/tab-bar-interface.ts b/core/src/components/tab-bar/tab-bar-interface.ts index ed086dd023..89091a5849 100644 --- a/core/src/components/tab-bar/tab-bar-interface.ts +++ b/core/src/components/tab-bar/tab-bar-interface.ts @@ -6,5 +6,6 @@ export interface TabBarChangedDetail { export interface TabButtonClickDetail { tab: string; + selected: boolean; href?: string; } diff --git a/core/src/components/tab-button/tab-button.tsx b/core/src/components/tab-button/tab-button.tsx index 9fd43d4edc..ec8165b2d9 100644 --- a/core/src/components/tab-button/tab-button.tsx +++ b/core/src/components/tab-button/tab-button.tsx @@ -74,7 +74,8 @@ export class TabButton implements ComponentInterface { if (!this.disabled) { this.ionTabButtonClick.emit({ tab: this.tab, - href: this.href + href: this.href, + selected: this.selected }); } ev.preventDefault(); diff --git a/core/src/components/tabs/readme.md b/core/src/components/tabs/readme.md index 66e1759ad1..5605aa92f5 100644 --- a/core/src/components/tabs/readme.md +++ b/core/src/components/tabs/readme.md @@ -87,6 +87,13 @@ Using tabs with Angular's router is fairly straight forward. The only additional +## Properties + +| Property | Attribute | Description | Type | Default | +| ----------- | ------------ | ----------- | --------- | ------- | +| `useRouter` | `use-router` | | `boolean` | `false` | + + ## Events | Event | Description | Detail | diff --git a/core/src/components/tabs/tabs.tsx b/core/src/components/tabs/tabs.tsx index c699d2f30e..2587fa67a0 100644 --- a/core/src/components/tabs/tabs.tsx +++ b/core/src/components/tabs/tabs.tsx @@ -11,7 +11,6 @@ export class Tabs implements NavOutlet { private transitioning = false; private leavingTab?: HTMLIonTabElement; - private useRouter = false; @Element() el!: HTMLStencilElement; @@ -21,6 +20,9 @@ export class Tabs implements NavOutlet { @Prop({ context: 'config' }) config!: Config; @Prop({ context: 'document' }) doc!: Document; + /** @internal */ + @Prop({ mutable: true }) useRouter = false; + /** * Emitted when the tab changes. */ @@ -42,7 +44,9 @@ export class Tabs implements NavOutlet { @Event() ionNavDidChange!: EventEmitter; async componentWillLoad() { - this.useRouter = !!this.doc.querySelector('ion-router') && !this.el.closest('[no-router]'); + if (!this.useRouter) { + this.useRouter = !!this.doc.querySelector('ion-router') && !this.el.closest('[no-router]'); + } this.tabs = Array.from(this.el.querySelectorAll('ion-tab')); this.ionNavWillLoad.emit(); this.componentWillUpdate(); @@ -69,9 +73,9 @@ export class Tabs implements NavOutlet { protected onTabClicked(ev: CustomEvent) { const { href, tab } = ev.detail; const selectedTab = this.tabs.find(t => t.tab === tab); - if (this.useRouter && href !== undefined) { + if (this.useRouter) { const router = this.doc.querySelector('ion-router'); - if (router) { + if (router && href !== undefined) { router.push(href); } } else if (selectedTab) {