mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
refactor(nav): queue based transitions
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,519 +0,0 @@
|
||||
import { EventEmitter } from '@angular/core';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { Ion } from '../ion';
|
||||
import { isBlank, pascalCaseToDashCase } from '../../util/util';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { NavOptions } from './nav-interfaces';
|
||||
import { ViewController } from './view-controller';
|
||||
|
||||
|
||||
/**
|
||||
* @name NavController
|
||||
* @description
|
||||
*
|
||||
* NavController is the base class for navigation controller components like
|
||||
* [`Nav`](../Nav/) and [`Tab`](../../tabs/Tab/). You use navigation controllers
|
||||
* to navigate to [pages](#view-creation) in your app. At a basic level, a
|
||||
* navigation controller is an array of pages representing a particular history
|
||||
* (of a Tab for example). This array can be manipulated to navigate throughout
|
||||
* an app by pushing and popping pages or inserting and removing them at
|
||||
* arbitrary locations in history.
|
||||
*
|
||||
* The current page is the last one in the array, or the top of the stack if we
|
||||
* think of it that way. [Pushing](#push) a new page onto the top of the
|
||||
* navigation stack causes the new page to be animated in, while [popping](#pop)
|
||||
* the current page will navigate to the previous page in the stack.
|
||||
*
|
||||
* Unless you are using a directive like [NavPush](../NavPush/), or need a
|
||||
* specific NavController, most times you will inject and use a reference to the
|
||||
* nearest NavController to manipulate the navigation stack.
|
||||
*
|
||||
* ## Basic usage
|
||||
* The simplest way to navigate through an app is to create and initialize a new
|
||||
* nav controller using the `<ion-nav>` component. `ion-nav` extends the `NavController`
|
||||
* class.
|
||||
*
|
||||
* ```typescript
|
||||
* import { Component } from `@angular/core`;
|
||||
* import { ionicBootstrap } from 'ionic-angular';
|
||||
* import { StartPage } from './start-page';
|
||||
*
|
||||
* @Component(
|
||||
* template: `<ion-nav [root]="rootPage"></ion-nav>`
|
||||
* })
|
||||
* class MyApp {
|
||||
* // set the rootPage to the first page we want displayed
|
||||
* private rootPage: any = StartPage;
|
||||
*
|
||||
* constructor(){
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ionicBootstrap(MyApp);
|
||||
* ```
|
||||
*
|
||||
* ### Injecting NavController
|
||||
* Injecting NavController will always get you an instance of the nearest
|
||||
* NavController, regardless of whether it is a Tab or a Nav.
|
||||
*
|
||||
* Behind the scenes, when Ionic instantiates a new NavController, it creates an
|
||||
* injector with NavController bound to that instance (usually either a Nav or
|
||||
* Tab) and adds the injector to its own providers. For more information on
|
||||
* providers and dependency injection, see [Providers and DI]().
|
||||
*
|
||||
* Instead, you can inject NavController and know that it is the correct
|
||||
* navigation controller for most situations (for more advanced situations, see
|
||||
* [Menu](../../menu/Menu/) and [Tab](../../tab/Tab/)).
|
||||
*
|
||||
* ```ts
|
||||
* import { NavController } from 'ionic-angular';
|
||||
*
|
||||
* class MyComponent {
|
||||
* constructor(public navCtrl: NavController) {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ### Navigating from the Root component
|
||||
* What if you want to control navigation from your root app component?
|
||||
* You can't inject `NavController` because any components that are navigation
|
||||
* controllers are _children_ of the root component so they aren't available
|
||||
* to be injected.
|
||||
*
|
||||
* By adding a reference variable to the `ion-nav`, you can use `@ViewChild` to
|
||||
* get an instance of the `Nav` component, which is a navigation controller
|
||||
* (it extends `NavController`):
|
||||
*
|
||||
* ```typescript
|
||||
*
|
||||
* import { App, ViewChild } from '@angular/core';
|
||||
* import { NavController } from 'ionic-angular';
|
||||
*
|
||||
* @App({
|
||||
* template: '<ion-nav #myNav [root]="rootPage"></ion-nav>'
|
||||
* })
|
||||
* export class MyApp {
|
||||
* @ViewChild('myNav') nav : NavController
|
||||
* private rootPage = TabsPage;
|
||||
*
|
||||
* // Wait for the components in MyApp's template to be initialized
|
||||
* // In this case, we are waiting for the Nav with id="my-nav"
|
||||
* ngAfterViewInit() {
|
||||
* // Let's navigate from TabsPage to Page1
|
||||
* this.nav.push(Page1);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## View creation
|
||||
* Views are created when they are added to the navigation stack. For methods
|
||||
* like [push()](#push), the NavController takes any component class that is
|
||||
* decorated with `@Component` as its first argument. The NavController then
|
||||
* compiles that component, adds it to the app and animates it into view.
|
||||
*
|
||||
* By default, pages are cached and left in the DOM if they are navigated away
|
||||
* from but still in the navigation stack (the exiting page on a `push()` for
|
||||
* example). They are destroyed when removed from the navigation stack (on
|
||||
* [pop()](#pop) or [setRoot()](#setRoot)).
|
||||
*
|
||||
* ## Pushing a View
|
||||
* To push a new view on to the navigation stack, use the `push` method.
|
||||
* If the page has an [`<ion-navbar>`](../../navbar/Navbar/),
|
||||
* a back button will automatically be added to the pushed view.
|
||||
*
|
||||
* Data can also be passed to a view by passing an object to the `push` method.
|
||||
* The pushed view can then receive the data by accessing it via the `NavParams`
|
||||
* class.
|
||||
*
|
||||
* ```typescript
|
||||
* import { Component } from '@angular/core';
|
||||
* import { NavController } from 'ionic-angular';
|
||||
* import { OtherPage } from './other-page';
|
||||
* @Component({
|
||||
* template: `
|
||||
* <ion-header>
|
||||
* <ion-navbar>
|
||||
* <ion-title>Login</ion-title>
|
||||
* </ion-navbar>
|
||||
* </ion-header>
|
||||
*
|
||||
* <ion-content>
|
||||
* <button ion-button (click)="pushPage()">
|
||||
* Go to OtherPage
|
||||
* </button>
|
||||
* </ion-content>
|
||||
* `
|
||||
* })
|
||||
* export class StartPage {
|
||||
* constructor(public navCtrl: NavController) {
|
||||
* }
|
||||
*
|
||||
* pushPage(){
|
||||
* // push another page on to the navigation stack
|
||||
* // causing the nav controller to transition to the new page
|
||||
* // optional data can also be passed to the pushed page.
|
||||
* this.navCtrl.push(OtherPage, {
|
||||
* id: "123",
|
||||
* name: "Carl"
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* import { NavParams } from 'ionic-angular';
|
||||
*
|
||||
* @Component({
|
||||
* template: `
|
||||
* <ion-header>
|
||||
* <ion-navbar>
|
||||
* <ion-title>Other Page</ion-title>
|
||||
* </ion-navbar>
|
||||
* </ion-header>
|
||||
* <ion-content>I'm the other page!</ion-content>`
|
||||
* })
|
||||
* class OtherPage {
|
||||
* constructor(private navParams: NavParams) {
|
||||
* let id = navParams.get('id');
|
||||
* let name = navParams.get('name');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## Removing a view
|
||||
* To remove a view from the stack, use the `pop` method.
|
||||
* Popping a view will transition to the previous view.
|
||||
*
|
||||
* ```ts
|
||||
* import { Component } from '@angular/core';
|
||||
* import { NavController } from 'ionic-angular';
|
||||
*
|
||||
* @Component({
|
||||
* template: `
|
||||
* <ion-header>
|
||||
* <ion-navbar>
|
||||
* <ion-title>Other Page</ion-title>
|
||||
* </ion-navbar>
|
||||
* </ion-header>
|
||||
* <ion-content>I'm the other page!</ion-content>`
|
||||
* })
|
||||
* class OtherPage {
|
||||
* constructor(private navController: NavController ){
|
||||
* }
|
||||
*
|
||||
* popView(){
|
||||
* this.navController.pop();
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## Lifecycle events
|
||||
* Lifecycle events are fired during various stages of navigation. They can be
|
||||
* defined in any component type which is pushed/popped from a `NavController`.
|
||||
*
|
||||
* ```ts
|
||||
* import { Component } from '@angular/core';
|
||||
*
|
||||
* @Component({
|
||||
* template: 'Hello World'
|
||||
* })
|
||||
* class HelloWorld {
|
||||
* ionViewLoaded() {
|
||||
* console.log("I'm alive!");
|
||||
* }
|
||||
* ionViewWillLeave() {
|
||||
* console.log("Looks like I'm about to leave :(");
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* | Page Event | Description |
|
||||
* |---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
* | `ionViewLoaded` | Runs when the page has loaded. This event only happens once per page being created and added to the DOM. If a page leaves but is cached, then this event will not fire again on a subsequent viewing. The `ionViewLoaded` event is good place to put your setup code for the page. |
|
||||
* | `ionViewWillEnter` | Runs when the page is about to enter and become the active page. |
|
||||
* | `ionViewDidEnter` | Runs when the page has fully entered and is now the active page. This event will fire, whether it was the first load or a cached page. |
|
||||
* | `ionViewWillLeave` | Runs when the page is about to leave and no longer be the active page. |
|
||||
* | `ionViewDidLeave` | Runs when the page has finished leaving and is no longer the active page. |
|
||||
* | `ionViewWillUnload` | Runs when the page is about to be destroyed and have its elements removed. |
|
||||
* | `ionViewDidUnload` | Runs after the page has been destroyed and its elements have been removed.
|
||||
*
|
||||
*
|
||||
* ## Asynchronous Nav Transitions
|
||||
*
|
||||
* Navigation transitions are asynchronous operations. When a transition is started,
|
||||
* the `push` or `pop` method will return immediately, before the transition is complete.
|
||||
*
|
||||
* Generally, the developer does not need to be concerned about this. In the event
|
||||
* multiple transitions need to be synchronized or transition timing is critical,
|
||||
* the best practice is to chain the transitions together using the return value
|
||||
* from the `push` and `pop` methods.
|
||||
*
|
||||
* The `push` and `pop` methods return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
* Promises are a way to represent and chain together multiple asynchronous
|
||||
* operations in order. Navigation actions can be chained together very easily using promises.
|
||||
*
|
||||
* ```typescript
|
||||
* let navTransitionPromise = this.navController.push(Page2);
|
||||
* navTransitionPromise.then( () => {
|
||||
* // the transition has completed, so I can push another page now
|
||||
* return this.navController.push(Page3);
|
||||
* }).then( () => {
|
||||
* // the second transition has completed, so I can push yet another page
|
||||
return this.navController.push(Page4);
|
||||
* }).then( () => {
|
||||
* console.log('The transitions are complete!');
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ## NavOptions
|
||||
*
|
||||
* Some methods on `NavController` allow for customizing the current transition.
|
||||
* To do this, we can pass an object with the modified properites.
|
||||
*
|
||||
*
|
||||
* | Property | Value | Description |
|
||||
* |-----------|-----------|------------------------------------------------------------------------------------------------------------|
|
||||
* | animate | `boolean` | Whether or not the transition should animate. |
|
||||
* | animation | `string` | What kind of animation should be used. |
|
||||
* | direction | `string` | The conceptual direction the user is navigating. For example, is the user navigating `forward`, or `back`? |
|
||||
* | duration | `number` | The length in milliseconds the animation should take. |
|
||||
* | easing | `string` | The easing for the animation. |
|
||||
*
|
||||
* The property 'animation' understands the following values: `md-transition`, `ios-transition` and `wp-transition`.
|
||||
*
|
||||
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
|
||||
*/
|
||||
export abstract class NavController {
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when a component is loaded.
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
viewDidLoad: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when a component is about to be loaded.
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
viewWillEnter: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when a component has fully become the active component.
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
viewDidEnter: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when a component is about to leave, and no longer active.
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
viewWillLeave: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when a component has fully left and is no longer active.
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
viewDidLeave: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when a component is about to be unloaded and destroyed.
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
viewWillUnload: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when a component has fully been unloaded and destroyed.
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
viewDidUnload: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The parent navigation instance. If this is the root nav, then
|
||||
* it'll be `null`. A `Tab` instance's parent is `Tabs`, otherwise
|
||||
* the parent would be another nav, if it's not already the root nav.
|
||||
*/
|
||||
parent: any;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
config: Config;
|
||||
|
||||
/**
|
||||
* Set the root for the current navigation stack.
|
||||
* @param {Page} page The name of the component you want to push on the navigation stack.
|
||||
* @param {object} [params={}] Any nav-params you want to pass along to the next view.
|
||||
* @param {object} [opts={}] Any options you want to use pass to transtion.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract setRoot(page: any, params?: any, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* Set the views of the current navigation stack and navigate to the
|
||||
* last view. By default animations are disabled, but they can be enabled
|
||||
* by passing options to the navigation controller.You can also pass any
|
||||
* navigation params to the individual pages in the array.
|
||||
*
|
||||
* @param {array<Page>} pages An arry of page components and their params to load in the stack.
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract setPages(pages: Array<{page: any, params?: any}>, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* Push a new component onto the current navication stack. Pass any aditional information
|
||||
* along as an object. This additional information is acessible through NavParams
|
||||
*
|
||||
* @param {Page} page The page component class you want to push on to the navigation stack
|
||||
* @param {object} [params={}] Any nav-params you want to pass along to the next view
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract push(page: any, params?: any, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* Inserts a component into the nav stack at the specified index. This is useful if
|
||||
* you need to add a component at any point in your navigation stack.
|
||||
*
|
||||
*
|
||||
* @param {number} insertIndex The index where to insert the page.
|
||||
* @param {Page} page The component you want to insert into the nav stack.
|
||||
* @param {object} [params={}] Any nav-params you want to pass along to the next page.
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* Inserts an array of components into the nav stack at the specified index.
|
||||
* The last component in the array will become instantiated as a view,
|
||||
* and animate in to become the active view.
|
||||
*
|
||||
* @param {number} insertIndex The index where you want to insert the page.
|
||||
* @param {array} insertPages An array of objects, each with a `page` and optionally `params` property.
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract insertPages(insertIndex: number, insertPages: Array<{page: any, params?: any}>, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* Call to navigate back from a current component. Similar to `push()`, you
|
||||
* can also pass navigation options.
|
||||
*
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract pop(opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* Navigate back to the root of the stack, no matter how far back that is.
|
||||
*
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract popToRoot(opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Pop to a specific view in the history stack.
|
||||
*
|
||||
* @param {ViewController} view to pop to
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract popTo(view: ViewController, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* Removes a page from the nav stack at the specified index.
|
||||
*
|
||||
* @param {number} [startIndex] The starting index to remove pages from the stack. Default is the index of the last page.
|
||||
* @param {number} [removeCount] The number of pages to remove, defaults to remove `1`.
|
||||
* @param {object} [opts={}] Any options you want to use pass to transtion.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract remove(startIndex: number, removeCount?: number, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
|
||||
/**
|
||||
* @param {number} index The index of the page to get.
|
||||
* @returns {ViewController} Returns the view controller that matches the given index.
|
||||
*/
|
||||
abstract getByIndex(index: number): ViewController;
|
||||
|
||||
/**
|
||||
* @returns {ViewController} Returns the active page's view controller.
|
||||
*/
|
||||
abstract getActive(): ViewController;
|
||||
|
||||
/**
|
||||
* Returns if the given view is the active view or not.
|
||||
* @param {ViewController} view
|
||||
* @returns {boolean}
|
||||
*/
|
||||
abstract isActive(view: ViewController): boolean;
|
||||
|
||||
/**
|
||||
* Returns the view controller which is before the given view controller.
|
||||
* @param {ViewController} view
|
||||
* @returns {viewController}
|
||||
*/
|
||||
abstract getPrevious(view: ViewController): ViewController;
|
||||
|
||||
/**
|
||||
* Returns the first view controller in this nav controller's stack.
|
||||
* @returns {ViewController}
|
||||
*/
|
||||
abstract first(): ViewController;
|
||||
|
||||
/**
|
||||
* Returns the last page in this nav controller's stack.
|
||||
* @returns {ViewController}
|
||||
*/
|
||||
abstract last(): ViewController;
|
||||
|
||||
/**
|
||||
* Returns the index number of the given view controller.
|
||||
* @param {ViewController} view
|
||||
* @returns {number}
|
||||
*/
|
||||
abstract indexOf(view: ViewController): number;
|
||||
|
||||
/**
|
||||
* Returns the number of views in this nav controller.
|
||||
* @returns {number} The number of views in this stack, including the current view.
|
||||
*/
|
||||
abstract length(): number;
|
||||
|
||||
/**
|
||||
* Returns the active child navigation.
|
||||
*/
|
||||
abstract getActiveChildNav(): any;
|
||||
|
||||
/**
|
||||
* Returns if the nav controller is actively transitioning or not.
|
||||
* @return {boolean}
|
||||
*/
|
||||
abstract isTransitioning(includeAncestors?: boolean): boolean
|
||||
|
||||
/**
|
||||
* If it's possible to use swipe back or not. If it's not possible
|
||||
* to go back, or swipe back is not enabled, then this will return `false`.
|
||||
* If it is possible to go back, and swipe back is enabled, then this
|
||||
* will return `true`.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
abstract canSwipeBack(): boolean;
|
||||
|
||||
/**
|
||||
* Returns `true` if there's a valid previous page that we can pop
|
||||
* back to. Otherwise returns `false`.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
abstract canGoBack(): boolean;
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
|
||||
export interface NavOptions {
|
||||
animate?: boolean;
|
||||
animation?: string;
|
||||
direction?: string;
|
||||
duration?: number;
|
||||
easing?: string;
|
||||
id?: string;
|
||||
keyboardClose?: boolean;
|
||||
preload?: boolean;
|
||||
transitionDelay?: number;
|
||||
progressAnimation?: boolean;
|
||||
climbNav?: boolean;
|
||||
ev?: any;
|
||||
}
|
||||
|
||||
export const DIRECTION_BACK = 'back';
|
||||
export const DIRECTION_FORWARD = 'forward';
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* @name NavParams
|
||||
* @description
|
||||
* NavParams are an object that exists on a page and can contain data for that particular view.
|
||||
* Similar to how data was pass to a view in V1 with `$stateParams`, NavParams offer a much more flexible
|
||||
* option with a simple `get` method.
|
||||
*
|
||||
* @usage
|
||||
* ```ts
|
||||
* export class MyClass{
|
||||
* constructor(public params: NavParams){
|
||||
* // userParams is an object we have in our nav-parameters
|
||||
* this.params.get('userParams');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @demo /docs/v2/demos/nav-params/
|
||||
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
|
||||
* @see {@link ../NavController/ NavController API Docs}
|
||||
* @see {@link ../Nav/ Nav API Docs}
|
||||
* @see {@link ../NavPush/ NavPush API Docs}
|
||||
*/
|
||||
export class NavParams {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {TODO} data TODO
|
||||
*/
|
||||
constructor(public data: any = {}) {}
|
||||
|
||||
/**
|
||||
* Get the value of a nav-parameter for the current view
|
||||
*
|
||||
* ```ts
|
||||
* export class MyClass{
|
||||
* constructor(public params: NavParams){
|
||||
* // userParams is an object we have in our nav-parameters
|
||||
* this.params.get('userParams');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @param {string} parameter Which param you want to look up
|
||||
*/
|
||||
get(param: string): any {
|
||||
return this.data[param];
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { NavController } from './nav-controller';
|
||||
import { noop } from '../../util/util';
|
||||
import { AfterViewInit, Directive, HostBinding, HostListener, OnChanges, Optional } from '@angular/core';
|
||||
|
||||
import { DeepLinker } from '../../navigation/deep-linker';
|
||||
import { NavController } from '../../navigation/nav-controller';
|
||||
import { ViewController } from '../../navigation/view-controller';
|
||||
|
||||
|
||||
/**
|
||||
* @name NavPop
|
||||
@@ -27,9 +30,9 @@ import { noop } from '../../util/util';
|
||||
})
|
||||
export class NavPop {
|
||||
|
||||
constructor(@Optional() private _nav: NavController) {
|
||||
constructor(@Optional() public _nav: NavController) {
|
||||
if (!_nav) {
|
||||
console.error('nav-pop must be within a NavController');
|
||||
console.error('navPop must be within a NavController');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +40,7 @@ export class NavPop {
|
||||
onClick(): boolean {
|
||||
// If no target, or if target is _self, prevent default browser behavior
|
||||
if (this._nav) {
|
||||
this._nav.pop(null, noop);
|
||||
this._nav.pop(null, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -45,3 +48,39 @@ export class NavPop {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'a[navPop]'
|
||||
})
|
||||
export class NavPopAnchor implements OnChanges, AfterViewInit {
|
||||
|
||||
constructor(
|
||||
@Optional() public host: NavPop,
|
||||
public linker: DeepLinker,
|
||||
@Optional() public viewCtrl: ViewController) {}
|
||||
|
||||
@HostBinding() href: string;
|
||||
|
||||
updateHref() {
|
||||
if (this.host && this.viewCtrl) {
|
||||
const previousView = this.host._nav.getPrevious(this.viewCtrl);
|
||||
this.href = (previousView && this.linker.createUrl(this.host._nav, this.viewCtrl.component, this.viewCtrl.data)) || '#';
|
||||
|
||||
} else {
|
||||
this.href = '#';
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.updateHref();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.updateHref();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { AfterViewInit, Directive, Host, HostBinding, HostListener, Input, Optional, OnChanges } from '@angular/core';
|
||||
|
||||
import { NavController } from './nav-controller';
|
||||
import { noop } from '../../util/util';
|
||||
import { DeepLinker } from '../../navigation/deep-linker';
|
||||
import { NavController } from '../../navigation/nav-controller';
|
||||
|
||||
/**
|
||||
* @name NavPush
|
||||
@@ -60,7 +60,7 @@ export class NavPush {
|
||||
@Input() navParams: {[k: string]: any};
|
||||
|
||||
|
||||
constructor(@Optional() private _nav: NavController) {
|
||||
constructor(@Optional() public _nav: NavController) {
|
||||
if (!_nav) {
|
||||
console.error('navPush must be within a NavController');
|
||||
}
|
||||
@@ -68,13 +68,44 @@ export class NavPush {
|
||||
|
||||
@HostListener('click')
|
||||
onClick(): boolean {
|
||||
// If no target, or if target is _self, prevent default browser behavior
|
||||
if (this._nav) {
|
||||
this._nav.push(this.navPush, this.navParams, noop);
|
||||
this._nav.push(this.navPush, this.navParams, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'a[navPush]'
|
||||
})
|
||||
export class NavPushAnchor implements OnChanges, AfterViewInit {
|
||||
|
||||
constructor(
|
||||
@Host() public host: NavPush,
|
||||
@Optional() public linker: DeepLinker) {}
|
||||
|
||||
@HostBinding() href: string;
|
||||
|
||||
updateHref() {
|
||||
if (this.host && this.linker) {
|
||||
this.href = this.linker.createUrl(this.host._nav, this.host.navPush, this.host.navParams) || '#';
|
||||
|
||||
} else {
|
||||
this.href = '#';
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.updateHref();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.updateHref();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { AfterViewInit, Component, ComponentResolver, ElementRef, Input, Optional, NgZone, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
import { AfterViewInit, Component, ComponentFactoryResolver, ElementRef, Input, Optional, NgZone, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { DeepLinker } from '../../navigation/deep-linker';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
import { NavControllerBase } from './nav-controller-base';
|
||||
import { ViewController } from './view-controller';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { NavControllerBase } from '../../navigation/nav-controller-base';
|
||||
import { NavOptions } from '../../navigation/nav-util';
|
||||
import { TransitionController } from '../../transitions/transition-controller';
|
||||
import { ViewController } from '../../navigation/view-controller';
|
||||
|
||||
/**
|
||||
* @name Nav
|
||||
@@ -24,32 +27,27 @@ import { ViewController } from './view-controller';
|
||||
*
|
||||
* ```ts
|
||||
* import { Component } from '@angular/core';
|
||||
* import { ionicBootstrap } from 'ionic-angular';
|
||||
* import { GettingStartedPage } from './getting-started';
|
||||
*
|
||||
* @Component({
|
||||
* template: `<ion-nav [root]="root"></ion-nav>`
|
||||
* })
|
||||
* class MyApp {
|
||||
* private root: any = GettingStartedPage;
|
||||
* root = GettingStartedPage;
|
||||
*
|
||||
* constructor(){
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ionicBootstrap(MyApp);
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @demo /docs/v2/demos/navigation/
|
||||
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-nav',
|
||||
template: `
|
||||
<div #viewport nav-viewport></div>
|
||||
<div class="nav-decor"></div>
|
||||
`,
|
||||
template:
|
||||
'<div #viewport nav-viewport></div>' +
|
||||
'<div class="nav-decor"></div>',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
@@ -65,16 +63,17 @@ export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
elementRef: ElementRef,
|
||||
zone: NgZone,
|
||||
renderer: Renderer,
|
||||
compiler: ComponentResolver,
|
||||
gestureCtrl: GestureController
|
||||
cfr: ComponentFactoryResolver,
|
||||
gestureCtrl: GestureController,
|
||||
transCtrl: TransitionController,
|
||||
@Optional() linker: DeepLinker
|
||||
) {
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker);
|
||||
|
||||
if (viewCtrl) {
|
||||
// an ion-nav can also act as an ion-page within a parent ion-nav
|
||||
// this would happen when an ion-nav nests a child ion-nav.
|
||||
viewCtrl.setContent(this);
|
||||
viewCtrl.setContentRef(elementRef);
|
||||
viewCtrl._setContent(this);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
@@ -89,7 +88,7 @@ export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
} else if (app && !app.getRootNav()) {
|
||||
// a root nav has not been registered yet with the app
|
||||
// this is the root navcontroller for the entire app
|
||||
app.setRootNav(this);
|
||||
app._setRootNav(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,17 +100,26 @@ export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
this.setViewport(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
this._hasInit = true;
|
||||
|
||||
if (this._root) {
|
||||
this.push(this._root);
|
||||
let navSegment = this._linker.initNav(this);
|
||||
if (navSegment && navSegment.component) {
|
||||
// there is a segment match in the linker
|
||||
this.setPages(this._linker.initViews(navSegment), null, null);
|
||||
|
||||
} else if (this._root) {
|
||||
// no segment match, so use the root property
|
||||
this.push(this._root, this.rootParams, {
|
||||
isNavRoot: (<any>this._app.getRootNav() === this)
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
goToRoot(opts: NavOptions) {
|
||||
this.setRoot(this._root, this.rootParams, opts, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {Page} The Page component to load as the root page within this nav.
|
||||
*/
|
||||
@@ -127,6 +135,11 @@ export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {object} Any nav-params to pass to the root page of this nav.
|
||||
*/
|
||||
@Input() rootParams: any;
|
||||
|
||||
/**
|
||||
* @input {boolean} Whether it's possible to swipe-to-go-back on this nav controller or not.
|
||||
*/
|
||||
@@ -138,4 +151,11 @@ export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
this._sbEnabled = isTrueProperty(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { ComponentResolver, Directive, ElementRef, forwardRef, Inject, NgZone, Optional, Renderer, ViewContainerRef } from '@angular/core';
|
||||
import { ComponentFactoryResolver, Directive, ElementRef, forwardRef, Inject, NgZone, Optional, Renderer, ViewContainerRef } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
import { DeepLinker } from '../../navigation/deep-linker';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||
import { NavControllerBase } from '../../navigation/nav-controller-base';
|
||||
import { TransitionController } from '../../transitions/transition-controller';
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[nav-portal]'
|
||||
selector: '[overlay-portal]'
|
||||
})
|
||||
export class NavPortal extends NavControllerBase {
|
||||
export class OverlayPortal extends NavControllerBase {
|
||||
constructor(
|
||||
@Inject(forwardRef(() => App)) app: App,
|
||||
config: Config,
|
||||
@@ -20,15 +22,16 @@ export class NavPortal extends NavControllerBase {
|
||||
elementRef: ElementRef,
|
||||
zone: NgZone,
|
||||
renderer: Renderer,
|
||||
compiler: ComponentResolver,
|
||||
cfr: ComponentFactoryResolver,
|
||||
gestureCtrl: GestureController,
|
||||
transCtrl: TransitionController,
|
||||
@Optional() linker: DeepLinker,
|
||||
viewPort: ViewContainerRef
|
||||
) {
|
||||
super(null, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
||||
super(null, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker);
|
||||
this._isPortal = true;
|
||||
this._init = true;
|
||||
this.setViewport(viewPort);
|
||||
app.setPortal(this);
|
||||
|
||||
// on every page change make sure the portal has
|
||||
// dismissed any views that should be auto dismissed on page change
|
||||
@@ -1,55 +0,0 @@
|
||||
import { assign } from '../../util/util';
|
||||
import { GestureController, GestureDelegate, GesturePriority } from '../../gestures/gesture-controller';
|
||||
import { MenuController } from '../menu/menu-controller';
|
||||
import { NavControllerBase } from './nav-controller-base';
|
||||
import { SlideData } from '../../gestures/slide-gesture';
|
||||
import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
|
||||
|
||||
|
||||
export class SwipeBackGesture extends SlideEdgeGesture {
|
||||
|
||||
constructor(
|
||||
element: HTMLElement,
|
||||
options: any,
|
||||
private _nav: NavControllerBase,
|
||||
gestureCtlr: GestureController
|
||||
) {
|
||||
super(element, assign({
|
||||
direction: 'x',
|
||||
maxEdgeStart: 75,
|
||||
gesture: gestureCtlr.create('goback-swipe', {
|
||||
priority: GesturePriority.GoBackSwipe,
|
||||
})
|
||||
}, options));
|
||||
}
|
||||
|
||||
canStart(ev: any): boolean {
|
||||
// the gesture swipe angle must be mainly horizontal and the
|
||||
// gesture distance would be relatively short for a swipe back
|
||||
// and swipe back must be possible on this nav controller
|
||||
return (
|
||||
this._nav.canSwipeBack() &&
|
||||
super.canStart(ev)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
onSlideBeforeStart(slideData: SlideData, ev: any) {
|
||||
console.debug('swipeBack, onSlideBeforeStart', ev.type);
|
||||
this._nav.swipeBackStart();
|
||||
}
|
||||
|
||||
onSlide(slide: SlideData) {
|
||||
let stepValue = (slide.distance / slide.max);
|
||||
console.debug('swipeBack, onSlide, distance', slide.distance, 'max', slide.max, 'stepValue', stepValue);
|
||||
this._nav.swipeBackProgress(stepValue);
|
||||
}
|
||||
|
||||
onSlideEnd(slide: SlideData, ev: any) {
|
||||
let shouldComplete = (Math.abs(slide.velocity) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
|
||||
let currentStepValue = (slide.distance / slide.max);
|
||||
|
||||
console.debug('swipeBack, onSlideEnd, shouldComplete', shouldComplete, 'currentStepValue', currentStepValue);
|
||||
this._nav.swipeBackEnd(shouldComplete, currentStepValue);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
import { LifeCycleEvent, ViewController } from '../../../../src';
|
||||
|
||||
export function run() {
|
||||
describe('ViewController', () => {
|
||||
|
||||
afterEach(() => {
|
||||
if ( subscription ) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
describe('willEnter', () => {
|
||||
it('should emit LifeCycleEvent when called with component data', (done) => {
|
||||
// arrange
|
||||
let viewController = new ViewController(FakePage);
|
||||
subscription = viewController.willEnter.subscribe((event: LifeCycleEvent) => {
|
||||
// assert
|
||||
expect(event).toEqual(null);
|
||||
done();
|
||||
}, (err: any) => {
|
||||
done(err);
|
||||
});
|
||||
|
||||
// act
|
||||
viewController.fireWillEnter();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('didEnter', () => {
|
||||
it('should emit LifeCycleEvent when called with component data', (done) => {
|
||||
// arrange
|
||||
let viewController = new ViewController(FakePage);
|
||||
subscription = viewController.didEnter.subscribe((event: LifeCycleEvent) => {
|
||||
// assert
|
||||
expect(event).toEqual(null);
|
||||
done();
|
||||
}, (err: any) => {
|
||||
done(err);
|
||||
});
|
||||
|
||||
// act
|
||||
viewController.fireDidEnter();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('willLeave', () => {
|
||||
it('should emit LifeCycleEvent when called with component data', (done) => {
|
||||
// arrange
|
||||
let viewController = new ViewController(FakePage);
|
||||
subscription = viewController.willLeave.subscribe((event: LifeCycleEvent) => {
|
||||
// assert
|
||||
expect(event).toEqual(null);
|
||||
done();
|
||||
}, (err: any) => {
|
||||
done(err);
|
||||
});
|
||||
|
||||
// act
|
||||
viewController.fireWillLeave();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('didLeave', () => {
|
||||
it('should emit LifeCycleEvent when called with component data', (done) => {
|
||||
// arrange
|
||||
let viewController = new ViewController(FakePage);
|
||||
subscription = viewController.didLeave.subscribe((event: LifeCycleEvent) => {
|
||||
// assert
|
||||
expect(event).toEqual(null);
|
||||
done();
|
||||
}, (err: any) => {
|
||||
done(err);
|
||||
});
|
||||
|
||||
// act
|
||||
viewController.fireDidLeave();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('willUnload', () => {
|
||||
it('should emit LifeCycleEvent when called with component data', (done) => {
|
||||
// arrange
|
||||
let viewController = new ViewController(FakePage);
|
||||
subscription = viewController.willUnload.subscribe((event: LifeCycleEvent) => {
|
||||
expect(event).toEqual(null);
|
||||
done();
|
||||
}, (err: any) => {
|
||||
done(err);
|
||||
});
|
||||
|
||||
// act
|
||||
viewController.fireWillUnload();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('destroy', () => {
|
||||
it('should emit LifeCycleEvent when called with component data', (done) => {
|
||||
// arrange
|
||||
let viewController = new ViewController(FakePage);
|
||||
subscription = viewController.didUnload.subscribe((event: LifeCycleEvent) => {
|
||||
// assert
|
||||
expect(event).toEqual(null);
|
||||
done();
|
||||
}, (err: any) => {
|
||||
done(err);
|
||||
});
|
||||
|
||||
// act
|
||||
viewController.destroy();
|
||||
}, 10000);
|
||||
});
|
||||
});
|
||||
|
||||
let subscription: any = null;
|
||||
class FakePage {}
|
||||
}
|
||||
@@ -1,628 +0,0 @@
|
||||
import { ChangeDetectorRef, ElementRef, EventEmitter, Output, Renderer } from '@angular/core';
|
||||
|
||||
import { Footer, Header } from '../toolbar/toolbar';
|
||||
import { isPresent, merge } from '../../util/util';
|
||||
import { Navbar } from '../navbar/navbar';
|
||||
import { NavController } from './nav-controller';
|
||||
import { NavOptions } from './nav-interfaces';
|
||||
import { NavParams } from './nav-params';
|
||||
|
||||
|
||||
/**
|
||||
* @name ViewController
|
||||
* @description
|
||||
* Access various features and information about the current view.
|
||||
* @usage
|
||||
* ```ts
|
||||
* import { Component } from '@angular/core';
|
||||
* import { ViewController } from 'ionic-angular';
|
||||
*
|
||||
* @Component({...})
|
||||
* export class MyPage{
|
||||
*
|
||||
* constructor(public viewCtrl: ViewController) {}
|
||||
*
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class ViewController {
|
||||
private _cntDir: any;
|
||||
private _cntRef: ElementRef;
|
||||
private _tbRefs: ElementRef[] = [];
|
||||
private _hdrDir: Header;
|
||||
private _ftrDir: Footer;
|
||||
private _destroyFn: Function;
|
||||
private _hdAttr: string = null;
|
||||
private _leavingOpts: NavOptions = null;
|
||||
private _loaded: boolean = false;
|
||||
private _nbDir: Navbar;
|
||||
private _onDidDismiss: Function = null;
|
||||
private _onWillDismiss: Function = null;
|
||||
private _pgRef: ElementRef;
|
||||
private _cd: ChangeDetectorRef;
|
||||
protected _nav: NavController;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when the current component will become active
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
willEnter: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when the current component has become active
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
didEnter: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when the current component will no longer be active
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
willLeave: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when the current component is no long active
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
didLeave: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when the current component will be destroyed
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
willUnload: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* Observable to be subscribed to when the current component has been destroyed
|
||||
* @returns {Observable} Returns an observable
|
||||
*/
|
||||
didUnload: EventEmitter<any>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
data: any;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
instance: any = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
state: number = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* If this is currently the active view, then set to false
|
||||
* if it does not want the other views to fire their own lifecycles.
|
||||
*/
|
||||
fireOtherLifecycles: boolean = true;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
isOverlay: boolean = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
zIndex: number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Output() private _emitter: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
constructor(public componentType?: any, data?: any) {
|
||||
// passed in data could be NavParams, but all we care about is its data object
|
||||
this.data = (data instanceof NavParams ? data.data : (isPresent(data) ? data : {}));
|
||||
|
||||
this.willEnter = new EventEmitter();
|
||||
this.didEnter = new EventEmitter();
|
||||
this.willLeave = new EventEmitter();
|
||||
this.didLeave = new EventEmitter();
|
||||
this.willUnload = new EventEmitter();
|
||||
this.didUnload = new EventEmitter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
subscribe(generatorOrNext?: any): any {
|
||||
return this._emitter.subscribe(generatorOrNext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
emit(data?: any) {
|
||||
this._emitter.emit(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* onDismiss(..) has been deprecated. Please use onDidDismiss(..) instead
|
||||
*/
|
||||
private onDismiss(callback: Function) {
|
||||
// deprecated warning: added beta.11 2016-06-30
|
||||
console.warn('onDismiss(..) has been deprecated. Please use onDidDismiss(..) instead');
|
||||
this.onDidDismiss(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDidDismiss(callback: Function) {
|
||||
this._onDidDismiss = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onWillDismiss(callback: Function) {
|
||||
this._onWillDismiss = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
dismiss(data?: any, role?: any, navOptions: NavOptions = {}) {
|
||||
let options = merge({}, this._leavingOpts, navOptions);
|
||||
this._onWillDismiss && this._onWillDismiss(data, role);
|
||||
return this._nav.remove(this._nav.indexOf(this), 1, options).then(() => {
|
||||
this._onDidDismiss && this._onDidDismiss(data, role);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setNav(navCtrl: NavController) {
|
||||
this._nav = navCtrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getNav() {
|
||||
return this._nav;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getTransitionName(direction: string): string {
|
||||
return this._nav && this._nav.config.get('pageTransition');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getNavParams(): NavParams {
|
||||
return new NavParams(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setLeavingOpts(opts: NavOptions) {
|
||||
this._leavingOpts = opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if you can go back in the navigation stack.
|
||||
* @param {boolean} Check whether or not you can go back from this page
|
||||
* @returns {boolean} Returns if it's possible to go back from this Page.
|
||||
*/
|
||||
enableBack(): boolean {
|
||||
// update if it's possible to go back from this nav item
|
||||
if (this._nav) {
|
||||
let previousItem = this._nav.getPrevious(this);
|
||||
// the previous view may exist, but if it's about to be destroyed
|
||||
// it shouldn't be able to go back to
|
||||
return !!(previousItem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setChangeDetector(cd: ChangeDetectorRef) {
|
||||
this._cd = cd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setInstance(instance: any) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
get name(): string {
|
||||
return this.componentType ? this.componentType['name'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the current component in the current navigation stack.
|
||||
* @returns {number} Returns the index of this page within its `NavController`.
|
||||
*/
|
||||
get index(): number {
|
||||
return (this._nav ? this._nav.indexOf(this) : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Returns if this Page is the first in the stack of pages within its NavController.
|
||||
*/
|
||||
isFirst(): boolean {
|
||||
return (this._nav ? this._nav.first() === this : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Returns if this Page is the last in the stack of pages within its NavController.
|
||||
*/
|
||||
isLast(): boolean {
|
||||
return (this._nav ? this._nav.last() === this : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
domShow(shouldShow: boolean, renderer: Renderer) {
|
||||
// using hidden element attribute to display:none and not render views
|
||||
// renderAttr of '' means the hidden attribute will be added
|
||||
// renderAttr of null means the hidden attribute will be removed
|
||||
// doing checks to make sure we only make an update to the element when needed
|
||||
if (this._pgRef &&
|
||||
(shouldShow && this._hdAttr === '' ||
|
||||
!shouldShow && this._hdAttr !== '')) {
|
||||
|
||||
this._hdAttr = (shouldShow ? null : '');
|
||||
|
||||
renderer.setElementAttribute(this._pgRef.nativeElement, 'hidden', this._hdAttr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setZIndex(zIndex: number, renderer: Renderer) {
|
||||
if (this._pgRef && zIndex !== this.zIndex) {
|
||||
this.zIndex = zIndex;
|
||||
renderer.setElementStyle(this._pgRef.nativeElement, 'z-index', zIndex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setPageRef(elementRef: ElementRef) {
|
||||
this._pgRef = elementRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {elementRef} Returns the Page's ElementRef
|
||||
*/
|
||||
pageRef(): ElementRef {
|
||||
return this._pgRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setContentRef(elementRef: ElementRef) {
|
||||
this._cntRef = elementRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {elementRef} Returns the Page's Content ElementRef
|
||||
*/
|
||||
contentRef(): ElementRef {
|
||||
return this._cntRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setContent(directive: any) {
|
||||
this._cntDir = directive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setToolbarRef(elementRef: ElementRef) {
|
||||
this._tbRefs.push(elementRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
toolbarRefs(): ElementRef[] {
|
||||
return this._tbRefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setHeader(directive: Header) {
|
||||
this._hdrDir = directive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getHeader() {
|
||||
return this._hdrDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setFooter(directive: Footer) {
|
||||
this._ftrDir = directive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getFooter() {
|
||||
return this._ftrDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {component} Returns the Page's Content component reference.
|
||||
*/
|
||||
getContent() {
|
||||
return this._cntDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setNavbar(directive: Navbar) {
|
||||
this._nbDir = directive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getNavbar() {
|
||||
return this._nbDir;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Find out if the current component has a NavBar or not. Be sure
|
||||
* to wrap this in an `ionViewWillEnter` method in order to make sure
|
||||
* the view has rendered fully.
|
||||
* @returns {boolean} Returns a boolean if this Page has a navbar or not.
|
||||
*/
|
||||
hasNavbar(): boolean {
|
||||
return !!this.getNavbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
navbarRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getElementRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
titleRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getTitleRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
navbarItemRefs(): Array<ElementRef> {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getItemRefs();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
backBtnRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getBackButtonRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
backBtnTextRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getBackButtonTextRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
navbarBgRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getBackgroundRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the title of the back-button. Be sure to call this
|
||||
* after `ionViewWillEnter` to make sure the DOM has been rendered.
|
||||
* @param {string} backButtonText Set the back button text.
|
||||
*/
|
||||
setBackButtonText(val: string) {
|
||||
let navbar = this.getNavbar();
|
||||
if (navbar) {
|
||||
navbar.setBackButtonText(val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the back button for the current view is visible or not. Be sure to call this
|
||||
* after `ionViewWillEnter` to make sure the DOM has been rendered.
|
||||
* @param {boolean} Set if this Page's back button should show or not.
|
||||
*/
|
||||
showBackButton(shouldShow: boolean) {
|
||||
let navbar = this.getNavbar();
|
||||
if (navbar) {
|
||||
navbar.hideBackButton = !shouldShow;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
isLoaded(): boolean {
|
||||
return this._loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* The loaded method is used to load any dynamic content/components
|
||||
* into the dom before proceeding with the transition. If a component
|
||||
* needs dynamic component loading, extending ViewController and
|
||||
* overriding this method is a good option
|
||||
* @param {function} done is a callback that must be called when async
|
||||
* loading/actions are completed
|
||||
*/
|
||||
loaded(done: (() => any)) {
|
||||
done();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The view has loaded. This event only happens once per view being
|
||||
* created. If a view leaves but is cached, then this will not
|
||||
* fire again on a subsequent viewing. This method is a good place
|
||||
* to put your setup code for the view; however, it is not the
|
||||
* recommended method to use when a view becomes active.
|
||||
*/
|
||||
fireLoaded() {
|
||||
this._loaded = true;
|
||||
ctrlFn(this, 'Loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The view is about to enter and become the active view.
|
||||
*/
|
||||
fireWillEnter() {
|
||||
if (this._cd) {
|
||||
// ensure this has been re-attached to the change detector
|
||||
this._cd.reattach();
|
||||
|
||||
// detect changes before we run any user code
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
this.willEnter.emit(null);
|
||||
ctrlFn(this, 'WillEnter');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The view has fully entered and is now the active view. This
|
||||
* will fire, whether it was the first load or loaded from the cache.
|
||||
*/
|
||||
fireDidEnter() {
|
||||
let navbar = this.getNavbar();
|
||||
navbar && navbar.didEnter();
|
||||
this.didEnter.emit(null);
|
||||
ctrlFn(this, 'DidEnter');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The view has is about to leave and no longer be the active view.
|
||||
*/
|
||||
fireWillLeave() {
|
||||
this.willLeave.emit(null);
|
||||
ctrlFn(this, 'WillLeave');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The view has finished leaving and is no longer the active view. This
|
||||
* will fire, whether it is cached or unloaded.
|
||||
*/
|
||||
fireDidLeave() {
|
||||
this.didLeave.emit(null);
|
||||
ctrlFn(this, 'DidLeave');
|
||||
|
||||
// when this is not the active page
|
||||
// we no longer need to detect changes
|
||||
this._cd && this._cd.detach();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The view is about to be destroyed and have its elements removed.
|
||||
*/
|
||||
fireWillUnload() {
|
||||
this.willUnload.emit(null);
|
||||
ctrlFn(this, 'WillUnload');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDestroy(destroyFn: Function) {
|
||||
this._destroyFn = destroyFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy() {
|
||||
this.didUnload.emit(null);
|
||||
ctrlFn(this, 'DidUnload');
|
||||
|
||||
this._destroyFn && this._destroyFn();
|
||||
this._destroyFn = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface LifeCycleEvent {
|
||||
componentType?: any;
|
||||
}
|
||||
|
||||
function ctrlFn(viewCtrl: ViewController, fnName: string) {
|
||||
if (viewCtrl.instance) {
|
||||
// deprecated warning: added 2016-06-01, beta.8
|
||||
if (viewCtrl.instance['onPage' + fnName]) {
|
||||
try {
|
||||
console.warn('onPage' + fnName + '() has been deprecated. Please rename to ionView' + fnName + '()');
|
||||
viewCtrl.instance['onPage' + fnName]();
|
||||
} catch (e) {
|
||||
console.error(viewCtrl.name + ' onPage' + fnName + ': ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// fire off ionView lifecycle instance method
|
||||
if (viewCtrl.instance['ionView' + fnName]) {
|
||||
try {
|
||||
viewCtrl.instance['ionView' + fnName]();
|
||||
} catch (e) {
|
||||
console.error(viewCtrl.name + ' ionView' + fnName + ': ' + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
import { ChangeDetectorRef, Component, ComponentResolver, ElementRef, EventEmitter, forwardRef, Input, Inject, NgZone, Optional, Output, Renderer, ViewChild, ViewEncapsulation, ViewContainerRef } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, ElementRef, EventEmitter, Input, NgZone, Optional, Output, Renderer, ViewChild, ViewEncapsulation, ViewContainerRef } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
import { DeepLinker } from '../../navigation/deep-linker';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||
import { NavOptions } from '../nav/nav-interfaces';
|
||||
import { NavControllerBase } from '../../navigation/nav-controller-base';
|
||||
import { NavOptions } from '../../navigation/nav-util';
|
||||
import { TabButton } from './tab-button';
|
||||
import { Tabs } from './tabs';
|
||||
import { ViewController } from '../nav/view-controller';
|
||||
import { TransitionController } from '../../transitions/transition-controller';
|
||||
import { ViewController } from '../../navigation/view-controller';
|
||||
|
||||
|
||||
/**
|
||||
@@ -93,14 +95,14 @@ import { ViewController } from '../nav/view-controller';
|
||||
* the tabs.
|
||||
*
|
||||
* ```html
|
||||
* <ion-tabs preloadTabs="false">
|
||||
* <ion-tabs>
|
||||
* <ion-tab (ionSelect)="chat()"></ion-tab>
|
||||
* </ion-tabs>
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* export class Tabs {
|
||||
* constructor(private modalCtrl: ModalController) {
|
||||
* constructor(public modalCtrl: ModalController) {
|
||||
*
|
||||
* }
|
||||
*
|
||||
@@ -120,23 +122,40 @@ import { ViewController } from '../nav/view-controller';
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-tab',
|
||||
template:
|
||||
'<div #viewport></div><div class="nav-decor"></div>',
|
||||
host: {
|
||||
'[class.show-tab]': 'isSelected',
|
||||
'[attr.id]': '_tabId',
|
||||
'[attr.aria-labelledby]': '_btnId',
|
||||
'role': 'tabpanel'
|
||||
},
|
||||
template: '<div #viewport></div><div class="nav-decor"></div>',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Tab extends NavControllerBase {
|
||||
private _isInitial: boolean;
|
||||
private _isEnabled: boolean = true;
|
||||
private _isShown: boolean = true;
|
||||
private _tabId: string;
|
||||
private _btnId: string;
|
||||
private _loaded: boolean;
|
||||
private _loadTmr: any;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_isInitial: boolean;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_isEnabled: boolean = true;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_isShown: boolean = true;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_tabId: string;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_btnId: string;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_loaded: boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -158,6 +177,11 @@ export class Tab extends NavControllerBase {
|
||||
*/
|
||||
@Input() rootParams: any;
|
||||
|
||||
/**
|
||||
* @input {string} The URL path name to represent this tab within the URL.
|
||||
*/
|
||||
@Input() tabUrlPath: string;
|
||||
|
||||
/**
|
||||
* @input {string} The title of the tab button.
|
||||
*/
|
||||
@@ -221,21 +245,23 @@ export class Tab extends NavControllerBase {
|
||||
@Output() ionSelect: EventEmitter<Tab> = new EventEmitter<Tab>();
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => Tabs)) public parent: Tabs,
|
||||
parent: Tabs,
|
||||
app: App,
|
||||
config: Config,
|
||||
keyboard: Keyboard,
|
||||
elementRef: ElementRef,
|
||||
zone: NgZone,
|
||||
renderer: Renderer,
|
||||
compiler: ComponentResolver,
|
||||
cfr: ComponentFactoryResolver,
|
||||
private _cd: ChangeDetectorRef,
|
||||
gestureCtrl: GestureController
|
||||
gestureCtrl: GestureController,
|
||||
transCtrl: TransitionController,
|
||||
@Optional() private linker: DeepLinker
|
||||
) {
|
||||
// A Tab is a NavController for its child pages
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, cfr, gestureCtrl, transCtrl, linker);
|
||||
|
||||
parent.add(this);
|
||||
this.id = parent.add(this);
|
||||
|
||||
this._tabId = 'tabpanel-' + this.id;
|
||||
this._btnId = 'tab-' + this.id;
|
||||
@@ -261,52 +287,33 @@ export class Tab extends NavControllerBase {
|
||||
*/
|
||||
load(opts: NavOptions, done?: Function) {
|
||||
if (!this._loaded && this.root) {
|
||||
this.push(this.root, this.rootParams, opts, () => {
|
||||
done(true);
|
||||
});
|
||||
this.push(this.root, this.rootParams, opts, done);
|
||||
this._loaded = true;
|
||||
|
||||
} else {
|
||||
done(false);
|
||||
done(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
preload(wait: number) {
|
||||
this._loadTmr = setTimeout(() => {
|
||||
if (!this._loaded) {
|
||||
console.debug('Tabs, preload', this.id);
|
||||
this.load({
|
||||
animate: false,
|
||||
preload: true
|
||||
}, function(){});
|
||||
}
|
||||
}, wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
loadPage(viewCtrl: ViewController, viewport: ViewContainerRef, opts: NavOptions, done: Function) {
|
||||
let isTabSubPage = (this.parent.subPages && viewCtrl.index > 0);
|
||||
_viewInsert(viewCtrl: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
||||
const isTabSubPage = (this.parent._subPages && viewCtrl.index > 0);
|
||||
|
||||
if (isTabSubPage) {
|
||||
viewport = this.parent.portal;
|
||||
}
|
||||
|
||||
super.loadPage(viewCtrl, viewport, opts, () => {
|
||||
if (isTabSubPage) {
|
||||
// add the .tab-subpage css class to tabs pages that should act like subpages
|
||||
let pageEleRef = viewCtrl.pageRef();
|
||||
if (pageEleRef) {
|
||||
this._renderer.setElementClass(pageEleRef.nativeElement, 'tab-subpage', true);
|
||||
}
|
||||
super._viewInsert(viewCtrl, componentRef, viewport);
|
||||
|
||||
if (isTabSubPage) {
|
||||
// add the .tab-subpage css class to tabs pages that should act like subpages
|
||||
const pageEleRef = viewCtrl.pageRef();
|
||||
if (pageEleRef) {
|
||||
this._renderer.setElementClass(pageEleRef.nativeElement, 'tab-subpage', true);
|
||||
}
|
||||
done();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,6 +322,9 @@ export class Tab extends NavControllerBase {
|
||||
setSelected(isSelected: boolean) {
|
||||
this.isSelected = isSelected;
|
||||
|
||||
this.setElementClass('show-tab', isSelected);
|
||||
this.setElementAttribute('aria-hidden', (!isSelected).toString());
|
||||
|
||||
if (isSelected) {
|
||||
// this is the selected tab, detect changes
|
||||
this._cd.reattach();
|
||||
@@ -335,9 +345,18 @@ export class Tab extends NavControllerBase {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
clearTimeout(this._loadTmr);
|
||||
super.ngOnDestroy();
|
||||
updateHref(component: any, data: any) {
|
||||
if (this.btn && this.linker) {
|
||||
let href = this.linker.createUrl(this, component, data) || '#';
|
||||
this.btn.updateHref(href);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import { Component, ElementRef, EventEmitter, Input, Output, Optional, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
import { NgClass, NgFor, NgIf } from '@angular/common';
|
||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output, Optional, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Badge } from '../badge/badge';
|
||||
import { Config } from '../../config/config';
|
||||
import { Content } from '../content/content';
|
||||
import { Icon } from '../icon/icon';
|
||||
import { DeepLinker } from '../../navigation/deep-linker';
|
||||
import { Ion } from '../ion';
|
||||
import { isBlank, isPresent, isTrueProperty } from '../../util/util';
|
||||
import { isBlank } from '../../util/util';
|
||||
import { nativeRaf } from '../../util/dom';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||
import { NavOptions, DIRECTION_FORWARD } from '../nav/nav-interfaces';
|
||||
import { NavController } from '../../navigation/nav-controller';
|
||||
import { NavControllerBase } from '../../navigation/nav-controller-base';
|
||||
import { NavOptions, DIRECTION_SWITCH } from '../../navigation/nav-util';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { Tab } from './tab';
|
||||
import { TabButton } from './tab-button';
|
||||
import { TabHighlight } from './tab-highlight';
|
||||
import { ViewController } from '../nav/view-controller';
|
||||
import { ViewController } from '../../navigation/view-controller';
|
||||
|
||||
|
||||
/**
|
||||
@@ -143,72 +140,61 @@ import { ViewController } from '../nav/view-controller';
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-tabs',
|
||||
template: `
|
||||
<ion-tabbar role="tablist" #tabbar>
|
||||
<a *ngFor="let t of _tabs" [tab]="t" class="tab-button" [class.tab-disabled]="!t.enabled" [class.tab-hidden]="!t.show" role="tab" href="#" (ionSelect)="select($event)">
|
||||
<ion-icon *ngIf="t.tabIcon" [name]="t.tabIcon" [isActive]="t.isSelected" class="tab-button-icon"></ion-icon>
|
||||
<span *ngIf="t.tabTitle" class="tab-button-text">{{t.tabTitle}}</span>
|
||||
<ion-badge *ngIf="t.tabBadge" class="tab-badge" [ngClass]="\'badge-\' + t.tabBadgeStyle">{{t.tabBadge}}</ion-badge>
|
||||
<ion-button-effect></ion-button-effect>
|
||||
</a>
|
||||
<tab-highlight></tab-highlight>
|
||||
</ion-tabbar>
|
||||
<ng-content></ng-content>
|
||||
<div #portal tab-portal></div>
|
||||
`,
|
||||
directives: [Badge, Icon, NgClass, NgFor, NgIf, TabButton, TabHighlight],
|
||||
template:
|
||||
'<div class="tabbar" role="tablist" #tabbar>' +
|
||||
'<a *ngFor="let t of _tabs" [tab]="t" class="tab-button" [class.tab-disabled]="!t.enabled" [class.tab-hidden]="!t.show" role="tab" href="#" (ionSelect)="select($event)">' +
|
||||
'<ion-icon *ngIf="t.tabIcon" [name]="t.tabIcon" [isActive]="t.isSelected" class="tab-button-icon"></ion-icon>' +
|
||||
'<span *ngIf="t.tabTitle" class="tab-button-text">{{t.tabTitle}}</span>' +
|
||||
'<ion-badge *ngIf="t.tabBadge" class="tab-badge" [ngClass]="\'badge-\' + t.tabBadgeStyle">{{t.tabBadge}}</ion-badge>' +
|
||||
'<div class="button-effect"></div>' +
|
||||
'</a>' +
|
||||
'<div class="tab-highlight"></div>' +
|
||||
'</div>' +
|
||||
'<ng-content></ng-content>' +
|
||||
'<div #portal tab-portal></div>',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Tabs extends Ion {
|
||||
private _ids: number = -1;
|
||||
private _tabs: Tab[] = [];
|
||||
private _onReady: any = null;
|
||||
private _sbPadding: boolean;
|
||||
private _top: number;
|
||||
private _bottom: number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
selectHistory: string[] = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
subPages: boolean;
|
||||
|
||||
export class Tabs extends Ion implements AfterViewInit {
|
||||
/** @internal */
|
||||
_color: string;
|
||||
_ids: number = -1;
|
||||
/** @internal */
|
||||
_tabs: Tab[] = [];
|
||||
/** @internal */
|
||||
_sbPadding: boolean;
|
||||
/** @internal */
|
||||
_top: number;
|
||||
/** @internal */
|
||||
_bottom: number;
|
||||
/** @internal */
|
||||
id: string;
|
||||
/** @internal */
|
||||
_selectHistory: string[] = [];
|
||||
/** @internal */
|
||||
_subPages: boolean;
|
||||
|
||||
/**
|
||||
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
|
||||
*/
|
||||
@Input()
|
||||
get color(): string {
|
||||
return this._color;
|
||||
set color(value: string) {
|
||||
this._setColor('tabs', value);
|
||||
}
|
||||
|
||||
set color(value: string) {
|
||||
this._updateColor(value);
|
||||
/**
|
||||
* @input {string} The mode to apply to this component.
|
||||
*/
|
||||
@Input()
|
||||
set mode(val: string) {
|
||||
this._setMode('tabs', val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {number} The default selected tab index when first loaded. If a selected index isn't provided then it will use `0`, the first tab.
|
||||
*/
|
||||
@Input() selectedIndex: any;
|
||||
@Input() selectedIndex: number;
|
||||
|
||||
/**
|
||||
* @input {boolean} Set whether to preload all the tabs: `true`, `false`.
|
||||
*/
|
||||
@Input() preloadTabs: any;
|
||||
|
||||
/**
|
||||
* @private DEPRECATED. Please use `tabsLayout` instead.
|
||||
* @internal DEPRECATED. Please use `tabsLayout` instead.
|
||||
*/
|
||||
@Input() private tabbarLayout: string;
|
||||
|
||||
@@ -218,7 +204,7 @@ export class Tabs extends Ion {
|
||||
@Input() tabsLayout: string;
|
||||
|
||||
/**
|
||||
* @private DEPRECATED. Please use `tabsPlacement` instead.
|
||||
* @internal DEPRECATED. Please use `tabsPlacement` instead.
|
||||
*/
|
||||
@Input() private tabbarPlacement: string;
|
||||
|
||||
@@ -238,17 +224,17 @@ export class Tabs extends Ion {
|
||||
@Output() ionChange: EventEmitter<Tab> = new EventEmitter<Tab>();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
@ViewChild(TabHighlight) private _highlight: TabHighlight;
|
||||
@ViewChild(TabHighlight) _highlight: TabHighlight;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
@ViewChild('tabbar') private _tabbar: ElementRef;
|
||||
@ViewChild('tabbar') _tabbar: ElementRef;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
@ViewChild('portal', {read: ViewContainerRef}) portal: ViewContainerRef;
|
||||
|
||||
@@ -261,29 +247,31 @@ export class Tabs extends Ion {
|
||||
@Optional() parent: NavController,
|
||||
@Optional() public viewCtrl: ViewController,
|
||||
private _app: App,
|
||||
private _config: Config,
|
||||
private _elementRef: ElementRef,
|
||||
config: Config,
|
||||
elementRef: ElementRef,
|
||||
private _platform: Platform,
|
||||
private _renderer: Renderer
|
||||
renderer: Renderer,
|
||||
private _linker: DeepLinker
|
||||
) {
|
||||
super(_elementRef);
|
||||
super(config, elementRef, renderer);
|
||||
|
||||
this.mode = config.get('mode');
|
||||
this.parent = <NavControllerBase>parent;
|
||||
this.id = 't' + (++tabIds);
|
||||
this._sbPadding = _config.getBoolean('statusbarPadding');
|
||||
this.subPages = _config.getBoolean('tabsHideOnSubPages');
|
||||
this.tabsHighlight = _config.getBoolean('tabsHighlight');
|
||||
this._sbPadding = config.getBoolean('statusbarPadding');
|
||||
this._subPages = config.getBoolean('tabsHideOnSubPages');
|
||||
this.tabsHighlight = config.getBoolean('tabsHighlight');
|
||||
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
if (_config.get('tabSubPages') !== null) {
|
||||
if (config.get('tabSubPages') !== null) {
|
||||
console.warn('Config option "tabSubPages" has been deprecated. Please use "tabsHideOnSubPages" instead.');
|
||||
this.subPages = _config.getBoolean('tabSubPages');
|
||||
this._subPages = config.getBoolean('tabSubPages');
|
||||
}
|
||||
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
if (_config.get('tabbarHighlight') !== null) {
|
||||
if (config.get('tabbarHighlight') !== null) {
|
||||
console.warn('Config option "tabbarHighlight" has been deprecated. Please use "tabsHighlight" instead.');
|
||||
this.tabsHighlight = _config.getBoolean('tabbarHighlight');
|
||||
this.tabsHighlight = config.getBoolean('tabbarHighlight');
|
||||
}
|
||||
|
||||
if (this.parent) {
|
||||
@@ -297,24 +285,20 @@ export class Tabs extends Ion {
|
||||
|
||||
} else if (this._app) {
|
||||
// this is the root navcontroller for the entire app
|
||||
this._app.setRootNav(this);
|
||||
this._app._setRootNav(this);
|
||||
}
|
||||
|
||||
// Tabs may also be an actual ViewController which was navigated to
|
||||
// if Tabs is static and not navigated to within a NavController
|
||||
// then skip this and don't treat it as it's own ViewController
|
||||
if (viewCtrl) {
|
||||
viewCtrl.setContent(this);
|
||||
viewCtrl.setContentRef(_elementRef);
|
||||
|
||||
viewCtrl.loaded = (done) => {
|
||||
this._onReady = done;
|
||||
};
|
||||
viewCtrl._setContent(this);
|
||||
viewCtrl._setContentRef(elementRef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
this._setConfig('tabsPlacement', 'bottom');
|
||||
@@ -328,27 +312,27 @@ export class Tabs extends Ion {
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
if (this.tabbarPlacement !== undefined) {
|
||||
console.warn('Input "tabbarPlacement" has been deprecated. Please use "tabsPlacement" instead.');
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'tabsPlacement', this.tabbarPlacement);
|
||||
this.setElementAttribute('tabsPlacement', this.tabbarPlacement);
|
||||
this.tabsPlacement = this.tabbarPlacement;
|
||||
}
|
||||
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
if (this._config.get('tabbarPlacement') !== null) {
|
||||
console.warn('Config option "tabbarPlacement" has been deprecated. Please use "tabsPlacement" instead.');
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'tabsPlacement', this._config.get('tabbarPlacement'));
|
||||
this.setElementAttribute('tabsPlacement', this._config.get('tabbarPlacement'));
|
||||
}
|
||||
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
if (this.tabbarLayout !== undefined) {
|
||||
console.warn('Input "tabbarLayout" has been deprecated. Please use "tabsLayout" instead.');
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'tabsLayout', this.tabbarLayout);
|
||||
this.setElementAttribute('tabsLayout', this.tabbarLayout);
|
||||
this.tabsLayout = this.tabbarLayout;
|
||||
}
|
||||
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
if (this._config.get('tabbarLayout') !== null) {
|
||||
console.warn('Config option "tabbarLayout" has been deprecated. Please use "tabsLayout" instead.');
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'tabsLayout', this._config.get('tabsLayout'));
|
||||
this.setElementAttribute('tabsLayout', this._config.get('tabsLayout'));
|
||||
}
|
||||
|
||||
if (this.tabsHighlight) {
|
||||
@@ -361,12 +345,19 @@ export class Tabs extends Ion {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
initTabs() {
|
||||
// get the selected index from the input
|
||||
// otherwise default it to use the first index
|
||||
let selectedIndex = (isBlank(this.selectedIndex) ? 0 : parseInt(this.selectedIndex, 10));
|
||||
let selectedIndex = (isBlank(this.selectedIndex) ? 0 : parseInt(<any>this.selectedIndex, 10));
|
||||
|
||||
// now see if the deep linker can find a tab index
|
||||
const tabsSegment = this._linker.initNav(this);
|
||||
if (tabsSegment && isBlank(tabsSegment.component)) {
|
||||
// we found a segment which probably represents which tab to select
|
||||
selectedIndex = this._linker.getSelectedTabIndex(this, tabsSegment.name, selectedIndex);
|
||||
}
|
||||
|
||||
// get the selectedIndex and ensure it isn't hidden or disabled
|
||||
let selectedTab = this._tabs.find((t, i) => i === selectedIndex && t.enabled && t.show);
|
||||
@@ -378,91 +369,73 @@ export class Tabs extends Ion {
|
||||
|
||||
if (selectedTab) {
|
||||
// we found a tab to select
|
||||
this.select(selectedTab);
|
||||
}
|
||||
|
||||
// check if preloadTab is set as an input @Input
|
||||
// otherwise check the preloadTabs config
|
||||
let shouldPreloadTabs = (isBlank(this.preloadTabs) ? this._config.getBoolean('preloadTabs') : isTrueProperty(this.preloadTabs));
|
||||
if (shouldPreloadTabs) {
|
||||
// preload all the tabs which isn't the selected tab
|
||||
this._tabs.filter((t) => t !== selectedTab).forEach((tab, index) => {
|
||||
tab.preload(this._config.getNumber('tabsPreloadDelay', 1000) * index);
|
||||
// get the segment the deep linker says this tab should load with
|
||||
let pageId: string = null;
|
||||
if (tabsSegment) {
|
||||
let selectedTabSegment = this._linker.initNav(selectedTab);
|
||||
if (selectedTabSegment && selectedTabSegment.component) {
|
||||
selectedTab.root = selectedTabSegment.component;
|
||||
selectedTab.rootParams = selectedTabSegment.data;
|
||||
pageId = selectedTabSegment.id;
|
||||
}
|
||||
}
|
||||
this.select(selectedTab, {
|
||||
id: pageId
|
||||
});
|
||||
}
|
||||
|
||||
// set the initial href attribute values for each tab
|
||||
this._tabs.forEach(t => {
|
||||
t.updateHref(t.root, t.rootParams);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
private _setConfig(attrKey: string, fallback: any) {
|
||||
var val = (<any>this)[attrKey];
|
||||
_setConfig(attrKey: string, fallback: any) {
|
||||
let val = (<any>this)[attrKey];
|
||||
if (isBlank(val)) {
|
||||
val = this._config.get(attrKey, fallback);
|
||||
}
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, attrKey, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_updateColor(newColor: string) {
|
||||
this._setElementColor(this._color, false);
|
||||
this._setElementColor(newColor, true);
|
||||
this._color = newColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_setElementColor(color: string, isAdd: boolean) {
|
||||
if (color !== null && color !== '') {
|
||||
this._renderer.setElementClass(this._elementRef.nativeElement, `tabs-${color}`, isAdd);
|
||||
}
|
||||
this.setElementAttribute(attrKey, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
add(tab: Tab) {
|
||||
tab.id = this.id + '-' + (++this._ids);
|
||||
this._tabs.push(tab);
|
||||
return this.id + '-' + (++this._ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number|Tab} tabOrIndex Index, or the Tab instance, of the tab to select.
|
||||
*/
|
||||
select(tabOrIndex: number | Tab, opts: NavOptions = {}, done?: Function): Promise<any> {
|
||||
let promise: Promise<any>;
|
||||
if (!done) {
|
||||
promise = new Promise(res => { done = res; });
|
||||
}
|
||||
|
||||
let selectedTab: Tab = (typeof tabOrIndex === 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex);
|
||||
select(tabOrIndex: number | Tab, opts: NavOptions = {}) {
|
||||
const selectedTab: Tab = (typeof tabOrIndex === 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex);
|
||||
if (isBlank(selectedTab)) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let deselectedTab = this.getSelected();
|
||||
const deselectedTab = this.getSelected();
|
||||
if (selectedTab === deselectedTab) {
|
||||
// no change
|
||||
this._touchActive(selectedTab);
|
||||
return Promise.resolve();
|
||||
return this._touchActive(selectedTab);
|
||||
}
|
||||
console.debug(`Tabs, select: ${selectedTab.id}`);
|
||||
|
||||
let deselectedPage: ViewController;
|
||||
if (deselectedTab) {
|
||||
deselectedPage = deselectedTab.getActive();
|
||||
deselectedPage && deselectedPage.fireWillLeave();
|
||||
deselectedPage && deselectedPage._willLeave();
|
||||
}
|
||||
|
||||
opts.animate = false;
|
||||
|
||||
let selectedPage = selectedTab.getActive();
|
||||
selectedPage && selectedPage.fireWillEnter();
|
||||
const selectedPage = selectedTab.getActive();
|
||||
selectedPage && selectedPage._willEnter();
|
||||
|
||||
selectedTab.load(opts, (initialLoad: boolean) => {
|
||||
selectedTab.load(opts, (alreadyLoaded: boolean) => {
|
||||
selectedTab.ionSelect.emit(selectedTab);
|
||||
this.ionChange.emit(selectedTab);
|
||||
|
||||
@@ -478,27 +451,26 @@ export class Tabs extends Ion {
|
||||
if (this.tabsHighlight) {
|
||||
this._highlight.select(selectedTab);
|
||||
}
|
||||
|
||||
if (opts.updateUrl !== false) {
|
||||
this._linker.navChange(DIRECTION_SWITCH);
|
||||
}
|
||||
}
|
||||
|
||||
selectedPage && selectedPage.fireDidEnter();
|
||||
deselectedPage && deselectedPage.fireDidLeave();
|
||||
|
||||
if (this._onReady) {
|
||||
this._onReady();
|
||||
this._onReady = null;
|
||||
}
|
||||
selectedPage && selectedPage._didEnter();
|
||||
deselectedPage && deselectedPage._didLeave();
|
||||
|
||||
// track the order of which tabs have been selected, by their index
|
||||
// do not track if the tab index is the same as the previous
|
||||
if (this.selectHistory[this.selectHistory.length - 1] !== selectedTab.id) {
|
||||
this.selectHistory.push(selectedTab.id);
|
||||
if (this._selectHistory[this._selectHistory.length - 1] !== selectedTab.id) {
|
||||
this._selectHistory.push(selectedTab.id);
|
||||
}
|
||||
|
||||
// if this is not the Tab's initial load then we need
|
||||
// to refresh the tabbar and content dimensions to be sure
|
||||
// they're lined up correctly
|
||||
if (!initialLoad && selectedPage) {
|
||||
var content = <Content>selectedPage.getContent();
|
||||
if (alreadyLoaded && selectedPage) {
|
||||
let content = <Content>selectedPage.getContent();
|
||||
if (content && content instanceof Content) {
|
||||
nativeRaf(() => {
|
||||
content.readDimensions();
|
||||
@@ -506,11 +478,7 @@ export class Tabs extends Ion {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,12 +489,12 @@ export class Tabs extends Ion {
|
||||
previousTab(trimHistory: boolean = true): Tab {
|
||||
// walk backwards through the tab selection history
|
||||
// and find the first previous tab that is enabled and shown
|
||||
console.debug('run previousTab', this.selectHistory);
|
||||
for (var i = this.selectHistory.length - 2; i >= 0; i--) {
|
||||
var tab = this._tabs.find(t => t.id === this.selectHistory[i]);
|
||||
console.debug('run previousTab', this._selectHistory);
|
||||
for (var i = this._selectHistory.length - 2; i >= 0; i--) {
|
||||
var tab = this._tabs.find(t => t.id === this._selectHistory[i]);
|
||||
if (tab && tab.enabled && tab.show) {
|
||||
if (trimHistory) {
|
||||
this.selectHistory.splice(i + 1);
|
||||
this._selectHistory.splice(i + 1);
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
@@ -540,17 +508,14 @@ export class Tabs extends Ion {
|
||||
* @returns {Tab} Returns the tab who's index matches the one passed
|
||||
*/
|
||||
getByIndex(index: number): Tab {
|
||||
if (index < this._tabs.length && index > -1) {
|
||||
return this._tabs[index];
|
||||
}
|
||||
return null;
|
||||
return this._tabs[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Tab} Returns the currently selected tab
|
||||
*/
|
||||
getSelected(): Tab {
|
||||
for (let i = 0; i < this._tabs.length; i++) {
|
||||
for (var i = 0; i < this._tabs.length; i++) {
|
||||
if (this._tabs[i].isSelected) {
|
||||
return this._tabs[i];
|
||||
}
|
||||
@@ -559,68 +524,57 @@ export class Tabs extends Ion {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
getActiveChildNav() {
|
||||
return this.getSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
getIndex(tab: Tab): number {
|
||||
return this._tabs.indexOf(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
*/
|
||||
length(): number {
|
||||
return this._tabs.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* "Touch" the active tab, going back to the root view of the tab
|
||||
* or optionally letting the tab handle the event
|
||||
*/
|
||||
private _touchActive(tab: Tab) {
|
||||
let active = tab.getActive();
|
||||
const active = tab.getActive();
|
||||
|
||||
if (!active) {
|
||||
return Promise.resolve();
|
||||
if (active) {
|
||||
if (active._cmp && active._cmp.instance.ionSelected) {
|
||||
// if they have a custom tab selected handler, call it
|
||||
active._cmp.instance.ionSelected();
|
||||
|
||||
} else if (tab.length() > 1) {
|
||||
// if we're a few pages deep, pop to root
|
||||
tab.popToRoot(null, null);
|
||||
|
||||
} else if (tab.root !== active.component) {
|
||||
// Otherwise, if the page we're on is not our real root, reset it to our
|
||||
// default root type
|
||||
tab.setRoot(tab.root);
|
||||
}
|
||||
}
|
||||
|
||||
let instance = active.instance;
|
||||
|
||||
// If they have a custom tab selected handler, call it
|
||||
if (instance.ionSelected) {
|
||||
return instance.ionSelected();
|
||||
}
|
||||
|
||||
// If we're a few pages deep, pop to root
|
||||
if (tab.length() > 1) {
|
||||
// Pop to the root view
|
||||
return tab.popToRoot();
|
||||
}
|
||||
|
||||
// Otherwise, if the page we're on is not our real root, reset it to our
|
||||
// default root type
|
||||
if (tab.root !== active.componentType) {
|
||||
return tab.setRoot(tab.root);
|
||||
}
|
||||
|
||||
// And failing all of that, we do something safe and secure
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @internal
|
||||
* DOM WRITE
|
||||
*/
|
||||
setTabbarPosition(top: number, bottom: number) {
|
||||
if (this._top !== top || this._bottom !== bottom) {
|
||||
let tabbarEle = <HTMLElement>this._tabbar.nativeElement;
|
||||
const tabbarEle = <HTMLElement>this._tabbar.nativeElement;
|
||||
tabbarEle.style.top = (top > -1 ? top + 'px' : '');
|
||||
tabbarEle.style.bottom = (bottom > -1 ? bottom + 'px' : '');
|
||||
tabbarEle.classList.add('show-tabbar');
|
||||
|
||||
Reference in New Issue
Block a user