refactor(navigation): get rid of ion-nav-controller, get nav working correctly in DOM, angular, react

This commit is contained in:
Dan Bucholtz
2017-12-08 14:45:13 -06:00
parent b0cd97b623
commit c30337bf8c
29 changed files with 1255 additions and 1191 deletions

View File

@ -1,17 +1,23 @@
import {
ChangeDetectorRef,
Component,
OnInit,
ComponentFactoryResolver,
ComponentRef,
ElementRef,
Injector,
ReflectiveInjector,
Type,
ViewContainerRef,
ViewChild
} from '@angular/core';
import { PublicNav } from '@ionic/core';
import { FrameworkDelegate } from '@ionic/core';
import { getProviders } from '../di/di';
import { AngularFrameworkDelegate } from '../providers/angular-framework-delegate';
import { AngularViewController } from '../types/angular-view-controller';
import { AngularComponentMounter } from '../providers/angular-component-mounter';
import { AngularMountingData } from '../types/interfaces';
const elementToComponentRefMap = new Map<HTMLElement, ComponentRef<any>>();
@Component({
selector: 'ion-nav',
@ -19,41 +25,41 @@ import { AngularViewController } from '../types/angular-view-controller';
<div #viewport class="ng-nav-viewport"></div>
`
})
export class IonNavDelegate implements OnInit {
export class IonNavDelegate implements FrameworkDelegate {
@ViewChild('viewport', { read: ViewContainerRef}) viewport: ViewContainerRef;
constructor(private changeDetection: ChangeDetectorRef, private angularFrameworkDelegate: AngularFrameworkDelegate) {
constructor(private elementRef: ElementRef, private changeDetection: ChangeDetectorRef, private angularComponentMounter: AngularComponentMounter, private injector: Injector, private componentResolveFactory: ComponentFactoryResolver) {
this.elementRef.nativeElement.delegate = this;
}
ngOnInit() {
const controllerElement = document.querySelector('ion-nav-controller') as any;
controllerElement.delegate = this;
async attachViewToDom(elementOrContainerToMountTo: HTMLIonNavElement, elementOrComponentToMount: Type<any>,
_propsOrDataObj?: any, _classesToAdd?: string[]): Promise<AngularMountingData> {
const componentProviders = ReflectiveInjector.resolve(getProviders(elementOrContainerToMountTo));
console.log('componentProviders: ', componentProviders);
const element = document.createElement('ion-page');
for (const clazz of _classesToAdd) {
element.classList.add(clazz);
}
elementOrContainerToMountTo.appendChild(element);
const mountingData = await this.angularComponentMounter.attachViewToDom(element, elementOrComponentToMount, [], this.changeDetection, this.componentResolveFactory, this.injector);
mountingData.element = element;
elementToComponentRefMap.set(mountingData.angularHostElement, mountingData.componentRef);
return mountingData;
}
attachViewToDom(nav: PublicNav, enteringView: AngularViewController): Promise<any> {
const componentProviders = ReflectiveInjector.resolve(getProviders(nav.element as HTMLIonNavElement));
return this.angularFrameworkDelegate.attachViewToDom(enteringView.component, this.viewport, componentProviders, this.changeDetection).then((angularMountingData) => {
enteringView.componentFactory = angularMountingData.componentFactory;
enteringView.injector = angularMountingData.childInjector;
enteringView.componentRef = angularMountingData.componentRef;
enteringView.instance = angularMountingData.componentRef.instance;
enteringView.angularHostElement = angularMountingData.componentRef.location.nativeElement;
enteringView.element = angularMountingData.componentRef.location.nativeElement.querySelector('ion-page');
});
}
removeViewFromDom(_nav: PublicNav, viewController: AngularViewController) {
return this.angularFrameworkDelegate.removeViewFromDom((viewController as any).componentRef).then(() => {
viewController.componentFactory = null;
viewController.injector = null;
viewController.componentRef = null;
viewController.instance = null;
viewController.angularHostElement = null;
viewController.element = null;
});
async removeViewFromDom(_parentElement: HTMLElement, childElement: HTMLElement) {
const componentRef = elementToComponentRefMap.get(childElement);
if (componentRef) {
return this.angularComponentMounter.removeViewFromDom(componentRef);
}
return Promise.resolve();
}
}

View File

@ -16,7 +16,7 @@ import { IonNavDelegate } from './components/ion-nav';
/* Providers */
import { ActionSheetController } from './providers/action-sheet-controller';
import { AlertController } from './providers/alert-controller';
import { AngularFrameworkDelegate } from './providers/angular-framework-delegate';
import { AngularComponentMounter } from './providers/angular-component-mounter';
import { LoadingController } from './providers/loading-controller';
import { ToastController } from './providers/toast-controller';
@ -48,7 +48,7 @@ export class IonicAngularModule {
providers: [
AlertController,
ActionSheetController,
AngularFrameworkDelegate,
AngularComponentMounter,
LoadingController,
ToastController
]

View File

@ -0,0 +1,59 @@
import {
ChangeDetectorRef,
ComponentFactoryResolver,
ComponentRef,
Injectable,
Injector,
NgZone,
ReflectiveInjector,
Type,
} from '@angular/core';
import { AngularMountingData } from '../types/interfaces';
@Injectable()
export class AngularComponentMounter {
constructor(private defaultCfr: ComponentFactoryResolver, private zone: NgZone) {
}
attachViewToDom(parentElement: HTMLElement, componentToMount: Type<any>, providers: any[], changeDetection: ChangeDetectorRef, componentResolveFactory: ComponentFactoryResolver, injector: Injector): Promise<AngularMountingData> {
return new Promise((resolve) => {
this.zone.run(() => {
console.log('parentElement: ', parentElement);
const crf = componentResolveFactory ? componentResolveFactory : this.defaultCfr;
const mountingData = attachViewToDom(crf, componentToMount, parentElement, providers, changeDetection, injector);
resolve(mountingData);
});
});
}
removeViewFromDom(componentRef: ComponentRef<any>): Promise<any> {
return new Promise((resolve) => {
this.zone.run(() => {
componentRef.destroy();
resolve();
});
});
}
}
export function attachViewToDom(crf: ComponentFactoryResolver, componentToMount: Type<any>, element: HTMLElement, providers: any, changeDetection: ChangeDetectorRef, injector: Injector): AngularMountingData {
const componentFactory = crf.resolveComponentFactory(componentToMount);
const componentProviders = ReflectiveInjector.resolve(providers);
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, injector);
const componentRef = componentFactory.create(childInjector, [], element);
changeDetection.detectChanges();
return {
componentFactory,
childInjector: childInjector,
componentRef: componentRef,
instance: componentRef.instance,
angularHostElement: componentRef.location.nativeElement,
element: componentRef.location.nativeElement,
};
}

View File

@ -1,60 +0,0 @@
import {
ChangeDetectorRef,
ComponentFactoryResolver,
ComponentRef,
Injectable,
Injector,
NgZone,
ReflectiveInjector,
Type,
ViewContainerRef,
} from '@angular/core';
import { ComponentFactory } from '@angular/core/src/linker/component_factory';
@Injectable()
export class AngularFrameworkDelegate {
constructor(private crf: ComponentFactoryResolver, private zone: NgZone) {
}
attachViewToDom(componentToMount: Type<any>, viewport: ViewContainerRef, providers: any, changeDetection: ChangeDetectorRef): Promise<AngularMountingData> {
return new Promise((resolve) => {
this.zone.run(() => {
const mountingData = attachViewToDom(this.crf, componentToMount, viewport, providers, changeDetection);
resolve(mountingData);
});
});
}
removeViewFromDom(componentRef: ComponentRef<any>): Promise<any> {
return new Promise((resolve) => {
this.zone.run(() => {
componentRef.destroy();
resolve();
});
});
}
}
export function attachViewToDom(crf: ComponentFactoryResolver, componentToMount: Type<any>, viewport: ViewContainerRef, providers: any, changeDetection: ChangeDetectorRef): AngularMountingData{
const componentFactory = crf.resolveComponentFactory(componentToMount);
const componentProviders = ReflectiveInjector.resolve(providers);
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, viewport.parentInjector);
const componentRef = componentFactory.create(childInjector, []);
viewport.insert(componentRef.hostView, viewport.length);
changeDetection.detectChanges();
return {
componentFactory,
childInjector: childInjector,
componentRef: componentRef
}
}
export interface AngularMountingData {
componentFactory: ComponentFactory<any>;
childInjector: Injector;
componentRef: ComponentRef<any>;
}

View File

@ -4,12 +4,12 @@ import {
Injector
} from '@angular/core';
import { PublicViewController } from '@ionic/core';
import { FrameworkMountingData } from '@ionic/core';
export interface AngularViewController extends PublicViewController {
export interface AngularMountingData extends FrameworkMountingData {
componentFactory?: ComponentFactory<any>;
injector?: Injector;
childInjector?: Injector;
componentRef?: ComponentRef<any>;
instance?: any;
angularHostElement?: HTMLElement;
}
}