diff --git a/src/components/app/app.ts b/src/components/app/app.ts index c1adb6cda5..cc027d32bf 100644 --- a/src/components/app/app.ts +++ b/src/components/app/app.ts @@ -7,16 +7,14 @@ import {Platform} from '../../platform/platform'; /** - * App utility service. Allows you to look up components that have been - * registered using the [Id directive](../Id/). + * Ionic App utility service. */ @Injectable() -export class IonicApp { +export class App { private _disTime: number = 0; private _scrollTime: number = 0; private _title: string = ''; private _titleSrv: Title = new Title(); - private _isProd: boolean = false; private _rootNav: any = null; private _appInjector: Injector; @@ -48,23 +46,6 @@ export class IonicApp { } } - /** - * Returns if the app has been set to be in be in production mode or not. - * Production mode can only be set within the config of `@App`. Defaults - * to `false`. - * @return {boolean} - */ - isProd(): boolean { - return this._isProd; - } - - /** - * @private - */ - setProd(val: boolean) { - this._isProd = !!val; - } - /** * @private * Sets if the app is currently enabled or not, meaning if it's @@ -80,9 +61,14 @@ export class IonicApp { setEnabled(isEnabled: boolean, duration: number = 700) { this._disTime = (isEnabled ? 0 : Date.now() + duration); - if (duration > 32 || isEnabled) { - // only do a click block if the duration is longer than XXms - this._clickBlock.show(!isEnabled, duration + 64); + if (this._clickBlock) { + if (duration > 32) { + // only do a click block if the duration is longer than XXms + this._clickBlock.show(true, duration + 64); + + } else { + this._clickBlock.show(false, 0); + } } } diff --git a/src/components/app/structure.scss b/src/components/app/structure.scss index e2191cfdb3..3b7a50600e 100644 --- a/src/components/app/structure.scss +++ b/src/components/app/structure.scss @@ -85,7 +85,7 @@ body { text-size-adjust: none; } -ion-app, +ion-app.app-init, ion-nav, ion-tabs { position: absolute; diff --git a/src/components/content/content.ts b/src/components/content/content.ts index 1b906db387..99c6e07576 100644 --- a/src/components/content/content.ts +++ b/src/components/content/content.ts @@ -1,7 +1,7 @@ import {Component, ElementRef, Optional, NgZone, ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core'; import {Ion} from '../ion'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {Config} from '../../config/config'; import {Keyboard} from '../../util/keyboard'; import {nativeRaf, nativeTimeout, transitionEnd} from '../../util/dom'; @@ -28,10 +28,10 @@ import {ScrollView} from '../../util/scroll-view'; * you can use Angular's `@ViewChild` annotation: * * ```ts - * import {ViewChild} from '@angular/core'; + * import {Component, ViewChild} from '@angular/core'; * import {Content} from 'ionic-angular'; * - * @Page({...} + * @Component({...}) * export class MyPage{ * @ViewChild(Content) content: Content; * @@ -67,7 +67,7 @@ export class Content extends Ion { constructor( private _elementRef: ElementRef, private _config: Config, - private _app: IonicApp, + private _app: App, private _keyboard: Keyboard, private _zone: NgZone, @Optional() viewCtrl: ViewController @@ -217,10 +217,10 @@ export class Content extends Ion { * Scroll to the specified position. * * ```ts - * import {ViewChild} from '@angular/core'; + * import {Component, ViewChild} from '@angular/core'; * import {Content} from 'ionic-angular'; * - * @Page({ + * @Component({ * template: ` * * ` @@ -248,10 +248,10 @@ export class Content extends Ion { * Scroll to the top of the content component. * * ```ts - * import {ViewChild} from '@angular/core'; + * import {Component, ViewChild} from '@angular/core'; * import {Content} from 'ionic-angular'; * - * @Page({ + * @Component({ * template: ` * * ` diff --git a/src/components/input/input-base.ts b/src/components/input/input-base.ts index 07763786b5..ed06a3c4f7 100644 --- a/src/components/input/input-base.ts +++ b/src/components/input/input-base.ts @@ -5,7 +5,7 @@ import {Config} from '../../config/config'; import {Content} from '../content/content'; import {Form} from '../../util/form'; import {Item} from '../item/item'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {isTrueProperty} from '../../util/util'; import {Label} from '../label/label'; import {pointerCoord, hasPointerMoved, closest, copyInputAttributes} from '../../util/dom'; @@ -41,7 +41,7 @@ export class InputBase { config: Config, protected _form: Form, protected _item: Item, - protected _app: IonicApp, + protected _app: App, protected _platform: Platform, protected _elementRef: ElementRef, protected _scrollView: Content, diff --git a/src/components/input/input.ts b/src/components/input/input.ts index c45572e0f8..386c460011 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -5,7 +5,7 @@ import {Config} from '../../config/config'; import {Content} from '../content/content'; import {Form} from '../../util/form'; import {InputBase} from './input-base'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {Item} from '../item/item'; import {Label} from '../label/label'; import {NativeInput, NextInput} from './native-input'; @@ -79,7 +79,7 @@ export class TextInput extends InputBase { config: Config, form: Form, @Optional() item: Item, - app: IonicApp, + app: App, platform: Platform, elementRef: ElementRef, @Optional() scrollView: Content, @@ -170,7 +170,7 @@ export class TextArea extends InputBase { config: Config, form: Form, @Optional() item: Item, - app: IonicApp, + app: App, platform: Platform, elementRef: ElementRef, @Optional() scrollView: Content, diff --git a/src/components/modal/modal.ts b/src/components/modal/modal.ts index 63a87b5b81..82bdb4f4ba 100644 --- a/src/components/modal/modal.ts +++ b/src/components/modal/modal.ts @@ -1,11 +1,12 @@ -import {Component, ComponentRef, DynamicComponentLoader, ElementRef, ViewChild, ViewContainerRef} from '@angular/core'; +import {Component, ComponentRef, DynamicComponentLoader, ViewChild, ViewContainerRef} from '@angular/core'; -import {windowDimensions} from '../../util/dom'; -import {pascalCaseToDashCase} from '../../util/util'; -import {NavParams} from '../nav/nav-params'; -import {ViewController} from '../nav/view-controller'; +import {addSelector} from '../../config/bootstrap'; import {Animation} from '../../animations/animation'; +import {NavParams} from '../nav/nav-params'; +import {pascalCaseToDashCase} from '../../util/util'; import {Transition, TransitionOptions} from '../../transitions/transition'; +import {ViewController} from '../nav/view-controller'; +import {windowDimensions} from '../../util/dom'; /** * @name Modal @@ -35,7 +36,7 @@ import {Transition, TransitionOptions} from '../../transitions/transition'; * ```ts * import {Page, Modal, NavController, NavParams} from 'ionic-angular'; * - * @Page(...) + * @Component(...) * class HomePage { * * constructor(nav: NavController) { @@ -49,7 +50,7 @@ import {Transition, TransitionOptions} from '../../transitions/transition'; * * } * - * @Page(...) + * @Component(...) * class Profile { * * constructor(params: NavParams) { @@ -65,9 +66,10 @@ import {Transition, TransitionOptions} from '../../transitions/transition'; * modal. * * ```ts - * import {Page, Modal, NavController, ViewController} from 'ionic-angular'; + * import {Component} from '@angular/core'; + * import {Modal, NavController, ViewController} from 'ionic-angular'; * - * @Page(...) + * @Component(...) * class HomePage { * * constructor(nav: NavController) { @@ -89,7 +91,7 @@ import {Transition, TransitionOptions} from '../../transitions/transition'; * * } * - * @Page(...) + * @Component(...) * class Profile { * * constructor(viewCtrl: ViewController) { @@ -140,7 +142,7 @@ export class Modal extends ViewController { let originalNgAfterViewInit = this.instance.ngAfterViewInit; this.instance.ngAfterViewInit = () => { - if ( originalNgAfterViewInit ) { + if (originalNgAfterViewInit) { originalNgAfterViewInit(); } this.instance.loadComponent().then( (componentRef: ComponentRef) => { @@ -163,11 +165,13 @@ export class ModalCmp { @ViewChild('viewport', {read: ViewContainerRef}) viewport: ViewContainerRef; - constructor(protected _eleRef: ElementRef, protected _loader: DynamicComponentLoader, protected _navParams: NavParams, protected _viewCtrl: ViewController) { - } + constructor(protected _loader: DynamicComponentLoader, protected _navParams: NavParams) {} loadComponent(): Promise> { - return this._loader.loadNextToLocation(this._navParams.data.componentType, this.viewport).then(componentRef => { + let componentType = this._navParams.data.componentType; + addSelector(componentType, 'ion-page'); + + return this._loader.loadNextToLocation(componentType, this.viewport).then(componentRef => { return componentRef; }); } diff --git a/src/components/modal/test/modal.spec.ts b/src/components/modal/test/modal.spec.ts index a7fa498d34..725b1b1cf5 100644 --- a/src/components/modal/test/modal.spec.ts +++ b/src/components/modal/test/modal.spec.ts @@ -1,4 +1,5 @@ -import {Modal, ModalCmp, Page, NavController, ViewController} from '../../../../src'; +import {Component} from '@angular/core'; +import {Modal, ModalCmp, NavController, ViewController} from '../../../../src'; export function run() { describe('Modal', () => { @@ -51,19 +52,19 @@ export function run() { it('should return a componentRef object after loading component', (done) => { // arrange - let mockLoader = { + let mockLoader: any = { loadNextToLocation: () => {} }; - let mockNavParams = { + let mockNavParams: any = { data: { - componentType: "myComponentType" + componentType: function mockComponentType(){} } }; let mockComponentRef = {}; spyOn(mockLoader, "loadNextToLocation").and.returnValue(Promise.resolve(mockComponentRef)); - let modalCmp = new ModalCmp(null, mockLoader, mockNavParams, null); - modalCmp.viewport = "mockViewport"; + let modalCmp = new ModalCmp(mockLoader, mockNavParams); + modalCmp.viewport = "mockViewport"; // act modalCmp.loadComponent().then(loadedComponentRef => { @@ -91,7 +92,7 @@ let componentToPresentSpy = { _ionicProjectContent: () => {}, }; -@Page({ +@Component({ template: `
` }) class ComponentToPresent{ diff --git a/src/components/nav/nav-controller.ts b/src/components/nav/nav-controller.ts index 34d3583866..a8bdab35a0 100644 --- a/src/components/nav/nav-controller.ts +++ b/src/components/nav/nav-controller.ts @@ -1,11 +1,12 @@ -import {ViewContainerRef, DynamicComponentLoader, provide, ReflectiveInjector, ResolvedReflectiveProvider, ElementRef, NgZone, Renderer, Type} from '@angular/core'; +import {ViewContainerRef, DynamicComponentLoader, provide, ReflectiveInjector, ResolvedReflectiveProvider, ElementRef, NgZone, Renderer, Type, EventEmitter} from '@angular/core'; +import {addSelector} from '../../config/bootstrap'; +import {App} from '../app/app'; import {Config} from '../../config/config'; import {Ion} from '../ion'; -import {IonicApp} from '../app/app'; +import {isBlank, pascalCaseToDashCase} from '../../util/util'; import {Keyboard} from '../../util/keyboard'; import {NavParams} from './nav-params'; -import {pascalCaseToDashCase, isBlank} from '../../util/util'; import {MenuController} from '../menu/menu-controller'; import {NavPortal} from './nav-portal'; import {SwipeBackGesture} from './swipe-back'; @@ -59,12 +60,9 @@ import {ViewController} from './view-controller'; * * * ## Page creation - * _For more information on the `@Page` decorator see the [@Page API - * reference](../../../decorators/Page/)._ - * * Pages 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 `@Page` as its first argument. The NavController then + * 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 @@ -75,10 +73,12 @@ import {ViewController} from './view-controller'; * * ## Lifecycle events * Lifecycle events are fired during various stages of navigation. They can be - * defined in any `@Page` decorated component class. + * defined in any component type which is pushed/popped from a `NavController`. * * ```ts - * @Page({ + * import {Component} from '@angular/core'; + * + * @Component({ * template: 'Hello World' * }) * class HelloWorld { @@ -171,6 +171,14 @@ export class NavController extends Ion { protected _trnsTime: number = 0; protected _views: Array = []; + pageDidLoad: EventEmitter; + pageWillEnter: EventEmitter; + pageDidEnter: EventEmitter; + pageWillLeave: EventEmitter; + pageDidLeave: EventEmitter; + pageWillUnload: EventEmitter; + pageDidUnload: EventEmitter; + /** * @private */ @@ -203,7 +211,7 @@ export class NavController extends Ion { constructor( parent: any, - protected _app: IonicApp, + protected _app: App, config: Config, protected _keyboard: Keyboard, elementRef: ElementRef, @@ -227,6 +235,14 @@ export class NavController extends Ion { this.providers = ReflectiveInjector.resolve([ provide(NavController, {useValue: this}) ]); + + this.pageDidLoad = new EventEmitter(); + this.pageWillEnter = new EventEmitter(); + this.pageDidEnter = new EventEmitter(); + this.pageWillLeave = new EventEmitter(); + this.pageDidLeave = new EventEmitter(); + this.pageWillUnload = new EventEmitter(); + this.pageDidUnload = new EventEmitter(); } /** @@ -508,6 +524,7 @@ export class NavController extends Ion { animation: enteringView.getTransitionName('back') }); + // present() always uses the root nav // start the transition return rootNav._insertViews(-1, [enteringView], opts); } @@ -819,9 +836,11 @@ export class NavController extends Ion { // Tabs can be a parent, but it is not a collection of views // only we're looking for an actual NavController w/ stack of views leavingView.fireWillLeave(); + this.pageWillLeave.emit(leavingView); return parentNav.pop(opts).then((rtnVal: boolean) => { leavingView.fireDidLeave(); + this.pageDidLeave.emit(leavingView); return rtnVal; }); } @@ -919,6 +938,7 @@ export class NavController extends Ion { // the first view to be removed, it should init leave view.state = STATE_INIT_LEAVE; view.fireWillUnload(); + this.pageWillUnload.emit(view); // from the index of the leaving view, go backwards and // find the first view that is inactive so it can be the entering @@ -952,7 +972,9 @@ export class NavController extends Ion { // apart of any transitions that will eventually happen this._views.filter(v => v.state === STATE_REMOVE).forEach(view => { view.fireWillLeave(); + this.pageWillLeave.emit(view); view.fireDidLeave(); + this.pageDidLeave.emit(view); this._views.splice(this.indexOf(view), 1); view.destroy(); }); @@ -987,6 +1009,7 @@ export class NavController extends Ion { // if no entering view then create a bogus one enteringView = new ViewController(); enteringView.fireLoaded(); + this.pageDidLoad.emit(enteringView); } /* Async steps to complete a transition @@ -1043,6 +1066,7 @@ export class NavController extends Ion { this.loadPage(enteringView, null, opts, () => { enteringView.fireLoaded(); + this.pageDidLoad.emit(enteringView); this._postRender(transId, enteringView, leavingView, isAlreadyTransitioning, opts, done); }); } @@ -1102,12 +1126,14 @@ export class NavController extends Ion { // only fire entering lifecycle if the leaving // view hasn't explicitly set not to enteringView.fireWillEnter(); + this.pageWillEnter.emit(enteringView); } if (enteringView.fireOtherLifecycles) { // only fire leaving lifecycle if the entering // view hasn't explicitly set not to leavingView.fireWillLeave(); + this.pageWillLeave.emit(leavingView); } } else { @@ -1214,12 +1240,14 @@ export class NavController extends Ion { // only fire entering lifecycle if the leaving // view hasn't explicitly set not to enteringView.fireDidEnter(); + this.pageDidEnter.emit(enteringView); } if (enteringView.fireOtherLifecycles) { // only fire leaving lifecycle if the entering // view hasn't explicitly set not to leavingView.fireDidLeave(); + this.pageDidLeave.emit(leavingView); } } @@ -1422,21 +1450,25 @@ export class NavController extends Ion { return; } + // add more providers to just this page let providers = this.providers.concat(ReflectiveInjector.resolve([ provide(ViewController, {useValue: view}), provide(NavParams, {useValue: view.getNavParams()}) ])); + // automatically set "ion-page" selector + addSelector(view.componentType, 'ion-page'); + // load the page component inside the nav this._loader.loadNextToLocation(view.componentType, this._viewport, providers).then(component => { - + // a new ComponentRef has been created // set the ComponentRef's instance to its ViewController view.setInstance(component.instance); - + // the component has been loaded, so call the view controller's loaded method to load any dependencies into the dom view.loaded( () => { - + // the ElementRef of the actual ion-page created let pageElementRef = component.location; diff --git a/src/components/nav/nav-portal.ts b/src/components/nav/nav-portal.ts index d5e25b1584..0000702832 100644 --- a/src/components/nav/nav-portal.ts +++ b/src/components/nav/nav-portal.ts @@ -1,6 +1,6 @@ import {Directive, ElementRef, Optional, NgZone, Renderer, DynamicComponentLoader, ViewContainerRef} from '@angular/core'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {Config} from '../../config/config'; import {Keyboard} from '../../util/keyboard'; import {NavController} from './nav-controller'; @@ -16,7 +16,7 @@ export class NavPortal extends NavController { constructor( @Optional() viewCtrl: ViewController, @Optional() parent: NavController, - app: IonicApp, + app: App, config: Config, keyboard: Keyboard, elementRef: ElementRef, diff --git a/src/components/nav/nav.ts b/src/components/nav/nav.ts index 3a0f7361bb..3ddb6fc92c 100644 --- a/src/components/nav/nav.ts +++ b/src/components/nav/nav.ts @@ -1,6 +1,6 @@ import {Component, ElementRef, ViewContainerRef, DynamicComponentLoader, Input, Optional, NgZone, Renderer, Type, ViewChild, ViewEncapsulation, AfterViewInit} from '@angular/core'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {Config} from '../../config/config'; import {Keyboard} from '../../util/keyboard'; import {isTrueProperty} from '../../util/util'; @@ -28,15 +28,18 @@ import {ViewController} from './view-controller'; * * @usage * ```ts - * import {GettingStartedPage} from 'getting-started'; - * @App({ - * template: `` + * import {Component} from '@angular/core'; + * import {ionicBootstrap} from 'ionic-angular'; + * import {GettingStartedPage} from './getting-started'; + * + * @Component({ + * template: `` * }) * class MyApp { - * constructor(){ - * this.rootPage = GettingStartedPage; - * } + * root = GettingStartedPage; * } + * + * ionicBootstrap(MyApp); * ``` * * ### Back Navigation @@ -117,7 +120,7 @@ export class Nav extends NavController implements AfterViewInit { constructor( @Optional() viewCtrl: ViewController, @Optional() parent: NavController, - app: IonicApp, + app: App, config: Config, keyboard: Keyboard, elementRef: ElementRef, diff --git a/src/components/navbar/navbar.ts b/src/components/navbar/navbar.ts index 71b88b1721..c71e276a4e 100644 --- a/src/components/navbar/navbar.ts +++ b/src/components/navbar/navbar.ts @@ -4,7 +4,7 @@ import {Ion} from '../ion'; import {Icon} from '../icon/icon'; import {ToolbarBase} from '../toolbar/toolbar'; import {Config} from '../../config/config'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {isTrueProperty} from '../../util/util'; import {ViewController} from '../nav/view-controller'; import {NavController} from '../nav/nav-controller'; @@ -134,7 +134,7 @@ export class Navbar extends ToolbarBase { } constructor( - private _app: IonicApp, + private _app: App, @Optional() viewCtrl: ViewController, elementRef: ElementRef, config: Config, diff --git a/src/components/tabs/tab.ts b/src/components/tabs/tab.ts index 58b32f0f2c..91401e927e 100644 --- a/src/components/tabs/tab.ts +++ b/src/components/tabs/tab.ts @@ -1,6 +1,6 @@ import {Component, Inject, forwardRef, ElementRef, NgZone, Renderer, DynamicComponentLoader, ViewContainerRef, ViewChild, Type, ViewEncapsulation, ChangeDetectorRef, EventEmitter, Input, Output} from '@angular/core'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {Config} from '../../config/config'; import {isTrueProperty} from '../../util/util'; import {Keyboard} from '../../util/keyboard'; @@ -208,7 +208,7 @@ export class Tab extends NavController { constructor( @Inject(forwardRef(() => Tabs)) parentTabs: Tabs, - app: IonicApp, + app: App, config: Config, keyboard: Keyboard, elementRef: ElementRef, diff --git a/src/components/tabs/tabs.ts b/src/components/tabs/tabs.ts index 14a3e25041..fd645debf6 100644 --- a/src/components/tabs/tabs.ts +++ b/src/components/tabs/tabs.ts @@ -1,6 +1,6 @@ import {Component, Directive, ElementRef, Optional, Host, forwardRef, ViewContainerRef, ViewChild, ViewChildren, EventEmitter, Output, Input, Renderer, ViewEncapsulation} from '@angular/core'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {Config} from '../../config/config'; import {Tab} from './tab'; import {TabButton} from './tab-button'; @@ -49,7 +49,7 @@ import {isBlank, isTrueProperty} from '../../util/util'; * * @usage * - * You can add a basic tabs template to a `@Page` using the following + * You can add a basic tabs template to a `@Component` using the following * template: * * ```html @@ -63,7 +63,7 @@ import {isBlank, isTrueProperty} from '../../util/util'; * Where `tab1Root`, `tab2Root`, and `tab3Root` are each a page: * *```ts - * @Page({ + * @Component({ * templateUrl: 'build/pages/tabs/tabs.html' * }) * export class TabsPage { @@ -223,7 +223,7 @@ export class Tabs extends Ion { constructor( @Optional() parent: NavController, @Optional() viewCtrl: ViewController, - private _app: IonicApp, + private _app: App, private _config: Config, private _elementRef: ElementRef, private _platform: Platform, diff --git a/src/components/tap-click/tap-click.ts b/src/components/tap-click/tap-click.ts index 33ee84c892..056747748f 100644 --- a/src/components/tap-click/tap-click.ts +++ b/src/components/tap-click/tap-click.ts @@ -1,6 +1,6 @@ import {Injectable, NgZone} from '@angular/core'; -import {IonicApp} from '../app/app'; +import {App} from '../app/app'; import {Config} from '../../config/config'; import {pointerCoord, hasPointerMoved} from '../../util/dom'; import {Activator} from './activator'; @@ -22,7 +22,7 @@ export class TapClick { constructor( config: Config, - private app: IonicApp, + private app: App, private zone: NgZone ) { let self = this; diff --git a/src/config/bootstrap.ts b/src/config/bootstrap.ts index 2226bfbdb5..3d79c87699 100644 --- a/src/config/bootstrap.ts +++ b/src/config/bootstrap.ts @@ -1,36 +1,81 @@ -import {provide, Provider, ComponentRef, NgZone} from '@angular/core'; +import {bootstrap} from '@angular/platform-browser-dynamic'; +import {Directive, ReflectiveInjector, Renderer, enableProdMode, ViewContainerRef, provide, PLATFORM_DIRECTIVES, ComponentRef, NgZone, DynamicComponentLoader} from '@angular/core'; import {ROUTER_PROVIDERS} from '@angular/router'; import {LocationStrategy, HashLocationStrategy} from '@angular/common'; import {HTTP_PROVIDERS} from '@angular/http'; +import {App} from '../components/app/app'; import {ClickBlock} from '../util/click-block'; import {Config} from './config'; import {Events} from '../util/events'; import {FeatureDetect} from '../util/feature-detect'; import {Form} from '../util/form'; -import {IonicApp} from '../components/app/app'; +import {IONIC_DIRECTIVES} from './directives'; +import {isPresent} from '../util/util'; import {Keyboard} from '../util/keyboard'; import {MenuController} from '../components/menu/menu-controller'; +import {nativeTimeout, closest} from '../util/dom'; import {NavRegistry} from '../components/nav/nav-registry'; import {Platform} from '../platform/platform'; -import {ready, closest} from '../util/dom'; import {ScrollView} from '../util/scroll-view'; import {TapClick} from '../components/tap-click/tap-click'; import {Translate} from '../translation/translate'; +const _reflect: any = Reflect; -/** - * @private - */ -export function ionicProviders(args: any = {}) { - let platform = new Platform(); - let navRegistry = new NavRegistry(args.pages); - var config = args.config; +export function ionicBootstrap(appRootComponent: any, customProviders?: Array, config?: any): Promise> { + // get all Ionic Providers + let providers = ionicProviders(customProviders, config); + // automatically set "ion-app" selector to users root component + addSelector(appRootComponent, 'ion-app'); + + // call angular bootstrap + return bootstrap(appRootComponent, providers).then(ngComponentRef => { + // ionic app has finished bootstrapping + return ionicPostBootstrap(ngComponentRef); + }); +} + + +export function ionicPostBootstrap(ngComponentRef: ComponentRef): ComponentRef { + //ngComponentRef.injector.get(TapClick); + let app: App = ngComponentRef.injector.get(App); + app.setAppInjector(ngComponentRef.injector); + + // prepare platform ready + let platform: Platform = ngComponentRef.injector.get(Platform); + platform.setZone(ngComponentRef.injector.get(NgZone)); + platform.prepareReady(); + + // TODO: Use Renderer + ngComponentRef.location.nativeElement.classList.add('app-init'); + + return ngComponentRef; +} + + +export function ionicProviders(customProviders?: Array, config?: any): any[] { + // add custom providers to Ionic's dev + let directives = IONIC_DIRECTIVES; + if (customProviders) { + directives.push(customProviders); + } + + // create an instance of Config if (!(config instanceof Config)) { config = new Config(config); } + // enable production mode if config set to true + if (config.getBoolean('prodMode')) { + enableProdMode(); + } + + // create an instance of Platform + let platform = new Platform(); + + // initialize platform platform.setUrl(window.location.href); platform.setUserAgent(window.navigator.userAgent); platform.setNavigatorPlatform(window.navigator.platform); @@ -38,7 +83,6 @@ export function ionicProviders(args: any = {}) { config.setPlatform(platform); let clickBlock = new ClickBlock(); - let events = new Events(); let featureDetect = new FeatureDetect(); @@ -46,51 +90,43 @@ export function ionicProviders(args: any = {}) { bindEvents(window, document, platform, events); return [ - IonicApp, + App, provide(ClickBlock, {useValue: clickBlock}), provide(Config, {useValue: config}), - provide(Platform, {useValue: platform}), - provide(FeatureDetect, {useValue: featureDetect}), provide(Events, {useValue: events}), - provide(NavRegistry, {useValue: navRegistry}), - TapClick, + provide(FeatureDetect, {useValue: featureDetect}), Form, Keyboard, MenuController, + NavRegistry, + provide(Platform, {useValue: platform}), Translate, + TapClick, + provide(PLATFORM_DIRECTIVES, {useValue: [directives], multi: true}), ROUTER_PROVIDERS, provide(LocationStrategy, {useClass: HashLocationStrategy}), HTTP_PROVIDERS, ]; } -/** - * @private - */ -export function postBootstrap(appRef: ComponentRef, prodMode: boolean) { - appRef.injector.get(TapClick); - let app: IonicApp = appRef.injector.get(IonicApp); - let platform: Platform = appRef.injector.get(Platform); - platform.setZone(appRef.injector.get(NgZone)); - platform.prepareReady(); - app.setProd(prodMode); - app.setAppInjector(appRef.injector); -} - -function setupDom(window, document, config, platform, clickBlock, featureDetect) { +function setupDom(window: Window, document: Document, config: Config, platform: Platform, clickBlock: ClickBlock, featureDetect: FeatureDetect) { let bodyEle = document.body; let mode = config.get('mode'); // if dynamic mode links have been added the fire up the correct one let modeLinkAttr = mode + '-href'; - let linkEle = document.head.querySelector('link[' + modeLinkAttr + ']'); + let linkEle = document.head.querySelector('link[' + modeLinkAttr + ']'); if (linkEle) { let href = linkEle.getAttribute(modeLinkAttr); linkEle.removeAttribute(modeLinkAttr); linkEle.href = href; } + let headStyle = document.createElement('style'); + headStyle.innerHTML = 'ion-app{display:none}'; + document.head.appendChild(headStyle); + // set the mode class name // ios/md/wp bodyEle.classList.add(mode); @@ -122,10 +158,6 @@ function setupDom(window, document, config, platform, clickBlock, featureDetect) bodyEle.classList.add('enable-hover'); } - if (config.get('clickBlock')) { - clickBlock.enable(); - } - // run feature detection tests featureDetect.run(window, document); } @@ -134,7 +166,7 @@ function setupDom(window, document, config, platform, clickBlock, featureDetect) /** * Bind some global events and publish on the 'app' channel */ -function bindEvents(window, document, platform, events) { +function bindEvents(window: Window, document: Document, platform: Platform, events: Events) { window.addEventListener('online', (ev) => { events.publish('app:online', ev); }, false); @@ -150,10 +182,10 @@ function bindEvents(window, document, platform, events) { // When that status taps, we respond window.addEventListener('statusTap', (ev) => { // TODO: Make this more better - var el = document.elementFromPoint(platform.width() / 2, platform.height() / 2); + let el = document.elementFromPoint(platform.width() / 2, platform.height() / 2); if (!el) { return; } - var content = closest(el, 'scroll-content'); + let content = closest(el, 'scroll-content'); if (content) { var scroll = new ScrollView(content); scroll.scrollTo(0, 0, 300); @@ -161,9 +193,22 @@ function bindEvents(window, document, platform, events) { }); // start listening for resizes XXms after the app starts - setTimeout(function() { - window.addEventListener('resize', function() { + nativeTimeout(() => { + window.addEventListener('resize', () => { platform.windowResize(); }); }, 2000); } + +/** + * @private + */ +export function addSelector(type: any, selector: string) { + if (type) { + let annotations = _reflect.getMetadata('annotations', type); + if (annotations && !annotations[0].selector) { + annotations[0].selector = selector; + _reflect.defineMetadata('annotations', annotations, type); + } + } +} diff --git a/src/config/test/config.spec.ts b/src/config/test/config.spec.ts index 65160debcc..ae83b77b53 100644 --- a/src/config/test/config.spec.ts +++ b/src/config/test/config.spec.ts @@ -80,7 +80,7 @@ export function run() { let userConfig = new Config({ mode: 'configInstance' }) - let providers = ionicProviders({config:userConfig}); + let providers = ionicProviders(null, userConfig); let config = providers.find(provider => provider.useValue instanceof Config).useValue; @@ -88,9 +88,9 @@ export function run() { }); it('should create new Config instance from config object in ionicProviders', () => { - let providers = ionicProviders({config: { + let providers = ionicProviders(null, { mode: 'configObj' - }}); + }); let config = providers.find(provider => provider.useValue instanceof Config).useValue; diff --git a/src/decorators/app.ts b/src/decorators/app.ts deleted file mode 100644 index c6f2efecaf..0000000000 --- a/src/decorators/app.ts +++ /dev/null @@ -1,107 +0,0 @@ -import {Component, ChangeDetectionStrategy, ViewEncapsulation, enableProdMode, Type, provide, PLATFORM_DIRECTIVES} from '@angular/core'; -import {bootstrap} from '@angular/platform-browser-dynamic'; -import {ionicProviders, postBootstrap} from '../config/bootstrap'; -import {IONIC_DIRECTIVES} from '../config/directives'; - -const _reflect: any = Reflect; - -export interface AppMetadata { - prodMode?: boolean; - selector?: string; - inputs?: string[]; - outputs?: string[]; - properties?: string[]; - events?: string[]; - host?: { - [key: string]: string; - }; - providers?: any[]; - directives?: Array; - pipes?: Array; - exportAs?: string; - queries?: { - [key: string]: any; - }; - template?: string; - templateUrl?: string; - moduleId?: string; - styleUrls?: string[]; - styles?: string[]; - changeDetection?: ChangeDetectionStrategy; - encapsulation?: ViewEncapsulation; - config?: any; -} - -/** -* @name App -* @description -* App is an Ionic decorator that bootstraps an application. It can be passed a -* number of arguments that act as global config variables for the app. -* `@App` is similar to Angular's `@Component` in which it can accept a `template` -* property that has an inline template, or a `templateUrl` property that points -* to an external html template. The `@App` decorator runs the Angular bootstrapping -* process automatically, however you can bootstrap your app separately if you prefer. -* Additionally, `@App` will automatically bootstrap with all of Ionic's -* core components, meaning they won't all have to be individually imported and added -* to each component's `directives` property. -* -* @usage -* ```ts -* import {App} from 'ionic-angular'; -* -* @App({ -* templateUrl: 'app/app.html', -* providers: [DataService] -* }) -* -* export class MyApp{ -* // Anything we would want to do at the root of our app -* } -* ``` -* -* @property {object} [config] - the app's {@link /docs/v2/api/config/Config/ Config} object. -* @property {boolean} [prodMode] - Enable Angular's production mode, which turns off assertions and other checks within the framework. Additionally, this config sets the return value of `isProd()` which is on the `IonicApp` instance. Defaults to `false`. -* @property {array} [pipes] - any pipes for your app. -* @property {array} [providers] - any providers for your app. -* @property {string} [template] - the template to use for the app root. -* @property {string} [templateUrl] - a relative URL pointing to the template to use for the app root. -*/ -export function App(args: AppMetadata = {}) { - - return function(cls) { - // get current annotations - let annotations = _reflect.getMetadata('annotations', cls) || []; - - args.selector = 'ion-app'; - - // if no template was provided, default so it has a root - if (!args.templateUrl && !args.template) { - args.template = ''; - } - - // create @Component - annotations.push(new Component(args)); - - // redefine with added annotations - _reflect.defineMetadata('annotations', annotations, cls); - - // define array of bootstrap providers - let providers = ionicProviders(args).concat(args.providers || []); - - // auto add Ionic directives - let directives = args.directives ? args.directives.concat(IONIC_DIRECTIVES) : IONIC_DIRECTIVES; - - // automatically provide all of Ionic's directives to every component - providers.push(provide(PLATFORM_DIRECTIVES, {useValue: [directives], multi: true})); - - if (args.prodMode) { - enableProdMode(); - } - - bootstrap(cls, providers).then(appRef => { - postBootstrap(appRef, args.prodMode); - }); - - return cls; - }; -} diff --git a/src/decorators/page.ts b/src/decorators/page.ts index d70e29520d..162312f581 100644 --- a/src/decorators/page.ts +++ b/src/decorators/page.ts @@ -28,41 +28,13 @@ export interface PageMetadata { } /** - * @name Page - * @description - * - * The Page decorator indicates that the decorated class is an Ionic - * navigation component, meaning it can be navigated to using a - * [NavController](../../nav/NavController). - * - * Since the app has already been bootstrapped with Ionic's core directives, it - * is not needed to include `IONIC_DIRECTIVES` in the directives property. Additionally, - * Angular's [CORE_DIRECTIVES](https://angular.io/docs/ts/latest/api/common/CORE_DIRECTIVES-let.html) - * and [FORM_DIRECTIVES](https://angular.io/docs/ts/latest/api/common/FORM_DIRECTIVES-let.html), - * are also already provided, so you only need to supply any custom components and directives - * to your pages: - * - * @usage - * - * ```ts - * @Page({ - * template: ` - * - * I am a page! - * - * ` - * }) - * class MyPage {} - * ``` - * - * Pages have their content automatically wrapped in ``, so although - * you may see these tags if you inspect your markup, you don't need to include - * them in your templates. - * - * For more information on how pages are created, see the [NavController API Docs](../../components/nav/NavController/#creating_pages) + * @private */ export function Page(config: PageMetadata) { return function(cls) { + // deprecated warning: added beta.8 2016-05-27 + console.warn('@Page decorator has been deprecated. Please use Angular\'s @Component instead.\nimport {Component} from \'@angular/core\';'); + config.selector = 'ion-page'; config.host = config.host || {}; config.host['[hidden]'] = '_hidden'; diff --git a/src/index.ts b/src/index.ts index 65c7635b7f..78d8f01acf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,6 @@ export * from './config/bootstrap'; export * from './config/config'; export * from './config/directives'; -export * from './decorators/app'; export * from './decorators/page'; export * from './components';