mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-21 21:15:24 +08:00

this allows the user to push multiple loading indicators on top of each other without interfering with the page stack, as well as navigation between pages behind the loading indicator. references #5426
539 lines
11 KiB
TypeScript
539 lines
11 KiB
TypeScript
import {Output, EventEmitter, Type, TemplateRef, ViewContainerRef, ElementRef, Renderer} from 'angular2/core';
|
|
|
|
import {Navbar} from '../navbar/navbar';
|
|
import {NavController, NavOptions} from './nav-controller';
|
|
import {NavParams} from './nav-params';
|
|
import {isPresent} from '../../util/util';
|
|
|
|
|
|
/**
|
|
* @name ViewController
|
|
* @description
|
|
* Access various features and information about the current view
|
|
* @usage
|
|
* ```ts
|
|
* import {Page, ViewController} from 'ionic-angular';
|
|
* @Page....
|
|
* export class MyPage{
|
|
* constructor(viewCtrl: ViewController){
|
|
* this.viewCtrl = viewCtrl;
|
|
* }
|
|
* }
|
|
* ```
|
|
*/
|
|
export class ViewController {
|
|
private _cntDir: any;
|
|
private _cntRef: ElementRef;
|
|
private _destroys: Array<Function> = [];
|
|
private _hdAttr: string = null;
|
|
private _leavingOpts: NavOptions = null;
|
|
private _loaded: boolean = false;
|
|
private _nbDir: Navbar;
|
|
private _nbTmpRef: TemplateRef;
|
|
private _nbVwRef: ViewContainerRef;
|
|
private _onDismiss: Function = null;
|
|
private _pgRef: ElementRef;
|
|
protected _nav: NavController;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
data: any;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
id: string;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
instance: any = {};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
state: string = '';
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
viewType: string = '';
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
onReady: Function;
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
usePortal: boolean = false;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
zIndex: number;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
@Output() private _emitter: EventEmitter<any> = new EventEmitter();
|
|
|
|
constructor(public componentType?: Type, 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 : {});
|
|
}
|
|
|
|
subscribe(generatorOrNext?: any): any {
|
|
return this._emitter.subscribe(generatorOrNext);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
emit(data?: any) {
|
|
this._emitter.emit(data);
|
|
}
|
|
|
|
onDismiss(callback: Function) {
|
|
this._onDismiss = callback;
|
|
}
|
|
|
|
dismiss(data?: any, role?: any) {
|
|
return this._nav.remove(this._nav.indexOf(this), 1, this._leavingOpts).then(() => {
|
|
this._onDismiss && this._onDismiss(data, role);
|
|
return data;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
setNav(navCtrl: NavController) {
|
|
this._nav = navCtrl;
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
setInstance(instance: any) {
|
|
this.instance = instance;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
get name(): string {
|
|
return this.componentType ? this.componentType['name'] : '';
|
|
}
|
|
|
|
/**
|
|
* You can find out the index of the current view is in the current navigation stack
|
|
*
|
|
* ```typescript
|
|
* export class Page1 {
|
|
* constructor(view: ViewController){
|
|
* this.view = view;
|
|
* // Just log out the index
|
|
* console.log(this.view.index);
|
|
* }
|
|
* }
|
|
* ```
|
|
*
|
|
* @returns {number} Returns the index of this page within its NavController.
|
|
*/
|
|
get index(): number {
|
|
return (this._nav ? this._nav.indexOf(this) : -1);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private isRoot(): boolean {
|
|
// deprecated warning
|
|
console.warn('ViewController isRoot() has been renamed to isFirst()');
|
|
return this.isFirst();
|
|
}
|
|
|
|
/**
|
|
* @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);
|
|
|
|
let navbarRef = this.navbarRef();
|
|
if (navbarRef) {
|
|
renderer.setElementAttribute(navbarRef.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
|
|
*/
|
|
setNavbarTemplateRef(templateRef: TemplateRef) {
|
|
this._nbTmpRef = templateRef;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
getNavbarTemplateRef(): TemplateRef {
|
|
return this._nbTmpRef;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
getNavbarViewRef() {
|
|
return this._nbVwRef;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
setNavbarViewRef(viewContainerRef: ViewContainerRef) {
|
|
this._nbVwRef = viewContainerRef;
|
|
}
|
|
|
|
/**
|
|
* @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) {
|
|
this._cntDir = directive;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* You can find out of the current view has a Navbar or not. Be sure to wrap this in an `onPageWillEnter` method in order to make sure the view has rendered fully.
|
|
*
|
|
* ```typescript
|
|
* export class Page1 {
|
|
* constructor(view: ViewController) {
|
|
* this.view = view
|
|
* }
|
|
* onPageWillEnter(){
|
|
* console.log('Do we have a Navbar?', this.view.hasNavbar());
|
|
* }
|
|
*}
|
|
* ```
|
|
*
|
|
* @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();
|
|
}
|
|
|
|
/**
|
|
* You can change the text of the back button on a view-by-view basis.
|
|
*
|
|
* ```ts
|
|
* export class MyClass{
|
|
* constructor(viewCtrl: ViewController){
|
|
* this.viewCtrl = viewCtrl
|
|
* }
|
|
* onPageWillEnter() {
|
|
* this.viewCtrl.setBackButtonText('Previous');
|
|
* }
|
|
* }
|
|
* ```
|
|
* Make sure you use the view events when calling this method, otherwise the back-button will not have been created
|
|
*
|
|
* @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 wrap this in `onPageWillEnter` to make sure the has been compleltly 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;
|
|
}
|
|
|
|
/**
|
|
* @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.
|
|
*/
|
|
loaded() {
|
|
this._loaded = true;
|
|
ctrlFn(this, 'onPageLoaded');
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* The view is about to enter and become the active view.
|
|
*/
|
|
willEnter() {
|
|
ctrlFn(this, 'onPageWillEnter');
|
|
}
|
|
|
|
/**
|
|
* @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.
|
|
*/
|
|
didEnter() {
|
|
let navbar = this.getNavbar();
|
|
navbar && navbar.didEnter();
|
|
ctrlFn(this, 'onPageDidEnter');
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* The view has is about to leave and no longer be the active view.
|
|
*/
|
|
willLeave() {
|
|
ctrlFn(this, 'onPageWillLeave');
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* The view has finished leaving and is no longer the active view. This
|
|
* will fire, whether it is cached or unloaded.
|
|
*/
|
|
didLeave() {
|
|
ctrlFn(this, 'onPageDidLeave');
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* The view is about to be destroyed and have its elements removed.
|
|
*/
|
|
willUnload() {
|
|
ctrlFn(this, 'onPageWillUnload');
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
addDestroy(destroyFn: Function) {
|
|
this._destroys.push(destroyFn);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
destroy() {
|
|
ctrlFn(this, 'onPageDidUnload');
|
|
|
|
for (var i = 0; i < this._destroys.length; i++) {
|
|
this._destroys[i]();
|
|
}
|
|
this._destroys = [];
|
|
}
|
|
|
|
}
|
|
|
|
function ctrlFn(viewCtrl: ViewController, fnName: string) {
|
|
if (viewCtrl.instance && viewCtrl.instance[fnName]) {
|
|
try {
|
|
viewCtrl.instance[fnName]();
|
|
} catch(e) {
|
|
console.error(viewCtrl.name + ' ' + fnName + ': ' + e.message);
|
|
}
|
|
}
|
|
}
|