diff --git a/src/components/modal/modal.ts b/src/components/modal/modal.ts index 6363ffd3ea3..ec0a17cf117 100644 --- a/src/components/modal/modal.ts +++ b/src/components/modal/modal.ts @@ -1,4 +1,4 @@ -import {Component, ComponentRef, ElementRef, DynamicComponentLoader, ViewChild, ViewContainerRef} from '@angular/core'; +import {Component, ComponentRef, ElementRef, ViewChild, ViewContainerRef, ComponentResolver} from '@angular/core'; import {addSelector} from '../../config/bootstrap'; import {Animation} from '../../animations/animation'; @@ -148,10 +148,7 @@ export class Modal extends ViewController { if (originalNgAfterViewInit) { originalNgAfterViewInit(); } - this.instance.loadComponent().then( (componentRef: ComponentRef) => { - this.setInstance(componentRef.instance); - done(); - }); + this.instance.loadComponent(done); }; } } @@ -168,14 +165,21 @@ export class ModalCmp { @ViewChild('viewport', {read: ViewContainerRef}) viewport: ViewContainerRef; - constructor(protected _loader: DynamicComponentLoader, protected _navParams: NavParams) {} + constructor( + private _compiler: ComponentResolver, + private _navParams: NavParams, + private _viewCtrl: ViewController + ) {} - loadComponent(): Promise> { - let componentType = this._navParams.data.componentType; - addSelector(componentType, 'ion-page'); + loadComponent(done: Function) { + addSelector(this._navParams.data.componentType, 'ion-modal-inner'); - return this._loader.loadNextToLocation(componentType, this.viewport).then( (componentRef: ComponentRef) => { - return componentRef; + this._compiler.resolveComponent(this._navParams.data.componentType).then((componentFactory) => { + let componentRef = this.viewport.createComponent(componentFactory, this.viewport.length, this.viewport.parentInjector); + + this._viewCtrl.setInstance(componentRef.instance); + + done(); }); } diff --git a/src/components/modal/test/modal.spec.ts b/src/components/modal/test/modal.spec.ts index 725b1b1cf5d..a4f2271e00e 100644 --- a/src/components/modal/test/modal.spec.ts +++ b/src/components/modal/test/modal.spec.ts @@ -16,65 +16,8 @@ export function run() { }); }); - describe('loaded', () => { - it('should call done after loading component and call original ngAfterViewInit method', (done) => { - // arrange - let modal = new Modal({}, {}); - let mockInstance = { - ngAfterViewInit: () => {}, - loadComponent: () => {} - }; - let mockComponentRef = { - instance: "someData" - }; - modal.instance = mockInstance; - - let ngAfterViewInitSpy = spyOn(mockInstance, "ngAfterViewInit"); - spyOn(mockInstance, "loadComponent").and.returnValue(Promise.resolve(mockComponentRef)); - - let doneCallback = () => { - // assert - expect(ngAfterViewInitSpy).toHaveBeenCalled(); - expect(modal.instance).toEqual("someData"); - done(); - }; - - // act - modal.loaded(doneCallback); - // (angular calls ngAfterViewInit, we're not testing angular so manually call it) - mockInstance.ngAfterViewInit(); - - }, 5000); - }); }); - describe('ModalCmp', () => { - - it('should return a componentRef object after loading component', (done) => { - // arrange - let mockLoader: any = { - loadNextToLocation: () => {} - }; - let mockNavParams: any = { - data: { - componentType: function mockComponentType(){} - } - }; - let mockComponentRef = {}; - - spyOn(mockLoader, "loadNextToLocation").and.returnValue(Promise.resolve(mockComponentRef)); - let modalCmp = new ModalCmp(mockLoader, mockNavParams); - modalCmp.viewport = "mockViewport"; - - // act - modalCmp.loadComponent().then(loadedComponentRef => { - // assert - expect(loadedComponentRef).toEqual(mockComponentRef); - expect(mockLoader.loadNextToLocation).toHaveBeenCalledWith(mockNavParams.data.componentType, modalCmp.viewport); - done(); - }); - }, 5000); - }); } const STATE_ACTIVE = 'active'; diff --git a/src/components/nav/nav-controller.ts b/src/components/nav/nav-controller.ts index 5edd378dbd4..c5173b43d15 100644 --- a/src/components/nav/nav-controller.ts +++ b/src/components/nav/nav-controller.ts @@ -1,4 +1,4 @@ -import {ViewContainerRef, DynamicComponentLoader, provide, ReflectiveInjector, ResolvedReflectiveProvider, ElementRef, NgZone, Renderer, Type, EventEmitter} from '@angular/core'; +import {ViewContainerRef, ComponentResolver, ComponentRef, provide, ReflectiveInjector, ResolvedReflectiveProvider, ElementRef, NgZone, Renderer, EventEmitter} from '@angular/core'; import {addSelector} from '../../config/bootstrap'; import {App} from '../app/app'; @@ -184,11 +184,6 @@ export class NavController extends Ion { */ id: string; - /** - * @private - */ - providers: ResolvedReflectiveProvider[]; - /** * @private */ @@ -217,7 +212,7 @@ export class NavController extends Ion { elementRef: ElementRef, protected _zone: NgZone, protected _renderer: Renderer, - protected _loader: DynamicComponentLoader + protected _compiler: ComponentResolver ) { super(elementRef); @@ -231,11 +226,6 @@ export class NavController extends Ion { this.id = (++ctrlIds).toString(); - // build a new injector for child ViewControllers to use - this.providers = ReflectiveInjector.resolve([ - provide(NavController, {useValue: this}) - ]); - this.viewDidLoad = new EventEmitter(); this.viewWillEnter = new EventEmitter(); this.viewDidEnter = new EventEmitter(); @@ -268,12 +258,12 @@ export class NavController extends Ion { /** * Set the root for the current navigation stack. - * @param {Type} page The name of the component you want to push on the navigation stack. + * @param {Page} page The name of the component you want to push on the navigation stack. * @param {object} [params={}] Any nav-params you want to pass along to the next view. * @param {object} [opts={}] Any options you want to use pass to transtion. * @returns {Promise} Returns a promise which is resolved when the transition has completed. */ - setRoot(page: Type, params?: any, opts?: NavOptions): Promise { + setRoot(page: any, params?: any, opts?: NavOptions): Promise { return this.setPages([{page, params}], opts); } @@ -350,11 +340,11 @@ export class NavController extends Ion { * } *``` * - * @param {array} pages An arry of page components and their params to load in the stack. + * @param {array} pages An arry of page components and their params to load in the stack. * @param {object} [opts={}] Nav options to go with this transition. * @returns {Promise} Returns a promise which is resolved when the transition has completed. */ - setPages(pages: Array<{page: Type, params?: any}>, opts?: NavOptions): Promise { + setPages(pages: Array<{page: any, params?: any}>, opts?: NavOptions): Promise { if (!pages || !pages.length) { return Promise.resolve(false); } @@ -451,12 +441,12 @@ export class NavController extends Ion { * } * } * ``` - * @param {Type} page The page component class you want to push on to the navigation stack + * @param {Page} page The page component class you want to push on to the navigation stack * @param {object} [params={}] Any nav-params you want to pass along to the next view * @param {object} [opts={}] Nav options to go with this transition. * @returns {Promise} Returns a promise which is resolved when the transition has completed. */ - push(page: Type, params?: any, opts?: NavOptions) { + push(page: any, params?: any, opts?: NavOptions) { return this.insertPages(-1, [{page: page, params: params}], opts); } @@ -543,12 +533,12 @@ export class NavController extends Ion { * This will insert the `Info` page into the second slot of our navigation stack. * * @param {number} insertIndex The index where to insert the page. - * @param {Type} page The component you want to insert into the nav stack. + * @param {Page} page The component you want to insert into the nav stack. * @param {object} [params={}] Any nav-params you want to pass along to the next page. * @param {object} [opts={}] Nav options to go with this transition. * @returns {Promise} Returns a promise which is resolved when the transition has completed. */ - insert(insertIndex: number, page: Type, params?: any, opts?: NavOptions): Promise { + insert(insertIndex: number, page: any, params?: any, opts?: NavOptions): Promise { return this.insertPages(insertIndex, [{page: page, params: params}], opts); } @@ -576,11 +566,11 @@ export class NavController extends Ion { * in and become the active page. * * @param {number} insertIndex The index where you want to insert the page. - * @param {array<{page: Type, params=: any}>} insertPages An array of objects, each with a `page` and optionally `params` property. + * @param {array<{page: Page, params=: any}>} insertPages An array of objects, each with a `page` and optionally `params` property. * @param {object} [opts={}] Nav options to go with this transition. * @returns {Promise} Returns a promise which is resolved when the transition has completed. */ - insertPages(insertIndex: number, insertPages: Array<{page: Type, params?: any}>, opts?: NavOptions): Promise { + insertPages(insertIndex: number, insertPages: Array<{page: any, params?: any}>, opts?: NavOptions): Promise { let views = insertPages.map(p => new ViewController(p.page, p.params)); return this._insertViews(insertIndex, views, opts); } @@ -1450,43 +1440,50 @@ 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 + // TODO: see about having this set using ComponentFactory addSelector(view.componentType, 'ion-page'); - // load the page component inside the nav - this._loader.loadNextToLocation(view.componentType, this._viewport, providers).then(component => { + this._compiler.resolveComponent(view.componentType).then(componentFactory => { + + // add more providers to just this page + let componentProviders = ReflectiveInjector.resolve([ + provide(NavController, {useValue: this}), + provide(ViewController, {useValue: view}), + provide(NavParams, {useValue: view.getNavParams()}) + ]); + + let childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, this._viewport.parentInjector); + + let componentRef = componentFactory.create(childInjector, null, null); + + this._viewport.insert(componentRef.hostView, this._viewport.length); // a new ComponentRef has been created // set the ComponentRef's instance to its ViewController - view.setInstance(component.instance); + view.setInstance(componentRef.instance); // the component has been loaded, so call the view controller's loaded method to load any dependencies into the dom - view.loaded( () => { + view.loaded(() => { // the ElementRef of the actual ion-page created - let pageElementRef = component.location; + let pageElementRef = componentRef.location; // remember the ChangeDetectorRef for this ViewController - view.setChangeDetector(component.changeDetectorRef); + view.setChangeDetector(componentRef.changeDetectorRef); // remember the ElementRef to the ion-page elementRef that was just created view.setPageRef(pageElementRef); // auto-add page css className created from component JS class name - let cssClassName = pascalCaseToDashCase(view.componentType['name']); + let cssClassName = pascalCaseToDashCase(view.componentType.name); this._renderer.setElementClass(pageElementRef.nativeElement, cssClassName, true); view.onDestroy(() => { // ensure the element is cleaned up for when the view pool reuses this element this._renderer.setElementAttribute(pageElementRef.nativeElement, 'class', null); this._renderer.setElementAttribute(pageElementRef.nativeElement, 'style', null); - component.destroy(); + componentRef.destroy(); }); if (!navbarContainerRef) { diff --git a/src/components/nav/nav-portal.ts b/src/components/nav/nav-portal.ts index 00007028324..1d956c008be 100644 --- a/src/components/nav/nav-portal.ts +++ b/src/components/nav/nav-portal.ts @@ -1,4 +1,4 @@ -import {Directive, ElementRef, Optional, NgZone, Renderer, DynamicComponentLoader, ViewContainerRef} from '@angular/core'; +import {Directive, ElementRef, Optional, NgZone, Renderer, ComponentResolver, ViewContainerRef} from '@angular/core'; import {App} from '../app/app'; import {Config} from '../../config/config'; @@ -22,10 +22,10 @@ export class NavPortal extends NavController { elementRef: ElementRef, zone: NgZone, renderer: Renderer, - loader: DynamicComponentLoader, + compiler: ComponentResolver, viewPort: ViewContainerRef ) { - super(parent, app, config, keyboard, elementRef, zone, renderer, loader); + super(parent, app, config, keyboard, elementRef, zone, renderer, compiler); this.isPortal = true; this.setViewport(viewPort); } diff --git a/src/components/nav/nav-router.ts b/src/components/nav/nav-router.ts index a217b4ecc85..88c72b94cb9 100644 --- a/src/components/nav/nav-router.ts +++ b/src/components/nav/nav-router.ts @@ -1,10 +1,4 @@ -import {Directive, ViewContainerRef, DynamicComponentLoader, Attribute} from '@angular/core'; -import { - RouterOutletMap, - Router} from '@angular/router'; - -import {Nav} from './nav'; -import {ViewController} from './view-controller'; +import {Directive} from '@angular/core'; /** * @private diff --git a/src/components/nav/nav.ts b/src/components/nav/nav.ts index 3ddb6fc92c0..819c97eac52 100644 --- a/src/components/nav/nav.ts +++ b/src/components/nav/nav.ts @@ -1,4 +1,4 @@ -import {Component, ElementRef, ViewContainerRef, DynamicComponentLoader, Input, Optional, NgZone, Renderer, Type, ViewChild, ViewEncapsulation, AfterViewInit} from '@angular/core'; +import {Component, ElementRef, ViewContainerRef, ComponentResolver, Input, Optional, NgZone, Renderer, ViewChild, ViewEncapsulation, AfterViewInit} from '@angular/core'; import {App} from '../app/app'; import {Config} from '../../config/config'; @@ -114,7 +114,7 @@ import {ViewController} from './view-controller'; encapsulation: ViewEncapsulation.None, }) export class Nav extends NavController implements AfterViewInit { - private _root: Type; + private _root: any; private _hasInit: boolean = false; constructor( @@ -126,9 +126,9 @@ export class Nav extends NavController implements AfterViewInit { elementRef: ElementRef, zone: NgZone, renderer: Renderer, - loader: DynamicComponentLoader + compiler: ComponentResolver ) { - super(parent, app, config, keyboard, elementRef, zone, renderer, loader); + super(parent, app, config, keyboard, elementRef, zone, renderer, compiler); if (viewCtrl) { // an ion-nav can also act as an ion-page within a parent ion-nav @@ -173,10 +173,10 @@ export class Nav extends NavController implements AfterViewInit { * @input {Page} The Page component to load as the root page within this nav. */ @Input() - get root(): Type { + get root(): any { return this._root; } - set root(page: Type) { + set root(page: any) { this._root = page; if (this._hasInit) { diff --git a/src/components/nav/view-controller.ts b/src/components/nav/view-controller.ts index f02418c5ebe..d93697ea19e 100644 --- a/src/components/nav/view-controller.ts +++ b/src/components/nav/view-controller.ts @@ -98,7 +98,7 @@ export class ViewController { */ @Output() private _emitter: EventEmitter = new EventEmitter(); - constructor(public componentType?: Type, data?: any) { + constructor(public componentType?: any, data?: any) { // passed in data could be NavParams, but all we care about is its data object this.data = (data instanceof NavParams ? data.data : (isPresent(data) ? data : {})); diff --git a/src/components/popover/popover.ts b/src/components/popover/popover.ts index a89f2eef57d..1f4c4981781 100644 --- a/src/components/popover/popover.ts +++ b/src/components/popover/popover.ts @@ -1,6 +1,7 @@ -import {Component, ViewChild, ViewContainerRef, DynamicComponentLoader} from '@angular/core'; +import {Component, ViewChild, ViewContainerRef, ComponentResolver} from '@angular/core'; import {Renderer, ElementRef} from '@angular/core'; +import {addSelector} from '../../config/bootstrap'; import {Animation} from '../../animations/animation'; import {Transition, TransitionOptions} from '../../transitions/transition'; import {Config} from '../../config/config'; @@ -173,7 +174,8 @@ class PopoverCmp { private created: number; private showSpinner: boolean; - constructor(private _loader: DynamicComponentLoader, + constructor( + private _compiler: ComponentResolver, private _elementRef: ElementRef, private _renderer: Renderer, private _config: Config, @@ -191,7 +193,11 @@ class PopoverCmp { } ionViewWillEnter() { - this._loader.loadNextToLocation(this._navParams.data.componentType, this.viewport).then(componentRef => { + addSelector(this._navParams.data.componentType, 'ion-popover-inner'); + + this._compiler.resolveComponent(this._navParams.data.componentType).then((componentFactory) => { + let componentRef = this.viewport.createComponent(componentFactory, this.viewport.length, this.viewport.parentInjector); + this._viewCtrl.setInstance(componentRef.instance); // manually fire ionViewWillEnter() since PopoverCmp's ionViewWillEnter already happened diff --git a/src/components/tabs/tab.ts b/src/components/tabs/tab.ts index 3a68b5e46d7..0abbf34cb1e 100644 --- a/src/components/tabs/tab.ts +++ b/src/components/tabs/tab.ts @@ -1,4 +1,4 @@ -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, ComponentResolver, ViewContainerRef, ViewChild, Type, ViewEncapsulation, ChangeDetectorRef, EventEmitter, Input, Output} from '@angular/core'; import {App} from '../app/app'; import {Config} from '../../config/config'; @@ -214,11 +214,11 @@ export class Tab extends NavController { elementRef: ElementRef, zone: NgZone, renderer: Renderer, - loader: DynamicComponentLoader, + compiler: ComponentResolver, private _cd: ChangeDetectorRef ) { // A Tab is a NavController for its child pages - super(parentTabs, app, config, keyboard, elementRef, zone, renderer, loader); + super(parentTabs, app, config, keyboard, elementRef, zone, renderer, compiler); parentTabs.add(this); diff --git a/src/config/bootstrap.ts b/src/config/bootstrap.ts index 9713d64963f..07aa740a324 100644 --- a/src/config/bootstrap.ts +++ b/src/config/bootstrap.ts @@ -1,5 +1,5 @@ import {bootstrap} from '@angular/platform-browser-dynamic'; -import {Directive, ReflectiveInjector, Renderer, enableProdMode, ViewContainerRef, provide, PLATFORM_DIRECTIVES, ComponentRef, NgZone, DynamicComponentLoader} from '@angular/core'; +import {enableProdMode, provide, PLATFORM_DIRECTIVES, ComponentRef, NgZone} from '@angular/core'; import {ROUTER_PROVIDERS} from '@angular/router'; import {LocationStrategy, HashLocationStrategy} from '@angular/common'; import {HTTP_PROVIDERS} from '@angular/http';