mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
Fix some angular (#16615)
* fix(angular): platform types fixes #16535 * fix(angular): memory leak in lifecycle events fixes #16285 * fix ci * single core
This commit is contained in:
@ -60,12 +60,9 @@ jobs:
|
|||||||
working_directory: /tmp/workspace/core
|
working_directory: /tmp/workspace/core
|
||||||
- save_cache: *save-cache-core
|
- save_cache: *save-cache-core
|
||||||
- run:
|
- run:
|
||||||
command: npm run build # --max-workers 1 --debug
|
command: npm run build -- --max-workers 1
|
||||||
working_directory: /tmp/workspace/core
|
working_directory: /tmp/workspace/core
|
||||||
- save_cache: *save-cache-core-stencil
|
- save_cache: *save-cache-core-stencil
|
||||||
- run:
|
|
||||||
command: sudo npm link
|
|
||||||
working_directory: /tmp/workspace/core
|
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: /tmp/workspace
|
root: /tmp/workspace
|
||||||
paths:
|
paths:
|
||||||
@ -80,6 +77,9 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
command: npm install
|
command: npm install
|
||||||
working_directory: /tmp/workspace/angular
|
working_directory: /tmp/workspace/angular
|
||||||
|
- run:
|
||||||
|
command: sudo npm link
|
||||||
|
working_directory: /tmp/workspace/core
|
||||||
- run:
|
- run:
|
||||||
command: sudo npm link @ionic/core
|
command: sudo npm link @ionic/core
|
||||||
working_directory: /tmp/workspace/angular
|
working_directory: /tmp/workspace/angular
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ApplicationRef, ComponentFactoryResolver, Injectable, InjectionToken, Injector, NgZone, ViewContainerRef } from '@angular/core';
|
import { ApplicationRef, ComponentFactoryResolver, Injectable, InjectionToken, Injector, NgZone, ViewContainerRef } from '@angular/core';
|
||||||
import { FrameworkDelegate, ViewLifecycle } from '@ionic/core';
|
import { FrameworkDelegate } from '@ionic/core';
|
||||||
import { NavParams } from '../directives/navigation/nav-params';
|
import { NavParams } from '../directives/navigation/nav-params';
|
||||||
|
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ export class AngularDelegate {
|
|||||||
export class AngularFrameworkDelegate implements FrameworkDelegate {
|
export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||||
|
|
||||||
private elRefMap = new WeakMap<HTMLElement, any>();
|
private elRefMap = new WeakMap<HTMLElement, any>();
|
||||||
|
private elEventsMap = new WeakMap<HTMLElement, () => void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private resolver: ComponentFactoryResolver,
|
private resolver: ComponentFactoryResolver,
|
||||||
@ -37,7 +38,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
const el = attachView(
|
const el = attachView(
|
||||||
this.resolver, this.injector, this.location, this.appRef, this.elRefMap,
|
this.resolver, this.injector, this.location, this.appRef,
|
||||||
|
this.elRefMap, this.elEventsMap,
|
||||||
container, component, params, cssClasses
|
container, component, params, cssClasses
|
||||||
);
|
);
|
||||||
resolve(el);
|
resolve(el);
|
||||||
@ -52,6 +54,11 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
|||||||
if (componentRef) {
|
if (componentRef) {
|
||||||
componentRef.destroy();
|
componentRef.destroy();
|
||||||
this.elRefMap.delete(component);
|
this.elRefMap.delete(component);
|
||||||
|
const unbindEvents = this.elEventsMap.get(component);
|
||||||
|
if (unbindEvents) {
|
||||||
|
unbindEvents();
|
||||||
|
this.elEventsMap.delete(component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@ -65,6 +72,7 @@ export function attachView(
|
|||||||
location: ViewContainerRef | undefined,
|
location: ViewContainerRef | undefined,
|
||||||
appRef: ApplicationRef,
|
appRef: ApplicationRef,
|
||||||
elRefMap: WeakMap<HTMLElement, any>,
|
elRefMap: WeakMap<HTMLElement, any>,
|
||||||
|
elEventsMap: WeakMap<HTMLElement, () => void>,
|
||||||
container: any, component: any, params: any, cssClasses: string[] | undefined
|
container: any, component: any, params: any, cssClasses: string[] | undefined
|
||||||
) {
|
) {
|
||||||
const factory = resolver.resolveComponentFactory(component);
|
const factory = resolver.resolveComponentFactory(component);
|
||||||
@ -86,34 +94,41 @@ export function attachView(
|
|||||||
hostElement.classList.add(clazz);
|
hostElement.classList.add(clazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bindLifecycleEvents(instance, hostElement);
|
const unbindEvents = bindLifecycleEvents(instance, hostElement);
|
||||||
container.appendChild(hostElement);
|
container.appendChild(hostElement);
|
||||||
|
|
||||||
if (!location) {
|
if (!location) {
|
||||||
appRef.attachView(componentRef.hostView);
|
appRef.attachView(componentRef.hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentRef.changeDetectorRef.reattach();
|
componentRef.changeDetectorRef.reattach();
|
||||||
elRefMap.set(hostElement, componentRef);
|
elRefMap.set(hostElement, componentRef);
|
||||||
|
elEventsMap.set(hostElement, unbindEvents);
|
||||||
return hostElement;
|
return hostElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LIFECYCLES = [
|
const LIFECYCLES = [
|
||||||
ViewLifecycle.WillEnter,
|
'ionViewWillEnter',
|
||||||
ViewLifecycle.DidEnter,
|
'ionViewDidEnter',
|
||||||
ViewLifecycle.WillLeave,
|
'ionViewWillLeave',
|
||||||
ViewLifecycle.DidLeave,
|
'ionViewDidLeave',
|
||||||
ViewLifecycle.WillUnload
|
'ionViewWillUnload'
|
||||||
];
|
];
|
||||||
|
|
||||||
export function bindLifecycleEvents(instance: any, element: HTMLElement) {
|
export function bindLifecycleEvents(instance: any, element: HTMLElement) {
|
||||||
LIFECYCLES.forEach(eventName => {
|
const unregisters = LIFECYCLES.map(eventName => {
|
||||||
element.addEventListener(eventName, (ev: any) => {
|
const handler = (ev: any) => {
|
||||||
if (typeof instance[eventName] === 'function') {
|
if (typeof instance[eventName] === 'function') {
|
||||||
instance[eventName](ev.detail);
|
instance[eventName](ev.detail);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
element.addEventListener(eventName, handler);
|
||||||
|
return () => {
|
||||||
|
element.removeEventListener(eventName, handler);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
return () => {
|
||||||
|
unregisters.forEach(fn => fn());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const NavParamsToken = new InjectionToken<any>('NavParamsToken');
|
const NavParamsToken = new InjectionToken<any>('NavParamsToken');
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { EventEmitter, Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { BackButtonDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
|
import { BackButtonDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
|
||||||
import { proxyEvent } from '../util/util';
|
import { proxyEvent } from '../util/util';
|
||||||
|
import { Subject, Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
export interface BackButtonEmitter extends EventEmitter<BackButtonDetail> {
|
export interface BackButtonEmitter extends Subject<BackButtonDetail> {
|
||||||
subscribeWithPriority(priority: number, callback: () => Promise<any> | void): void;
|
subscribeWithPriority(priority: number, callback: () => Promise<any> | void): Subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -15,7 +16,7 @@ export class Platform {
|
|||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
backButton: BackButtonEmitter = new EventEmitter<BackButtonDetail>() as any;
|
backButton: BackButtonEmitter = new Subject<BackButtonDetail>() as any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pause event emits when the native platform puts the application
|
* The pause event emits when the native platform puts the application
|
||||||
@ -23,25 +24,25 @@ export class Platform {
|
|||||||
* application. This event would emit when a Cordova app is put into
|
* application. This event would emit when a Cordova app is put into
|
||||||
* the background, however, it would not fire on a standard web browser.
|
* the background, however, it would not fire on a standard web browser.
|
||||||
*/
|
*/
|
||||||
pause = new EventEmitter<void>();
|
pause = new Subject<void>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The resume event emits when the native platform pulls the application
|
* The resume event emits when the native platform pulls the application
|
||||||
* out from the background. This event would emit when a Cordova app comes
|
* out from the background. This event would emit when a Cordova app comes
|
||||||
* out from the background, however, it would not fire on a standard web browser.
|
* out from the background, however, it would not fire on a standard web browser.
|
||||||
*/
|
*/
|
||||||
resume = new EventEmitter<void>();
|
resume = new Subject<void>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The resize event emits when the browser window has changed dimensions. This
|
* The resize event emits when the browser window has changed dimensions. This
|
||||||
* could be from a browser window being physically resized, or from a device
|
* could be from a browser window being physically resized, or from a device
|
||||||
* changing orientation.
|
* changing orientation.
|
||||||
*/
|
*/
|
||||||
resize = new EventEmitter<void>();
|
resize = new Subject<void>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.backButton.subscribeWithPriority = function(priority, callback) {
|
this.backButton.subscribeWithPriority = function(priority, callback) {
|
||||||
return this.subscribe((ev: BackButtonDetail) => {
|
return this.subscribe(ev => {
|
||||||
ev.register(priority, callback);
|
ev.register(priority, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ElementRef, EventEmitter } from '@angular/core';
|
import { ElementRef } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export function inputs(instance: any, el: ElementRef, props: string[]) {
|
export function inputs(instance: any, el: ElementRef, props: string[]) {
|
||||||
props.forEach(propName => {
|
props.forEach(propName => {
|
||||||
@ -8,13 +9,12 @@ export function inputs(instance: any, el: ElementRef, props: string[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function proxyEvent<T>(emitter: EventEmitter<T>, el: EventTarget, eventName: string) {
|
export function proxyEvent<T>(emitter: Subject<T>, el: EventTarget, eventName: string) {
|
||||||
el.addEventListener(eventName, (ev) => {
|
el.addEventListener(eventName, (ev) => {
|
||||||
emitter.emit(ev ? (ev as any).detail as T : undefined);
|
emitter.next(ev ? (ev as any).detail as T : undefined);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function proxyMethod(ctrlName: string, methodName: string, ...args: any[]) {
|
export function proxyMethod(ctrlName: string, methodName: string, ...args: any[]) {
|
||||||
const controller = ensureElementInBody(ctrlName);
|
const controller = ensureElementInBody(ctrlName);
|
||||||
return controller.componentOnReady()
|
return controller.componentOnReady()
|
||||||
|
Reference in New Issue
Block a user