Files
ionic-framework/ionic/components/nav/view-controller.ts
2015-09-23 23:17:06 -05:00

413 lines
10 KiB
TypeScript

import {Component, EventEmitter, ElementRef, bind, Injector, ComponentRef} from 'angular2/angular2';
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {NavParams} from './nav-controller';
/**
* TODO
*/
export class ViewController {
constructor(navCtrl, componentType, params = {}) {
this.navCtrl = navCtrl;
this.componentType = componentType;
this.params = new NavParams(params);
this.instance = null;
this.state = 0;
this.disposals = [];
this.protos = {};
this._nbItms = [];
this._promises = [];
this.templateRefs = {};
}
/**
* TODO
* @param {TODO} name TODO
* @param {TODO} protoViewRef TODO
*/
addProtoViewRef(name, protoViewRef) {
this.protos[name] = protoViewRef;
}
/**
* TODO
* @param {TODO} name TODO
* @param {TODO} templateRef TODO
*/
addTemplateRef(name, templateRef) {
this.templateRefs[name] = templateRef;
}
/**
* TODO
* @param {Function} callback TODO
* @returns {TODO} TODO
*/
stage(callback) {
let navCtrl = this.navCtrl;
if (this.instance || !navCtrl) {
// already compiled this view
return callback();
}
let annotation = new Component({
selector: 'ion-view',
host: {
'class': 'nav-item'
}
});
let ionViewComponentType = DirectiveBinding.createFromType(this.componentType, annotation);
// create a unique token that works as a cache key
ionViewComponentType.token = 'ionView' + this.componentType.name;
// compile the Component
navCtrl.compiler.compileInHost(ionViewComponentType).then(hostProtoViewRef => {
// figure out the sturcture of this Component
// does it have a navbar? Is it tabs? Should it not have a navbar or any toolbars?
let itemStructure = this.sturcture = this.inspectStructure(hostProtoViewRef);
// get the appropriate Pane which this ViewController will fit into
navCtrl.panes.get(itemStructure, pane => {
this.pane = pane;
let bindings = navCtrl.bindings.concat(Injector.resolve([
bind(NavParams).toValue(this.params),
bind(ViewController).toValue(this)
]));
// add the content of the view to the content area
// it will already have the correct context
let contentContainer = pane.contentContainerRef;
// the same guts as DynamicComponentLoader.loadNextToLocation
var hostViewRef =
contentContainer.createHostView(hostProtoViewRef, -1, bindings);
var newLocation = navCtrl.viewMngr.getHostElement(hostViewRef);
var newComponent = navCtrl.viewMngr.getComponent(newLocation);
pane.totalItems++;
var dispose = () => {
var index = contentContainer.indexOf(hostViewRef);
if (index !== -1) {
contentContainer.remove(index);
// remove the pane if there are no view items left
pane.totalItems--;
if (pane.totalItems === 0) {
pane.dispose();
}
}
};
this.disposals.push(dispose);
var viewComponetRef = new ComponentRef(newLocation, newComponent, dispose);
// get the component's instance, and set it to the this ViewController
this.setInstance(viewComponetRef.instance);
this.viewElementRef(viewComponetRef.location);
// // get the item container's nav bar
let navbarViewContainer = navCtrl.navbarViewContainer();
// // get the item's navbar protoview
let navbarTemplateRef = this.templateRefs.navbar;
// add a navbar view if the pane has a navbar container, and the
// item's instance has a navbar protoview to go to inside of it
if (navbarViewContainer && navbarTemplateRef) {
let navbarView = navbarViewContainer.createEmbeddedView(navbarTemplateRef, -1);
this.disposals.push(() => {
let index = navbarViewContainer.indexOf(navbarView);
if (index > -1) {
navbarViewContainer.remove(index);
}
});
}
// this item has finished loading
try {
this.loaded();
} catch (e) {
console.error(e);
}
// fire callback when all child promises have been resolved
Promise.all(this._promises).then(() => {
callback();
this._promises = [];
});
}, panesErr => {
console.error(panesErr);
});
}, compileInHostErr => {
console.error(compileInHostErr);
});
}
/**
* TODO
* @param {TODO} childPromise TODO
*/
addPromise(childPromise) {
this._promises.push(childPromise);
}
/**
* TODO
* @param {TODO} componentProtoViewRef TODO
*/
inspectStructure(componentProtoViewRef) {
let navbar = false;
let key = '_';
componentProtoViewRef._protoView.elementBinders.forEach(rootElementBinder => {
if (!rootElementBinder.componentDirective || !rootElementBinder.nestedProtoView) return;
rootElementBinder.nestedProtoView.elementBinders.forEach(nestedElementBinder => {
if ( isComponent(nestedElementBinder, 'Tabs') ) {
navbar = true;
}
if (!nestedElementBinder.componentDirective && nestedElementBinder.nestedProtoView) {
nestedElementBinder.nestedProtoView.elementBinders.forEach(templatedElementBinder => {
if ( isComponent(templatedElementBinder, 'Navbar') ) {
navbar = true;
}
});
}
});
});
if (this.navCtrl.childNavbar()) {
navbar = false;
}
if (navbar) key += 'n'
return {
navbar,
key
};
}
/**
* TODO
* @returns {boolean} TODO
*/
enableBack() {
// update if it's possible to go back from this nav item
if (this.navCtrl) {
let previousItem = this.navCtrl.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 && !previousItem.shouldDestroy);
}
return false;
}
/**
* TODO
* @param {TODO} instance TODO
*/
setInstance(instance) {
this.instance = instance;
}
get index() {
return (this.navCtrl ? this.navCtrl.indexOf(this) : -1);
}
isRoot() {
return this.index === 0;
}
/**
* TODO
*/
destroy() {
for (let i = 0; i < this.disposals.length; i++) {
this.disposals[i]();
}
this.didUnload();
// just to help prevent any possible memory leaks
for (let name in this) {
if (this.hasOwnProperty(name)) {
this[name] = null;
}
}
}
/**
* TODO
* @param {TODO} val TODO
* @returns {TODO} TODO
*/
viewElementRef(val) {
if (arguments.length) {
this._vwEle = val;
}
return this._vwEle;
}
/**
* TODO
* @returns {TODO} TODO
*/
navbarView() {
if (arguments.length) {
this._nbView = arguments[0];
}
return this._nbView;
}
/**
* TODO
* @returns {TODO} TODO
*/
navbarRef() {
let navbarView = this.navbarView();
if (navbarView) {
return navbarView.getElementRef();
}
}
/**
* TODO
* @returns {TODO} TODO
*/
titleRef() {
let navbarView = this.navbarView();
if (navbarView) {
return navbarView.getTitleRef();
}
}
/**
* TODO
* @returns {TODO} TODO
*/
navbarItemRefs() {
let navbarView = this.navbarView();
if (navbarView) {
return navbarView.getItemRefs();
}
}
/**
* TODO
* @returns {TODO} TODO
*/
backBtnRef() {
let navbarView = this.navbarView();
if (navbarView) {
return navbarView.getBackButtonRef();
}
}
/**
* TODO
* @returns {TODO} TODO
*/
backBtnTextRef() {
let navbarView = this.navbarView();
if (navbarView) {
return navbarView.getBackButtonTextRef();
}
}
/**
* TODO
* @returns {TODO} TODO
*/
navbarBackgroundRef() {
let navbarView = this.navbarView();
if (navbarView) {
return navbarView.getNativeElement().querySelector('.toolbar-background');
}
}
/**
* TODO
* @returns {TODO} TODO
*/
postRender() {
// the elements are in the DOM and the browser
// has rendered them in their correct locations
}
/**
* 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.instance && this.instance.onViewLoaded && this.instance.onViewLoaded();
}
/**
* The view is about to enter and become the active view.
*/
willEnter() {
this.instance && this.instance.onViewWillEnter && this.instance.onViewWillEnter();
}
/**
* 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 navbarView = this.navbarView();
if (navbarView) {
navbarView.didEnter();
}
this.instance && this.instance.onViewDidEnter && this.instance.onViewDidEnter();
}
/**
* The view has is about to leave and no longer be the active view.
*/
willLeave() {
this.instance && this.instance.onViewWillLeave && this.instance.onViewWillLeave();
}
/**
* The view has finished leaving and is no longer the active view. This
* will fire, whether it is cached or unloaded.
*/
didLeave() {
this.instance && this.instance.onViewDidLeave && this.instance.onViewDidLeave();
}
/**
* The view is about to be destroyed and have its elements removed.
*/
willUnload() {
this.instance && this.instance.onViewWillUnload && this.instance.onViewWillUnload();
}
/**
* The view has been destroyed and its elements have been removed.
*/
didUnload() {
this.instance && this.instance.onViewDidUnload && this.instance.onViewDidUnload();
}
}
function isComponent(elementBinder, id) {
return (elementBinder && elementBinder.componentDirective && elementBinder.componentDirective.metadata.id == id);
}