chore(angular/providers): making progress on angular providers, fix merge issues

* chore(input): remove interface .d.ts file, move to .ts file

* refactor(angular): start working on providers, etc

* chore(angular/dependencies): update deps for angular demo
This commit is contained in:
Dan Bucholtz
2017-11-21 11:58:57 -06:00
committed by GitHub
parent d17dfea4c5
commit f269766068
16 changed files with 1125 additions and 690 deletions

View File

@ -0,0 +1,109 @@
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ComponentFactoryResolver,
Directive,
ElementRef,
Injector,
InjectionToken,
NgZone,
OnInit,
ReflectiveInjector,
ViewContainerRef,
ViewChild
} from '@angular/core';
import { NavController, ViewController } from '@ionic/core';
import { App } from '../providers/app';
import { NavController as InjectableNavController } from '../providers/nav-controller';
@Component({
selector: 'ion-nav',
template: `
<div #viewport class="ng-nav-viewport"></div>
`
})
export class IonNavDelegate {
@ViewChild('viewport', { read: ViewContainerRef}) viewport: ViewContainerRef;
constructor(private crf: ComponentFactoryResolver, private changeDetection: ChangeDetectorRef, private zone: NgZone, private injector: Injector) {
}
ngOnInit() {
const controllerElement = document.querySelector('ion-nav-controller') as any;
controllerElement.delegate = this;
}
attachViewToDom(nav: NavController, enteringView: ViewController): Promise<any> {
return new Promise((resolve, reject) => {
this.zone.run(() => {
const componentProviders = ReflectiveInjector.resolve([
{
provide: NavControllerToken, useValue: nav.element,
},
{
provide: NavController, useFactory: provideNavControllerInjectable, deps: [NavControllerToken]
},
{
provide: AppToken, useValue: null,
},
{
provide: App, useFactory: provideAppInjectable, deps: [AppToken]
}
]);
const componentFactory = this.crf.resolveComponentFactory(enteringView.component);
const childInjector = ReflectiveInjector.fromResolvedProviders(componentProviders, this.viewport.parentInjector);
const componentRef = componentFactory.create(childInjector, []);
this.viewport.insert(componentRef.hostView, this.viewport.length);
this.changeDetection.detectChanges();
(enteringView as any).componentFactory = componentFactory;
(enteringView as any).childInjector = childInjector;
(enteringView as any).componentRef = componentRef;
enteringView.instance = componentRef.instance;
(enteringView as any).angularHostElement = componentRef.location.nativeElement;
enteringView.element = componentRef.location.nativeElement.querySelector('ion-page');
resolve();
});
});
}
removeViewFromDom(nav: NavController, viewController: ViewController) {
return new Promise((resolve, reject) => {
this.zone.run(() => {
(viewController as any).componentRef.destroy();
// (nav.element as HTMLElement).removeChild(viewController.angularHostElement);
(viewController as any).componentFactory = null;
(viewController as any).childInjector = null;
(viewController as any).componentRef = null;
viewController.instance = null;
(viewController as any).angularHostElement = null;
viewController.element = null;
resolve();
});
})
}
}
export const NavControllerToken = new InjectionToken<any>('NavControllerToken');
export const ViewControllerToken = new InjectionToken<any>('ViewControllerToken');
export const AppToken = new InjectionToken<any>('AppToken');
export function provideNavControllerInjectable(element: any) {
return new InjectableNavController(element);
}
export function provideAppInjectable() {
const ionAppElement = document.querySelector('ion-app');
return new App(ionAppElement);
}

View File

@ -0,0 +1,23 @@
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { IonNavDelegate } from './directives/ion-nav';
import { AlertController } from './providers/alert-controller';
@NgModule({
declarations: [
IonNavDelegate
],
exports: [
IonNavDelegate
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
],
providers: [
AlertController
]
})
export class IonicAngularModule {
}

View File

@ -0,0 +1,57 @@
import { AlertOptions } from '@ionic/core';
import { ensureElementInBody, hydrateElement } from '../util/util';
let alertId = 0;
export class AlertController {
create(opts?: AlertOptions): AlertProxy {
return getAlertProxy(opts);
}
}
export function getAlertProxy(opts: AlertOptions){
return {
id: alertId++,
state: PRESENTING,
opts: opts,
present: function() { return present(this)},
dismiss: function() { return dismiss(this)}
}
}
export function present(alertProxy: AlertProxyInternal): Promise<void> {
return loadOverlay(alertProxy.opts).then((alertElement: HTMLIonAlertElement) => {
if (alertProxy.state === PRESENTING) {
return alertElement.present();
}
});
}
export function dismiss(alertProxy: AlertProxyInternal): Promise<void> {
return loadOverlay(alertProxy.opts).then((alertElement: HTMLIonAlertElement) => {
if (alertProxy.state === DISMISSING) {
return alertElement.dismiss();
}
});
}
export function loadOverlay(opts: AlertOptions) {
const element = ensureElementInBody('ion-alert-controller') as HTMLIonAlertControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface AlertProxy {
present(): Promise<void>
dismiss(): Promise<void>
}
export interface AlertProxyInternal extends AlertProxy {
id: number;
opts: AlertOptions;
state: number;
}
export const PRESENTING = 1;
export const DISMISSING = 2;

View File

@ -0,0 +1,40 @@
import { NavContainer } from '@ionic/core';
import { hydrateElement } from '../util/util';
export class App {
constructor(private element: HTMLIonAppElement) {
}
setTitle(title: string) {
document.title = title;
}
isScrolling(): boolean {
if (this.element.isScrolling) {
return this.element.isScrolling();
}
return false;
}
getRootNavs(): NavContainer[] {
if (this.element.getRootNavs) {
return this.element.getRootNavs();
}
return [];
}
getActiveNavs(rootNavId?: number): NavContainer[] {
if (this.element.getActiveNavs) {
return this.element.getActiveNavs(rootNavId);
}
return [];
}
getNavByIdOrName(nameOrId: number | string): NavContainer {
if (this.element.getNavByIdOrName) {
return this.element.getNavByIdOrName(nameOrId);
}
return null;
}
}

View File

@ -0,0 +1,102 @@
import { NavOptions, PublicNavController, ViewController } from '@ionic/core';
import { hydrateElement } from '../util/util';
export class NavController implements PublicNavController {
constructor(public element: HTMLIonNavElement) {
}
push(component: any, data?: any, opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.push(component, data, opts);
});
}
pop(opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.pop(opts);
});
}
setRoot(component: any, data?: any, opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.setRoot(component, data, opts);
});
}
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.insert(insertIndex, page, params, opts);
});
}
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.insertPages(insertIndex, insertPages, opts);
});
}
popToRoot(opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.popToRoot(opts);
});
}
popTo(indexOrViewCtrl: any, opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.popTo(indexOrViewCtrl, opts);
});
}
removeIndex(startIndex: number, removeCount?: number, opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.removeIndex(startIndex, removeCount, opts);
});
}
removeView(viewController: ViewController, opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.removeView(viewController, opts);
});
}
setPages(componentDataPairs: any[], opts?: NavOptions): Promise<any> {
return hydrateElement(this.element).then((navElement: HTMLIonNavElement) => {
return navElement.setPages(componentDataPairs, opts);
});
}
getActive(): ViewController {
if (this.element.getActive) {
return this.element.getActive();
}
return null;
}
getPrevious(view?: ViewController): ViewController {
if (this.element.getPrevious) {
return this.element.getPrevious(view);
}
return null;
}
canGoBack(nav: PublicNavController): boolean {
if (this.element.canGoBack) {
return this.element.canGoBack(nav as any);
}
return false;
}
canSwipeBack(): boolean {
if (this.element.canSwipeBack) {
return this.element.canSwipeBack();
}
return false;
}
getFirstView(): ViewController {
if (this.element.getFirstView) {
return this.element.getFirstView();
}
return null;
}
}

View File

@ -0,0 +1,17 @@
export function hydrateElement(element: any) {
return element.componentOnReady();
}
export function getElement(elementName: string) {
return document.querySelector(elementName);
}
export function ensureElementInBody(elementName: string) {
let element = getElement(elementName);
if (!element) {
element = document.createElement(elementName);
document.body.appendChild(element);
}
return element;
}