refactor(bootstrap): use ionicBootstrap() to bootstrap

BREAKING CHANGES:

- Ionic's custom decorators have been removed.
- `@App` and `@Page` should be replaced with `@Component`.
- `IonicApp` has been renamed to `App`.
- `ionicBootstrap` is required to bootstrap the app.
- Config is now the 3rd parameter in `ionicBootstrap(rootComponent,
providers, config)`.
- Property `prodMode` is now a config option, enabling or disabling
production mode.
This commit is contained in:
Adam Bradley
2016-05-31 15:42:06 -05:00
parent 4b4092b25e
commit 73635f3939
19 changed files with 210 additions and 275 deletions

View File

@ -7,16 +7,14 @@ import {Platform} from '../../platform/platform';
/** /**
* App utility service. Allows you to look up components that have been * Ionic App utility service.
* registered using the [Id directive](../Id/).
*/ */
@Injectable() @Injectable()
export class IonicApp { export class App {
private _disTime: number = 0; private _disTime: number = 0;
private _scrollTime: number = 0; private _scrollTime: number = 0;
private _title: string = ''; private _title: string = '';
private _titleSrv: Title = new Title(); private _titleSrv: Title = new Title();
private _isProd: boolean = false;
private _rootNav: any = null; private _rootNav: any = null;
private _appInjector: Injector; 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 * @private
* Sets if the app is currently enabled or not, meaning if it's * 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) { setEnabled(isEnabled: boolean, duration: number = 700) {
this._disTime = (isEnabled ? 0 : Date.now() + duration); this._disTime = (isEnabled ? 0 : Date.now() + duration);
if (duration > 32 || isEnabled) { if (this._clickBlock) {
if (duration > 32) {
// only do a click block if the duration is longer than XXms // only do a click block if the duration is longer than XXms
this._clickBlock.show(!isEnabled, duration + 64); this._clickBlock.show(true, duration + 64);
} else {
this._clickBlock.show(false, 0);
}
} }
} }

View File

@ -85,7 +85,7 @@ body {
text-size-adjust: none; text-size-adjust: none;
} }
ion-app, ion-app.app-init,
ion-nav, ion-nav,
ion-tabs { ion-tabs {
position: absolute; position: absolute;

View File

@ -1,7 +1,7 @@
import {Component, ElementRef, Optional, NgZone, ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core'; import {Component, ElementRef, Optional, NgZone, ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {Ion} from '../ion'; import {Ion} from '../ion';
import {IonicApp} from '../app/app'; import {App} from '../app/app';
import {Config} from '../../config/config'; import {Config} from '../../config/config';
import {Keyboard} from '../../util/keyboard'; import {Keyboard} from '../../util/keyboard';
import {nativeRaf, nativeTimeout, transitionEnd} from '../../util/dom'; import {nativeRaf, nativeTimeout, transitionEnd} from '../../util/dom';
@ -28,10 +28,10 @@ import {ScrollView} from '../../util/scroll-view';
* you can use Angular's `@ViewChild` annotation: * you can use Angular's `@ViewChild` annotation:
* *
* ```ts * ```ts
* import {ViewChild} from '@angular/core'; * import {Component, ViewChild} from '@angular/core';
* import {Content} from 'ionic-angular'; * import {Content} from 'ionic-angular';
* *
* @Page({...} * @Component({...})
* export class MyPage{ * export class MyPage{
* @ViewChild(Content) content: Content; * @ViewChild(Content) content: Content;
* *
@ -67,7 +67,7 @@ export class Content extends Ion {
constructor( constructor(
private _elementRef: ElementRef, private _elementRef: ElementRef,
private _config: Config, private _config: Config,
private _app: IonicApp, private _app: App,
private _keyboard: Keyboard, private _keyboard: Keyboard,
private _zone: NgZone, private _zone: NgZone,
@Optional() viewCtrl: ViewController @Optional() viewCtrl: ViewController
@ -217,10 +217,10 @@ export class Content extends Ion {
* Scroll to the specified position. * Scroll to the specified position.
* *
* ```ts * ```ts
* import {ViewChild} from '@angular/core'; * import {Component, ViewChild} from '@angular/core';
* import {Content} from 'ionic-angular'; * import {Content} from 'ionic-angular';
* *
* @Page({ * @Component({
* template: `<ion-content> * template: `<ion-content>
* <button (click)="scrollTo()">Down 500px</button> * <button (click)="scrollTo()">Down 500px</button>
* </ion-content>` * </ion-content>`
@ -248,10 +248,10 @@ export class Content extends Ion {
* Scroll to the top of the content component. * Scroll to the top of the content component.
* *
* ```ts * ```ts
* import {ViewChild} from '@angular/core'; * import {Component, ViewChild} from '@angular/core';
* import {Content} from 'ionic-angular'; * import {Content} from 'ionic-angular';
* *
* @Page({ * @Component({
* template: `<ion-content> * template: `<ion-content>
* <button (click)="scrollToTop()">Scroll to top</button> * <button (click)="scrollToTop()">Scroll to top</button>
* </ion-content>` * </ion-content>`

View File

@ -5,7 +5,7 @@ import {Config} from '../../config/config';
import {Content} from '../content/content'; import {Content} from '../content/content';
import {Form} from '../../util/form'; import {Form} from '../../util/form';
import {Item} from '../item/item'; import {Item} from '../item/item';
import {IonicApp} from '../app/app'; import {App} from '../app/app';
import {isTrueProperty} from '../../util/util'; import {isTrueProperty} from '../../util/util';
import {Label} from '../label/label'; import {Label} from '../label/label';
import {pointerCoord, hasPointerMoved, closest, copyInputAttributes} from '../../util/dom'; import {pointerCoord, hasPointerMoved, closest, copyInputAttributes} from '../../util/dom';
@ -41,7 +41,7 @@ export class InputBase {
config: Config, config: Config,
protected _form: Form, protected _form: Form,
protected _item: Item, protected _item: Item,
protected _app: IonicApp, protected _app: App,
protected _platform: Platform, protected _platform: Platform,
protected _elementRef: ElementRef, protected _elementRef: ElementRef,
protected _scrollView: Content, protected _scrollView: Content,

View File

@ -5,7 +5,7 @@ import {Config} from '../../config/config';
import {Content} from '../content/content'; import {Content} from '../content/content';
import {Form} from '../../util/form'; import {Form} from '../../util/form';
import {InputBase} from './input-base'; import {InputBase} from './input-base';
import {IonicApp} from '../app/app'; import {App} from '../app/app';
import {Item} from '../item/item'; import {Item} from '../item/item';
import {Label} from '../label/label'; import {Label} from '../label/label';
import {NativeInput, NextInput} from './native-input'; import {NativeInput, NextInput} from './native-input';
@ -79,7 +79,7 @@ export class TextInput extends InputBase {
config: Config, config: Config,
form: Form, form: Form,
@Optional() item: Item, @Optional() item: Item,
app: IonicApp, app: App,
platform: Platform, platform: Platform,
elementRef: ElementRef, elementRef: ElementRef,
@Optional() scrollView: Content, @Optional() scrollView: Content,
@ -170,7 +170,7 @@ export class TextArea extends InputBase {
config: Config, config: Config,
form: Form, form: Form,
@Optional() item: Item, @Optional() item: Item,
app: IonicApp, app: App,
platform: Platform, platform: Platform,
elementRef: ElementRef, elementRef: ElementRef,
@Optional() scrollView: Content, @Optional() scrollView: Content,

View File

@ -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 {addSelector} from '../../config/bootstrap';
import {pascalCaseToDashCase} from '../../util/util';
import {NavParams} from '../nav/nav-params';
import {ViewController} from '../nav/view-controller';
import {Animation} from '../../animations/animation'; import {Animation} from '../../animations/animation';
import {NavParams} from '../nav/nav-params';
import {pascalCaseToDashCase} from '../../util/util';
import {Transition, TransitionOptions} from '../../transitions/transition'; import {Transition, TransitionOptions} from '../../transitions/transition';
import {ViewController} from '../nav/view-controller';
import {windowDimensions} from '../../util/dom';
/** /**
* @name Modal * @name Modal
@ -35,7 +36,7 @@ import {Transition, TransitionOptions} from '../../transitions/transition';
* ```ts * ```ts
* import {Page, Modal, NavController, NavParams} from 'ionic-angular'; * import {Page, Modal, NavController, NavParams} from 'ionic-angular';
* *
* @Page(...) * @Component(...)
* class HomePage { * class HomePage {
* *
* constructor(nav: NavController) { * constructor(nav: NavController) {
@ -49,7 +50,7 @@ import {Transition, TransitionOptions} from '../../transitions/transition';
* *
* } * }
* *
* @Page(...) * @Component(...)
* class Profile { * class Profile {
* *
* constructor(params: NavParams) { * constructor(params: NavParams) {
@ -65,9 +66,10 @@ import {Transition, TransitionOptions} from '../../transitions/transition';
* modal. * modal.
* *
* ```ts * ```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 { * class HomePage {
* *
* constructor(nav: NavController) { * constructor(nav: NavController) {
@ -89,7 +91,7 @@ import {Transition, TransitionOptions} from '../../transitions/transition';
* *
* } * }
* *
* @Page(...) * @Component(...)
* class Profile { * class Profile {
* *
* constructor(viewCtrl: ViewController) { * constructor(viewCtrl: ViewController) {
@ -163,11 +165,13 @@ export class ModalCmp {
@ViewChild('viewport', {read: ViewContainerRef}) viewport: ViewContainerRef; @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<ComponentRef<any>> { loadComponent(): Promise<ComponentRef<any>> {
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; return componentRef;
}); });
} }

View File

@ -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() { export function run() {
describe('Modal', () => { describe('Modal', () => {
@ -51,19 +52,19 @@ export function run() {
it('should return a componentRef object after loading component', (done) => { it('should return a componentRef object after loading component', (done) => {
// arrange // arrange
let mockLoader = { let mockLoader: any = {
loadNextToLocation: () => {} loadNextToLocation: () => {}
}; };
let mockNavParams = { let mockNavParams: any = {
data: { data: {
componentType: "myComponentType" componentType: function mockComponentType(){}
} }
}; };
let mockComponentRef = {}; let mockComponentRef = {};
spyOn(mockLoader, "loadNextToLocation").and.returnValue(Promise.resolve(mockComponentRef)); spyOn(mockLoader, "loadNextToLocation").and.returnValue(Promise.resolve(mockComponentRef));
let modalCmp = new ModalCmp(null, mockLoader, mockNavParams, null); let modalCmp = new ModalCmp(mockLoader, mockNavParams);
modalCmp.viewport = "mockViewport"; modalCmp.viewport = <any>"mockViewport";
// act // act
modalCmp.loadComponent().then(loadedComponentRef => { modalCmp.loadComponent().then(loadedComponentRef => {
@ -91,7 +92,7 @@ let componentToPresentSpy = {
_ionicProjectContent: () => {}, _ionicProjectContent: () => {},
}; };
@Page({ @Component({
template: `<div class="myComponent"></div>` template: `<div class="myComponent"></div>`
}) })
class ComponentToPresent{ class ComponentToPresent{

View File

@ -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 {Config} from '../../config/config';
import {Ion} from '../ion'; import {Ion} from '../ion';
import {IonicApp} from '../app/app'; import {isBlank, pascalCaseToDashCase} from '../../util/util';
import {Keyboard} from '../../util/keyboard'; import {Keyboard} from '../../util/keyboard';
import {NavParams} from './nav-params'; import {NavParams} from './nav-params';
import {pascalCaseToDashCase, isBlank} from '../../util/util';
import {MenuController} from '../menu/menu-controller'; import {MenuController} from '../menu/menu-controller';
import {NavPortal} from './nav-portal'; import {NavPortal} from './nav-portal';
import {SwipeBackGesture} from './swipe-back'; import {SwipeBackGesture} from './swipe-back';
@ -59,12 +60,9 @@ import {ViewController} from './view-controller';
* *
* *
* ## Page creation * ## 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 * Pages are created when they are added to the navigation stack. For methods
* like [push()](#push), the NavController takes any component class that is * 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. * 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 * 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
* Lifecycle events are fired during various stages of navigation. They can be * 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 * ```ts
* @Page({ * import {Component} from '@angular/core';
*
* @Component({
* template: 'Hello World' * template: 'Hello World'
* }) * })
* class HelloWorld { * class HelloWorld {
@ -171,6 +171,14 @@ export class NavController extends Ion {
protected _trnsTime: number = 0; protected _trnsTime: number = 0;
protected _views: Array<ViewController> = []; protected _views: Array<ViewController> = [];
pageDidLoad: EventEmitter<any>;
pageWillEnter: EventEmitter<any>;
pageDidEnter: EventEmitter<any>;
pageWillLeave: EventEmitter<any>;
pageDidLeave: EventEmitter<any>;
pageWillUnload: EventEmitter<any>;
pageDidUnload: EventEmitter<any>;
/** /**
* @private * @private
*/ */
@ -203,7 +211,7 @@ export class NavController extends Ion {
constructor( constructor(
parent: any, parent: any,
protected _app: IonicApp, protected _app: App,
config: Config, config: Config,
protected _keyboard: Keyboard, protected _keyboard: Keyboard,
elementRef: ElementRef, elementRef: ElementRef,
@ -227,6 +235,14 @@ export class NavController extends Ion {
this.providers = ReflectiveInjector.resolve([ this.providers = ReflectiveInjector.resolve([
provide(NavController, {useValue: this}) 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') animation: enteringView.getTransitionName('back')
}); });
// present() always uses the root nav
// start the transition // start the transition
return rootNav._insertViews(-1, [enteringView], opts); 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 // 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 // only we're looking for an actual NavController w/ stack of views
leavingView.fireWillLeave(); leavingView.fireWillLeave();
this.pageWillLeave.emit(leavingView);
return parentNav.pop(opts).then((rtnVal: boolean) => { return parentNav.pop(opts).then((rtnVal: boolean) => {
leavingView.fireDidLeave(); leavingView.fireDidLeave();
this.pageDidLeave.emit(leavingView);
return rtnVal; return rtnVal;
}); });
} }
@ -919,6 +938,7 @@ export class NavController extends Ion {
// the first view to be removed, it should init leave // the first view to be removed, it should init leave
view.state = STATE_INIT_LEAVE; view.state = STATE_INIT_LEAVE;
view.fireWillUnload(); view.fireWillUnload();
this.pageWillUnload.emit(view);
// from the index of the leaving view, go backwards and // from the index of the leaving view, go backwards and
// find the first view that is inactive so it can be the entering // 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 // apart of any transitions that will eventually happen
this._views.filter(v => v.state === STATE_REMOVE).forEach(view => { this._views.filter(v => v.state === STATE_REMOVE).forEach(view => {
view.fireWillLeave(); view.fireWillLeave();
this.pageWillLeave.emit(view);
view.fireDidLeave(); view.fireDidLeave();
this.pageDidLeave.emit(view);
this._views.splice(this.indexOf(view), 1); this._views.splice(this.indexOf(view), 1);
view.destroy(); view.destroy();
}); });
@ -987,6 +1009,7 @@ export class NavController extends Ion {
// if no entering view then create a bogus one // if no entering view then create a bogus one
enteringView = new ViewController(); enteringView = new ViewController();
enteringView.fireLoaded(); enteringView.fireLoaded();
this.pageDidLoad.emit(enteringView);
} }
/* Async steps to complete a transition /* Async steps to complete a transition
@ -1043,6 +1066,7 @@ export class NavController extends Ion {
this.loadPage(enteringView, null, opts, () => { this.loadPage(enteringView, null, opts, () => {
enteringView.fireLoaded(); enteringView.fireLoaded();
this.pageDidLoad.emit(enteringView);
this._postRender(transId, enteringView, leavingView, isAlreadyTransitioning, opts, done); this._postRender(transId, enteringView, leavingView, isAlreadyTransitioning, opts, done);
}); });
} }
@ -1102,12 +1126,14 @@ export class NavController extends Ion {
// only fire entering lifecycle if the leaving // only fire entering lifecycle if the leaving
// view hasn't explicitly set not to // view hasn't explicitly set not to
enteringView.fireWillEnter(); enteringView.fireWillEnter();
this.pageWillEnter.emit(enteringView);
} }
if (enteringView.fireOtherLifecycles) { if (enteringView.fireOtherLifecycles) {
// only fire leaving lifecycle if the entering // only fire leaving lifecycle if the entering
// view hasn't explicitly set not to // view hasn't explicitly set not to
leavingView.fireWillLeave(); leavingView.fireWillLeave();
this.pageWillLeave.emit(leavingView);
} }
} else { } else {
@ -1214,12 +1240,14 @@ export class NavController extends Ion {
// only fire entering lifecycle if the leaving // only fire entering lifecycle if the leaving
// view hasn't explicitly set not to // view hasn't explicitly set not to
enteringView.fireDidEnter(); enteringView.fireDidEnter();
this.pageDidEnter.emit(enteringView);
} }
if (enteringView.fireOtherLifecycles) { if (enteringView.fireOtherLifecycles) {
// only fire leaving lifecycle if the entering // only fire leaving lifecycle if the entering
// view hasn't explicitly set not to // view hasn't explicitly set not to
leavingView.fireDidLeave(); leavingView.fireDidLeave();
this.pageDidLeave.emit(leavingView);
} }
} }
@ -1422,11 +1450,15 @@ export class NavController extends Ion {
return; return;
} }
// add more providers to just this page
let providers = this.providers.concat(ReflectiveInjector.resolve([ let providers = this.providers.concat(ReflectiveInjector.resolve([
provide(ViewController, {useValue: view}), provide(ViewController, {useValue: view}),
provide(NavParams, {useValue: view.getNavParams()}) provide(NavParams, {useValue: view.getNavParams()})
])); ]));
// automatically set "ion-page" selector
addSelector(view.componentType, 'ion-page');
// load the page component inside the nav // load the page component inside the nav
this._loader.loadNextToLocation(view.componentType, this._viewport, providers).then(component => { this._loader.loadNextToLocation(view.componentType, this._viewport, providers).then(component => {

View File

@ -1,6 +1,6 @@
import {Directive, ElementRef, Optional, NgZone, Renderer, DynamicComponentLoader, ViewContainerRef} from '@angular/core'; 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 {Config} from '../../config/config';
import {Keyboard} from '../../util/keyboard'; import {Keyboard} from '../../util/keyboard';
import {NavController} from './nav-controller'; import {NavController} from './nav-controller';
@ -16,7 +16,7 @@ export class NavPortal extends NavController {
constructor( constructor(
@Optional() viewCtrl: ViewController, @Optional() viewCtrl: ViewController,
@Optional() parent: NavController, @Optional() parent: NavController,
app: IonicApp, app: App,
config: Config, config: Config,
keyboard: Keyboard, keyboard: Keyboard,
elementRef: ElementRef, elementRef: ElementRef,

View File

@ -1,6 +1,6 @@
import {Component, ElementRef, ViewContainerRef, DynamicComponentLoader, Input, Optional, NgZone, Renderer, Type, ViewChild, ViewEncapsulation, AfterViewInit} from '@angular/core'; 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 {Config} from '../../config/config';
import {Keyboard} from '../../util/keyboard'; import {Keyboard} from '../../util/keyboard';
import {isTrueProperty} from '../../util/util'; import {isTrueProperty} from '../../util/util';
@ -28,15 +28,18 @@ import {ViewController} from './view-controller';
* *
* @usage * @usage
* ```ts * ```ts
* import {GettingStartedPage} from 'getting-started'; * import {Component} from '@angular/core';
* @App({ * import {ionicBootstrap} from 'ionic-angular';
* template: `<ion-nav [root]="rootPage"></ion-nav>` * import {GettingStartedPage} from './getting-started';
*
* @Component({
* template: `<ion-nav [root]="root"></ion-nav>`
* }) * })
* class MyApp { * class MyApp {
* constructor(){ * root = GettingStartedPage;
* this.rootPage = GettingStartedPage;
* }
* } * }
*
* ionicBootstrap(MyApp);
* ``` * ```
* *
* ### Back Navigation * ### Back Navigation
@ -117,7 +120,7 @@ export class Nav extends NavController implements AfterViewInit {
constructor( constructor(
@Optional() viewCtrl: ViewController, @Optional() viewCtrl: ViewController,
@Optional() parent: NavController, @Optional() parent: NavController,
app: IonicApp, app: App,
config: Config, config: Config,
keyboard: Keyboard, keyboard: Keyboard,
elementRef: ElementRef, elementRef: ElementRef,

View File

@ -4,7 +4,7 @@ import {Ion} from '../ion';
import {Icon} from '../icon/icon'; import {Icon} from '../icon/icon';
import {ToolbarBase} from '../toolbar/toolbar'; import {ToolbarBase} from '../toolbar/toolbar';
import {Config} from '../../config/config'; import {Config} from '../../config/config';
import {IonicApp} from '../app/app'; import {App} from '../app/app';
import {isTrueProperty} from '../../util/util'; import {isTrueProperty} from '../../util/util';
import {ViewController} from '../nav/view-controller'; import {ViewController} from '../nav/view-controller';
import {NavController} from '../nav/nav-controller'; import {NavController} from '../nav/nav-controller';
@ -134,7 +134,7 @@ export class Navbar extends ToolbarBase {
} }
constructor( constructor(
private _app: IonicApp, private _app: App,
@Optional() viewCtrl: ViewController, @Optional() viewCtrl: ViewController,
elementRef: ElementRef, elementRef: ElementRef,
config: Config, config: Config,

View File

@ -1,6 +1,6 @@
import {Component, Inject, forwardRef, ElementRef, NgZone, Renderer, DynamicComponentLoader, ViewContainerRef, ViewChild, Type, ViewEncapsulation, ChangeDetectorRef, EventEmitter, Input, Output} from '@angular/core'; 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 {Config} from '../../config/config';
import {isTrueProperty} from '../../util/util'; import {isTrueProperty} from '../../util/util';
import {Keyboard} from '../../util/keyboard'; import {Keyboard} from '../../util/keyboard';
@ -208,7 +208,7 @@ export class Tab extends NavController {
constructor( constructor(
@Inject(forwardRef(() => Tabs)) parentTabs: Tabs, @Inject(forwardRef(() => Tabs)) parentTabs: Tabs,
app: IonicApp, app: App,
config: Config, config: Config,
keyboard: Keyboard, keyboard: Keyboard,
elementRef: ElementRef, elementRef: ElementRef,

View File

@ -1,6 +1,6 @@
import {Component, Directive, ElementRef, Optional, Host, forwardRef, ViewContainerRef, ViewChild, ViewChildren, EventEmitter, Output, Input, Renderer, ViewEncapsulation} from '@angular/core'; 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 {Config} from '../../config/config';
import {Tab} from './tab'; import {Tab} from './tab';
import {TabButton} from './tab-button'; import {TabButton} from './tab-button';
@ -49,7 +49,7 @@ import {isBlank, isTrueProperty} from '../../util/util';
* *
* @usage * @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: * template:
* *
* ```html * ```html
@ -63,7 +63,7 @@ import {isBlank, isTrueProperty} from '../../util/util';
* Where `tab1Root`, `tab2Root`, and `tab3Root` are each a page: * Where `tab1Root`, `tab2Root`, and `tab3Root` are each a page:
* *
*```ts *```ts
* @Page({ * @Component({
* templateUrl: 'build/pages/tabs/tabs.html' * templateUrl: 'build/pages/tabs/tabs.html'
* }) * })
* export class TabsPage { * export class TabsPage {
@ -223,7 +223,7 @@ export class Tabs extends Ion {
constructor( constructor(
@Optional() parent: NavController, @Optional() parent: NavController,
@Optional() viewCtrl: ViewController, @Optional() viewCtrl: ViewController,
private _app: IonicApp, private _app: App,
private _config: Config, private _config: Config,
private _elementRef: ElementRef, private _elementRef: ElementRef,
private _platform: Platform, private _platform: Platform,

View File

@ -1,6 +1,6 @@
import {Injectable, NgZone} from '@angular/core'; import {Injectable, NgZone} from '@angular/core';
import {IonicApp} from '../app/app'; import {App} from '../app/app';
import {Config} from '../../config/config'; import {Config} from '../../config/config';
import {pointerCoord, hasPointerMoved} from '../../util/dom'; import {pointerCoord, hasPointerMoved} from '../../util/dom';
import {Activator} from './activator'; import {Activator} from './activator';
@ -22,7 +22,7 @@ export class TapClick {
constructor( constructor(
config: Config, config: Config,
private app: IonicApp, private app: App,
private zone: NgZone private zone: NgZone
) { ) {
let self = this; let self = this;

View File

@ -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 {ROUTER_PROVIDERS} from '@angular/router';
import {LocationStrategy, HashLocationStrategy} from '@angular/common'; import {LocationStrategy, HashLocationStrategy} from '@angular/common';
import {HTTP_PROVIDERS} from '@angular/http'; import {HTTP_PROVIDERS} from '@angular/http';
import {App} from '../components/app/app';
import {ClickBlock} from '../util/click-block'; import {ClickBlock} from '../util/click-block';
import {Config} from './config'; import {Config} from './config';
import {Events} from '../util/events'; import {Events} from '../util/events';
import {FeatureDetect} from '../util/feature-detect'; import {FeatureDetect} from '../util/feature-detect';
import {Form} from '../util/form'; 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 {Keyboard} from '../util/keyboard';
import {MenuController} from '../components/menu/menu-controller'; import {MenuController} from '../components/menu/menu-controller';
import {nativeTimeout, closest} from '../util/dom';
import {NavRegistry} from '../components/nav/nav-registry'; import {NavRegistry} from '../components/nav/nav-registry';
import {Platform} from '../platform/platform'; import {Platform} from '../platform/platform';
import {ready, closest} from '../util/dom';
import {ScrollView} from '../util/scroll-view'; import {ScrollView} from '../util/scroll-view';
import {TapClick} from '../components/tap-click/tap-click'; import {TapClick} from '../components/tap-click/tap-click';
import {Translate} from '../translation/translate'; 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<any>, config?: any): Promise<ComponentRef<any>> {
// 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<any>): ComponentRef<any> {
//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<any>, 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)) { if (!(config instanceof Config)) {
config = new Config(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.setUrl(window.location.href);
platform.setUserAgent(window.navigator.userAgent); platform.setUserAgent(window.navigator.userAgent);
platform.setNavigatorPlatform(window.navigator.platform); platform.setNavigatorPlatform(window.navigator.platform);
@ -38,7 +83,6 @@ export function ionicProviders(args: any = {}) {
config.setPlatform(platform); config.setPlatform(platform);
let clickBlock = new ClickBlock(); let clickBlock = new ClickBlock();
let events = new Events(); let events = new Events();
let featureDetect = new FeatureDetect(); let featureDetect = new FeatureDetect();
@ -46,51 +90,43 @@ export function ionicProviders(args: any = {}) {
bindEvents(window, document, platform, events); bindEvents(window, document, platform, events);
return [ return [
IonicApp, App,
provide(ClickBlock, {useValue: clickBlock}), provide(ClickBlock, {useValue: clickBlock}),
provide(Config, {useValue: config}), provide(Config, {useValue: config}),
provide(Platform, {useValue: platform}),
provide(FeatureDetect, {useValue: featureDetect}),
provide(Events, {useValue: events}), provide(Events, {useValue: events}),
provide(NavRegistry, {useValue: navRegistry}), provide(FeatureDetect, {useValue: featureDetect}),
TapClick,
Form, Form,
Keyboard, Keyboard,
MenuController, MenuController,
NavRegistry,
provide(Platform, {useValue: platform}),
Translate, Translate,
TapClick,
provide(PLATFORM_DIRECTIVES, {useValue: [directives], multi: true}),
ROUTER_PROVIDERS, ROUTER_PROVIDERS,
provide(LocationStrategy, {useClass: HashLocationStrategy}), provide(LocationStrategy, {useClass: HashLocationStrategy}),
HTTP_PROVIDERS, HTTP_PROVIDERS,
]; ];
} }
/**
* @private
*/
export function postBootstrap(appRef: ComponentRef<any>, 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: Window, document: Document, config: Config, platform: Platform, clickBlock: ClickBlock, featureDetect: FeatureDetect) {
function setupDom(window, document, config, platform, clickBlock, featureDetect) {
let bodyEle = document.body; let bodyEle = document.body;
let mode = config.get('mode'); let mode = config.get('mode');
// if dynamic mode links have been added the fire up the correct one // if dynamic mode links have been added the fire up the correct one
let modeLinkAttr = mode + '-href'; let modeLinkAttr = mode + '-href';
let linkEle = document.head.querySelector('link[' + modeLinkAttr + ']'); let linkEle = <HTMLLinkElement>document.head.querySelector('link[' + modeLinkAttr + ']');
if (linkEle) { if (linkEle) {
let href = linkEle.getAttribute(modeLinkAttr); let href = linkEle.getAttribute(modeLinkAttr);
linkEle.removeAttribute(modeLinkAttr); linkEle.removeAttribute(modeLinkAttr);
linkEle.href = href; linkEle.href = href;
} }
let headStyle = document.createElement('style');
headStyle.innerHTML = 'ion-app{display:none}';
document.head.appendChild(headStyle);
// set the mode class name // set the mode class name
// ios/md/wp // ios/md/wp
bodyEle.classList.add(mode); bodyEle.classList.add(mode);
@ -122,10 +158,6 @@ function setupDom(window, document, config, platform, clickBlock, featureDetect)
bodyEle.classList.add('enable-hover'); bodyEle.classList.add('enable-hover');
} }
if (config.get('clickBlock')) {
clickBlock.enable();
}
// run feature detection tests // run feature detection tests
featureDetect.run(window, document); 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 * 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) => { window.addEventListener('online', (ev) => {
events.publish('app:online', ev); events.publish('app:online', ev);
}, false); }, false);
@ -150,10 +182,10 @@ function bindEvents(window, document, platform, events) {
// When that status taps, we respond // When that status taps, we respond
window.addEventListener('statusTap', (ev) => { window.addEventListener('statusTap', (ev) => {
// TODO: Make this more better // TODO: Make this more better
var el = document.elementFromPoint(platform.width() / 2, platform.height() / 2); let el = <HTMLElement>document.elementFromPoint(platform.width() / 2, platform.height() / 2);
if (!el) { return; } if (!el) { return; }
var content = closest(el, 'scroll-content'); let content = closest(el, 'scroll-content');
if (content) { if (content) {
var scroll = new ScrollView(content); var scroll = new ScrollView(content);
scroll.scrollTo(0, 0, 300); scroll.scrollTo(0, 0, 300);
@ -161,9 +193,22 @@ function bindEvents(window, document, platform, events) {
}); });
// start listening for resizes XXms after the app starts // start listening for resizes XXms after the app starts
setTimeout(function() { nativeTimeout(() => {
window.addEventListener('resize', function() { window.addEventListener('resize', () => {
platform.windowResize(); platform.windowResize();
}); });
}, 2000); }, 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);
}
}
}

View File

@ -80,7 +80,7 @@ export function run() {
let userConfig = new Config({ let userConfig = new Config({
mode: 'configInstance' mode: 'configInstance'
}) })
let providers = ionicProviders({config:userConfig}); let providers = ionicProviders(null, userConfig);
let config = providers.find(provider => provider.useValue instanceof Config).useValue; 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', () => { it('should create new Config instance from config object in ionicProviders', () => {
let providers = ionicProviders({config: { let providers = ionicProviders(null, {
mode: 'configObj' mode: 'configObj'
}}); });
let config = providers.find(provider => provider.useValue instanceof Config).useValue; let config = providers.find(provider => provider.useValue instanceof Config).useValue;

View File

@ -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<Type | any[]>;
pipes?: Array<Type | any[]>;
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 <ion-nav>
if (!args.templateUrl && !args.template) {
args.template = '<ion-nav></ion-nav>';
}
// 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;
};
}

View File

@ -28,41 +28,13 @@ export interface PageMetadata {
} }
/** /**
* @name Page * @private
* @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: `
* <ion-content>
* I am a page!
* </ion-content>
* `
* })
* class MyPage {}
* ```
*
* Pages have their content automatically wrapped in `<ion-page>`, 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)
*/ */
export function Page(config: PageMetadata) { export function Page(config: PageMetadata) {
return function(cls) { 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.selector = 'ion-page';
config.host = config.host || {}; config.host = config.host || {};
config.host['[hidden]'] = '_hidden'; config.host['[hidden]'] = '_hidden';

View File

@ -3,7 +3,6 @@ export * from './config/bootstrap';
export * from './config/config'; export * from './config/config';
export * from './config/directives'; export * from './config/directives';
export * from './decorators/app';
export * from './decorators/page'; export * from './decorators/page';
export * from './components'; export * from './components';