refactor(navigation): make the framework delegate more generic

This commit is contained in:
Dan Bucholtz
2017-12-07 12:06:02 -06:00
parent 02d34a26aa
commit 14a67e1ff5
12 changed files with 140 additions and 53 deletions

View File

@ -1,5 +1,8 @@
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core'; import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import {
ControlValueAccessor,
NG_VALUE_ACCESSOR
} from '@angular/forms';
@Directive({ @Directive({
/* tslint:disable-next-line:directive-selector */ /* tslint:disable-next-line:directive-selector */

View File

@ -12,5 +12,4 @@ export interface AngularViewController extends PublicViewController {
componentRef?: ComponentRef<any>; componentRef?: ComponentRef<any>;
instance?: any; instance?: any;
angularHostElement?: HTMLElement; angularHostElement?: HTMLElement;
} }

View File

@ -14,9 +14,9 @@
} }
}, },
"@stencil/core": { "@stencil/core": {
"version": "0.0.8-10", "version": "0.0.8-12",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-0.0.8-10.tgz", "resolved": "https://registry.npmjs.org/@stencil/core/-/core-0.0.8-12.tgz",
"integrity": "sha512-SfZt+0bFaicndqncbWwRIOD1bb4ds3YUjAEYBiouonI392WxKLKHIHo1l2ONgtqQLa8yKrR71ZUatbdfRIXyXw==", "integrity": "sha512-hVHl80opV8ZKa5dCyLZW0V+Fx8ePsTzIjZrQ94+bSsDvi6y3Eo4IK1CEsoG5CxfwZhQQyqE42miWkI9sRZRdsw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chokidar": "1.7.0", "chokidar": "1.7.0",

View File

@ -1622,8 +1622,8 @@ declare global {
export interface IonModalAttributes extends HTMLAttributes { export interface IonModalAttributes extends HTMLAttributes {
mode?: string; mode?: string;
color?: string; color?: string;
component?: string; component?: any;
componentProps?: any; data?: any;
cssClass?: string; cssClass?: string;
enableBackdropDismiss?: boolean; enableBackdropDismiss?: boolean;
modalId?: string; modalId?: string;

View File

@ -62,8 +62,8 @@ export class Modal {
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop() mode: string; @Prop() mode: string;
@Prop() color: string; @Prop() color: string;
@Prop() component: string; @Prop() component: any;
@Prop() componentProps: any = {}; @Prop() data: any = {};
@Prop() cssClass: string; @Prop() cssClass: string;
@Prop() enableBackdropDismiss: boolean = true; @Prop() enableBackdropDismiss: boolean = true;
@ -161,15 +161,7 @@ export class Modal {
} }
render() { render() {
const ThisComponent = this.component;
let userCssClasses = 'ion-page';
if (this.cssClass) {
userCssClasses += ` ${this.cssClass}`;
}
const dialogClasses = createThemedClasses(this.mode, this.color, 'modal-wrapper'); const dialogClasses = createThemedClasses(this.mode, this.color, 'modal-wrapper');
return [ return [
<div <div
onClick={this.backdropClick.bind(this)} onClick={this.backdropClick.bind(this)}
@ -182,11 +174,6 @@ export class Modal {
role='dialog' role='dialog'
class={dialogClasses} class={dialogClasses}
> >
<ThisComponent
{...this.componentProps}
class={userCssClasses}
>
</ThisComponent>
</div> </div>
]; ];
} }
@ -194,8 +181,8 @@ export class Modal {
export interface ModalOptions { export interface ModalOptions {
component: string; component: any;
componentProps?: any; data?: any;
showBackdrop?: boolean; showBackdrop?: boolean;
enableBackdropDismiss?: boolean; enableBackdropDismiss?: boolean;
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;

View File

@ -19,11 +19,6 @@ string
#### component #### component
string
#### componentProps
any any
@ -32,6 +27,11 @@ any
string string
#### data
any
#### enableBackdropDismiss #### enableBackdropDismiss
boolean boolean
@ -76,11 +76,6 @@ string
#### component #### component
string
#### componentProps
any any
@ -89,6 +84,11 @@ any
string string
#### data
any
#### enableBackdropDismiss #### enableBackdropDismiss
boolean boolean

View File

@ -1,18 +1,28 @@
import { FrameworkDelegate, Nav, ViewController } from '../../index'; import { FrameworkDelegate, FrameworkMountingData, } from '../../index';
import { isString } from '../../utils/helpers';
export class DomFrameworkDelegate implements FrameworkDelegate { export class DomFrameworkDelegate implements FrameworkDelegate {
attachViewToDom(nav: Nav, enteringView: ViewController): Promise<any> { attachViewToDom(parentElement: HTMLElement, tagOrElement: string | HTMLElement, classesToAdd: string[] = []): Promise<FrameworkMountingData> {
return new Promise((resolve) => { return new Promise((resolve) => {
const usersElement = document.createElement(enteringView.component) as HTMLElement; const usersElement = (isString(tagOrElement) ? document.createElement(tagOrElement) : tagOrElement) as HTMLElement;
usersElement.classList.add('ion-page'); if (classesToAdd.length) {
enteringView.element = usersElement; for (const clazz of classesToAdd) {
nav.element.appendChild(usersElement); usersElement.classList.add(clazz);
resolve(); }
}
parentElement.appendChild(usersElement);
resolve({
element: usersElement
});
}); });
} }
removeViewFromDom(nav: Nav, leavingView: ViewController): Promise<any> { removeViewFromDom(parentElement: HTMLElement, childElement: HTMLElement): Promise<FrameworkMountingData> {
nav.element.removeChild(leavingView.element);
return Promise.resolve(); parentElement.removeChild(childElement);
return Promise.resolve({
element: null
});
} }
} }

View File

@ -6,11 +6,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="/dist/ionic.js"></script> <script src="/dist/ionic.js"></script>
</head> </head>
<body> <body onload="loadFirstPage()">
<ion-nav-delegate></ion-nav-delegate>
<ion-nav-controller></ion-nav-controller> <ion-nav-controller></ion-nav-controller>
<ion-app> <ion-app>
<ion-nav root="page-one"></ion-nav> <ion-nav></ion-nav>
</ion-app> </ion-app>
<style> <style>
f { f {
@ -26,4 +25,87 @@
} }
</style> </style>
</body> </body>
<script>
async function loadFirstPage() {
const nav = document.querySelector('ion-nav');
await nav.componentOnReady();
const firstPage = document.createElement('div');
firstPage.classList.add('first-page');
firstPage.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Page One</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h1>Page One</h1>
<ion-button>Go to Page Two</ion-button>
</ion-content>
`;
await nav.push(firstPage);
// okay cool, we're in the DOM now
const button = firstPage.querySelector('ion-button');
button.addEventListener('click', async () => {
await goToPageTwo(nav);
});
}
async function goToPageTwo(nav) {
const secondPage = document.createElement('div');
secondPage.classList.add('second-page');
secondPage.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Page Two</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h1>Page Two</h1>
<ion-button class="next">Go to Page Three</ion-button>
<ion-button class="previous">Go Back</ion-button>
</ion-content>
`;
// okay cool, we're in the DOM now
await nav.push(secondPage);
const previousButton = secondPage.querySelector('ion-button .previous');
previousButton.addEventListener('click', async () => {
await nav.pop();
});
const nextButton = secondPage.querySelector('ion-button .next');
nextButton.addEventListener('click', async () => {
await goToPageThree(nav);
});
}
async function goToPageThree(nav) {
const thirdPage = document.createElement('div');
thirdPage.classList.add('third-page');
thirdPage.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Page Three</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h1>Page Three</h1>
<ion-button class="previous">Go Back</ion-button>
</ion-content>
`;
// okay cool, we're in the DOM now
await nav.push(thirdPage);
const previousButton = thirdPage.querySelector('ion-button .previous');
previousButton.addEventListener('click', async () => {
await nav.pop();
});
}
</script>
</html> </html>

View File

@ -446,7 +446,8 @@ export function fireViewWillLifecycles(enteringView: ViewController, leavingView
export function attachViewToDom(nav: Nav, enteringView: ViewController, delegate: FrameworkDelegate) { export function attachViewToDom(nav: Nav, enteringView: ViewController, delegate: FrameworkDelegate) {
if (enteringView && enteringView.state === STATE_NEW) { if (enteringView && enteringView.state === STATE_NEW) {
return delegate.attachViewToDom(nav, enteringView).then(() => { return delegate.attachViewToDom(nav.element, enteringView.component, ['ion-page']).then((mountingData) => {
Object.assign(enteringView, mountingData);
enteringView.state = STATE_ATTACHED; enteringView.state = STATE_ATTACHED;
}); });
} }

View File

@ -6,8 +6,13 @@ import {
} from '../index'; } from '../index';
export interface FrameworkDelegate { export interface FrameworkDelegate {
attachViewToDom(navController: Nav, enteringView: ViewController): Promise<any>;
removeViewFromDom(navController: Nav, leavingView: ViewController): Promise<any>; attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, classesToAdd?: string[]): Promise<FrameworkMountingData>;
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any): Promise<FrameworkMountingData>;
}
export interface FrameworkMountingData {
element: HTMLElement;
} }
export interface NavContainer { export interface NavContainer {

View File

@ -99,7 +99,7 @@ export function dismiss(navCtrl: any, dismissProxy: any, data?: any, role?: stri
export function destroy(viewController: ViewController, delegate?: FrameworkDelegate): Promise<any> { export function destroy(viewController: ViewController, delegate?: FrameworkDelegate): Promise<any> {
assert(viewController.state !== STATE_DESTROYED, 'view state must be attached'); assert(viewController.state !== STATE_DESTROYED, 'view state must be attached');
return delegate ? delegate.removeViewFromDom(viewController.nav, viewController) : Promise.resolve().then(() => { return delegate ? delegate.removeViewFromDom(viewController.nav.element, viewController.element) : Promise.resolve().then(() => {
if (viewController.component) { if (viewController.component) {
// TODO - consider removing classes and styles as thats what we do in ionic-angular // TODO - consider removing classes and styles as thats what we do in ionic-angular

View File

@ -1,5 +1,5 @@
{ {
"name": "demo", "name": "@ionic/angular-demo",
"version": "0.0.0", "version": "0.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,