refactor(framework-delegate): wrap user element with ion-page for modal and nav

This commit is contained in:
Dan Bucholtz
2018-01-04 13:01:31 -06:00
parent ebfb54ff21
commit e64944d6de
6 changed files with 62 additions and 34 deletions

View File

@ -1,7 +1,6 @@
import { import {
ApplicationRef, ApplicationRef,
ComponentFactoryResolver, ComponentFactoryResolver,
ComponentRef,
Injectable, Injectable,
Injector, Injector,
NgZone, NgZone,
@ -12,7 +11,7 @@ import {
import { getProviders } from '../di/di'; import { getProviders } from '../di/di';
import { AngularMountingData } from '../types/interfaces'; import { AngularMountingData } from '../types/interfaces';
const elementToComponentRefMap = new Map<HTMLElement, ComponentRef<any>>(); const elementToComponentRefMap = new Map<HTMLElement, AngularMountingData>();
@Injectable() @Injectable()
export class AngularComponentMounter { export class AngularComponentMounter {
@ -20,14 +19,14 @@ export class AngularComponentMounter {
constructor(private defaultCfr: ComponentFactoryResolver, private zone: NgZone, private appRef: ApplicationRef) { constructor(private defaultCfr: ComponentFactoryResolver, private zone: NgZone, private appRef: ApplicationRef) {
} }
attachViewToDom(parentElement: HTMLElement, hostElement: HTMLElement, componentToMount: Type<any>, componentResolveFactory: ComponentFactoryResolver, injector: Injector, data: any, classesToAdd: string[], wrapUserTemplateInIonPage: boolean): Promise<AngularMountingData> { attachViewToDom(parentElement: HTMLElement, hostElement: HTMLElement, componentToMount: Type<any>, componentResolveFactory: ComponentFactoryResolver, injector: Injector, data: any, classesToAdd: string[], wrapUserComponentInIonPage: boolean): Promise<AngularMountingData> {
return new Promise((resolve) => { return new Promise((resolve) => {
this.zone.run(() => { this.zone.run(() => {
const crf = componentResolveFactory ? componentResolveFactory : this.defaultCfr; const crf = componentResolveFactory ? componentResolveFactory : this.defaultCfr;
const mountingData = attachViewToDom(crf, parentElement, hostElement, componentToMount, injector, this.appRef, data, classesToAdd, wrapUserTemplateInIonPage); const mountingData = attachViewToDom(crf, parentElement, hostElement, componentToMount, injector, this.appRef, data, classesToAdd, wrapUserComponentInIonPage);
resolve(mountingData); resolve(mountingData);
}); });
}); });
@ -45,16 +44,17 @@ export class AngularComponentMounter {
} }
export function removeViewFromDom(parentElement: HTMLElement, childElement: HTMLElement) { export function removeViewFromDom(parentElement: HTMLElement, childElement: HTMLElement) {
const componentRef = elementToComponentRefMap.get(childElement); const mountingData = elementToComponentRefMap.get(childElement);
if (componentRef) { if (mountingData) {
componentRef.destroy(); const componentParent = mountingData.element.parentNode;
if (parentElement.contains(childElement)) { mountingData.componentRef.destroy();
parentElement.removeChild(childElement); if (mountingData.wrapUserComponentInIonPage && parentElement.contains(componentParent) ) {
parentElement.removeChild(componentParent)
} }
} }
} }
export function attachViewToDom(crf: ComponentFactoryResolver, parentElement: HTMLElement, hostElement: HTMLElement, componentToMount: Type<any>, injector: Injector, appRef: ApplicationRef, data: any, classesToAdd: string[], wrapUserTemplateInIonPage: boolean): AngularMountingData { export function attachViewToDom(crf: ComponentFactoryResolver, parentElement: HTMLElement, hostElement: HTMLElement, componentToMount: Type<any>, injector: Injector, appRef: ApplicationRef, data: any, classesToAdd: string[], wrapUserComponentInIonPage: boolean): AngularMountingData {
const componentProviders = ReflectiveInjector.resolve(getProviders(parentElement, data)); const componentProviders = ReflectiveInjector.resolve(getProviders(parentElement, data));
const componentFactory = crf.resolveComponentFactory(componentToMount); const componentFactory = crf.resolveComponentFactory(componentToMount);
@ -62,32 +62,34 @@ export function attachViewToDom(crf: ComponentFactoryResolver, parentElement: HT
hostElement = document.createElement(componentFactory.selector); hostElement = document.createElement(componentFactory.selector);
} }
let mountingElement = hostElement;
if (wrapUserTemplateInIonPage) {
const ionPageElement = document.createElement('ion-page');
hostElement.appendChild(ionPageElement);
mountingElement = ionPageElement;
}
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, injector); const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, injector);
const componentRef = componentFactory.create(childInjector, [], mountingElement); const componentRef = componentFactory.create(childInjector, [], hostElement);
for (const clazz of classesToAdd) { for (const clazz of classesToAdd) {
hostElement.classList.add(clazz); hostElement.classList.add(clazz);
} }
parentElement.appendChild(hostElement); const elementToAppend = wrapUserComponentInIonPage ? getIonPageElement(hostElement) : hostElement;
parentElement.appendChild(elementToAppend);
appRef.attachView(componentRef.hostView); appRef.attachView(componentRef.hostView);
elementToComponentRefMap.set(hostElement, componentRef); const mountingData = {
return {
componentFactory, componentFactory,
childInjector, childInjector,
componentRef, componentRef,
instance: componentRef.instance, instance: componentRef.instance,
angularHostElement: componentRef.location.nativeElement, angularHostElement: componentRef.location.nativeElement,
element: hostElement, element: hostElement,
wrapUserComponentInIonPage
}; };
elementToComponentRefMap.set(hostElement, mountingData);
return mountingData;
} }
export function getIonPageElement(hostElement: HTMLElement) {
const page = document.createElement('ion-page');
page.appendChild(hostElement);
return page;
}

View File

@ -12,4 +12,5 @@ export interface AngularMountingData extends FrameworkMountingData {
componentRef?: ComponentRef<any>; componentRef?: ComponentRef<any>;
instance?: any; instance?: any;
angularHostElement?: HTMLElement; angularHostElement?: HTMLElement;
wrapUserComponentInIonPage?: boolean;
} }

View File

@ -1,21 +1,24 @@
import { FrameworkDelegate, FrameworkMountingData, } from '../index'; import { FrameworkDelegate, FrameworkMountingData, } from '../index';
import { isString } from './helpers'; import { isElementModal, isElementNav, isString } from './helpers';
export class DomFrameworkDelegate implements FrameworkDelegate { export class DomFrameworkDelegate implements FrameworkDelegate {
attachViewToDom(parentElement: HTMLElement, tagOrElement: string | HTMLElement, propsOrDataObj: any = {}, classesToAdd: string[] = []): Promise<FrameworkMountingData> { attachViewToDom(parentElement: HTMLElement, tagOrElement: string | HTMLElement, propsOrDataObj: any = {}, classesToAdd: string[] = []): Promise<FrameworkMountingData> {
return new Promise((resolve) => { return new Promise((resolve) => {
const usersElement = (isString(tagOrElement) ? document.createElement(tagOrElement) : tagOrElement) as HTMLElement; const usersElement = (isString(tagOrElement) ? document.createElement(tagOrElement) : tagOrElement) as HTMLElement;
Object.assign(usersElement, propsOrDataObj); Object.assign(usersElement, propsOrDataObj);
if (classesToAdd.length) { if (classesToAdd.length) {
for (const clazz of classesToAdd) { for (const clazz of classesToAdd) {
usersElement.classList.add(clazz); usersElement.classList.add(clazz);
} }
} }
parentElement.appendChild(usersElement);
const elementToAppend = shouldWrapInIonPage(parentElement) ? createIonPageAndAppendUserElement(usersElement) : usersElement;
parentElement.appendChild(elementToAppend);
resolve({ resolve({
element: usersElement element: elementToAppend
}); });
}); });
} }
@ -27,3 +30,13 @@ export class DomFrameworkDelegate implements FrameworkDelegate {
}); });
} }
} }
export function shouldWrapInIonPage(element: HTMLElement) {
return isElementModal(element) || isElementNav(element);
}
export function createIonPageAndAppendUserElement(userElement: HTMLElement) {
const wrappingElement = document.createElement('ion-page');
wrappingElement.appendChild(userElement);
return wrappingElement;
}

View File

@ -24,6 +24,14 @@ export function isStringOrNumber(v: any): v is (string | number) { return isStri
export function isBlank(val: any): val is null { return val === undefined || val === null; } export function isBlank(val: any): val is null { return val === undefined || val === null; }
export function isElementNav(element: HTMLElement) {
return element.tagName.toUpperCase() === 'ION-NAV';
}
export function isElementModal(element: HTMLElement) {
return element.classList.contains('modal-wrapper');
}
/** @hidden */ /** @hidden */
export function isCheckedProperty(a: any, b: any): boolean { export function isCheckedProperty(a: any, b: any): boolean {
if (a === undefined || a === null || a === '') { if (a === undefined || a === null || a === '') {

View File

@ -2,10 +2,9 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { FrameworkDelegate } from '@ionic/core'; import { FrameworkDelegate } from '@ionic/core';
import { isElementModal, isElementNav } from './utils/helpers';
export function attachViewToDom(parentElement: HTMLElement, reactComponent: any, propsOrData: any, classesToAdd: string[]) { export function attachViewToDom(parentElement: HTMLElement, reactComponent: any, propsOrData: any, classesToAdd: string[]) {
console.log('parentElement: ', parentElement);
console.log('reactComponent: ', reactComponent);
const wrappingDiv = shouldWrapInIonPage(parentElement) ? document.createElement('ion-page') : document.createElement('div'); const wrappingDiv = shouldWrapInIonPage(parentElement) ? document.createElement('ion-page') : document.createElement('div');
if (classesToAdd) { if (classesToAdd) {
for (const clazz of classesToAdd) { for (const clazz of classesToAdd) {
@ -41,8 +40,5 @@ export { Delegate }
export function shouldWrapInIonPage(element: HTMLElement) { export function shouldWrapInIonPage(element: HTMLElement) {
if (element.tagName.toUpperCase() === 'ION-NAV' || element.classList.contains('modal-wrapper')) { return isElementModal(element) || isElementNav(element);
return true; }
}
return false;
}

View File

@ -7,4 +7,12 @@ export function getOrAppendElement(tagName: string): Element {
const tmp = document.createElement(tagName); const tmp = document.createElement(tagName);
document.body.appendChild(tmp); document.body.appendChild(tmp);
return tmp; return tmp;
} }
export function isElementNav(element: HTMLElement) {
return element.tagName.toUpperCase() === 'ION-NAV';
}
export function isElementModal(element: HTMLElement) {
return element.classList.contains('modal-wrapper');
}