mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 17:42:15 +08:00
refactor(navigation): get rid of ion-nav-controller, get nav working correctly in DOM, angular, react
This commit is contained in:
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
]
|
||||
|
59
packages/angular/src/providers/angular-component-mounter.ts
Normal file
59
packages/angular/src/providers/angular-component-mounter.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
|
@ -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>;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user