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
* 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);
}
}
}

View File

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

View File

@ -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: `<ion-content>
* <button (click)="scrollTo()">Down 500px</button>
* </ion-content>`
@ -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: `<ion-content>
* <button (click)="scrollToTop()">Scroll to top</button>
* </ion-content>`

View File

@ -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,

View File

@ -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,

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 {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<any>) => {
@ -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<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;
});
}

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() {
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 = <any>"mockViewport";
// act
modalCmp.loadComponent().then(loadedComponentRef => {
@ -91,7 +92,7 @@ let componentToPresentSpy = {
_ionicProjectContent: () => {},
};
@Page({
@Component({
template: `<div class="myComponent"></div>`
})
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 {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<ViewController> = [];
pageDidLoad: EventEmitter<any>;
pageWillEnter: EventEmitter<any>;
pageDidEnter: EventEmitter<any>;
pageWillLeave: EventEmitter<any>;
pageDidLeave: EventEmitter<any>;
pageWillUnload: EventEmitter<any>;
pageDidUnload: EventEmitter<any>;
/**
* @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;

View File

@ -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,

View File

@ -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: `<ion-nav [root]="rootPage"></ion-nav>`
* import {Component} from '@angular/core';
* import {ionicBootstrap} from 'ionic-angular';
* import {GettingStartedPage} from './getting-started';
*
* @Component({
* template: `<ion-nav [root]="root"></ion-nav>`
* })
* class MyApp {
* 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,

View File

@ -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,

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 {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,

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 {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,

View File

@ -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;

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 {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<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)) {
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<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, 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 = <HTMLLinkElement>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 = <HTMLElement>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);
}
}
}

View File

@ -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;

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
* @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)
* @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';

View File

@ -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';