Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(angular): strict template event bindings #24314

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 19 additions & 10 deletions angular/src/directives/angular-component-lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/* eslint-disable */
/* tslint:disable */
import { fromEvent } from 'rxjs';

export const proxyInputs = (Cmp: any, inputs: string[]) => {
const Prototype = Cmp.prototype;
inputs.forEach(item => {
Expand All @@ -28,17 +26,28 @@ export const proxyMethods = (Cmp: any, methods: string[]) => {
});
};

export const proxyOutputs = (instance: any, el: any, events: string[]) => {
events.forEach(eventName => instance[eventName] = fromEvent(el, eventName));
export const defineCustomElement = (tagName: string, customElement: any) => {
if (
customElement !== undefined &&
typeof customElements !== 'undefined' &&
!customElements.get(tagName)
) {
customElements.define(tagName, customElement);
}
}

export function ProxyCmp(opts: { inputs?: any; methods?: any }) {
const decorator = function(cls: any) {
if (opts.inputs) {
proxyInputs(cls, opts.inputs);
// tslint:disable-next-line: only-arrow-functions
export function ProxyCmp(opts: { tagName: string, customElement?: any, inputs?: any; methods?: any }) {
const decorator = function (cls: any) {
const { tagName, customElement, inputs, methods } = opts;

defineCustomElement(tagName, customElement);

if (inputs) {
proxyInputs(cls, inputs);
}
if (opts.methods) {
proxyMethods(cls, opts.methods);
if (methods) {
proxyMethods(cls, methods);
}
return cls;
};
Expand Down
17 changes: 14 additions & 3 deletions angular/src/directives/navigation/nav-delegate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { ComponentFactoryResolver, ElementRef, Injector, ViewContainerRef, Directive } from '@angular/core';
import {
ComponentFactoryResolver,
ElementRef,
Injector,
ViewContainerRef,
Directive,
Output,
EventEmitter,
} from '@angular/core';

import { AngularDelegate } from '../../providers/angular-delegate';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { ProxyCmp } from '../angular-component-lib/utils';

@ProxyCmp({
tagName: 'ion-nav',
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
methods: [
'push',
Expand All @@ -26,6 +35,9 @@ import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class NavDelegate {
@Output() ionNavDidChange = new EventEmitter<CustomEvent>();
@Output() ionNavWillChange = new EventEmitter<CustomEvent>();

protected el: HTMLElement;
constructor(
ref: ElementRef,
Expand All @@ -36,6 +48,5 @@ export class NavDelegate {
) {
this.el = ref.nativeElement;
ref.nativeElement.delegate = angularDelegate.create(resolver, injector, location);
proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);
}
}
83 changes: 37 additions & 46 deletions angular/src/directives/overlays/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,15 @@ import {
ElementRef,
EventEmitter,
NgZone,
Output,
TemplateRef,
} from '@angular/core';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { ProxyCmp } from '../angular-component-lib/utils';
import { Components } from '@ionic/core';

export declare interface IonModal extends Components.IonModal {
/**
* Emitted after the modal has presented.
**/
ionModalDidPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has presented.
*/
ionModalWillPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has dismissed.
*/
ionModalWillDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the modal has dismissed.
*/
ionModalDidDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the modal has presented. Shorthand for ionModalWillDismiss.
*/
didPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has presented. Shorthand for ionModalWillPresent.
*/
willPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss.
*/
willDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the modal has dismissed. Shorthand for ionModalDidDismiss.
*/
didDismiss: EventEmitter<CustomEvent>;
}
export declare interface IonModal extends Components.IonModal {}
@ProxyCmp({
tagName: 'ion-modal',
inputs: [
'animated',
'backdropBreakpoint',
Expand Down Expand Up @@ -96,6 +65,39 @@ export declare interface IonModal extends Components.IonModal {
],
})
export class IonModal {
/**
* Emitted after the modal has presented.
**/
@Output() ionModalDidPresent = new EventEmitter<CustomEvent>();
/**
* Emitted before the modal has presented.
*/
@Output() ionModalWillPresent = new EventEmitter<CustomEvent>();
/**
* Emitted before the modal has dismissed.
*/
@Output() ionModalWillDismiss = new EventEmitter<CustomEvent>();
/**
* Emitted after the modal has dismissed.
*/
@Output() ionModalDidDismiss = new EventEmitter<CustomEvent>();
/**
* Emitted after the modal has presented. Shorthand for ionModalWillDismiss.
*/
@Output() didPresent = new EventEmitter<CustomEvent>();
/**
* Emitted before the modal has presented. Shorthand for ionModalWillPresent.
*/
@Output() willPresent = new EventEmitter<CustomEvent>();
/**
* Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss.
*/
@Output() willDismiss = new EventEmitter<CustomEvent>();
/**
* Emitted after the modal has dismissed. Shorthand for ionModalDidDismiss.
*/
@Output() didDismiss = new EventEmitter<CustomEvent>();

@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;

isCmpOpen: boolean = false;
Expand All @@ -114,16 +116,5 @@ export class IonModal {
this.isCmpOpen = false;
c.detectChanges();
});

proxyOutputs(this, this.el, [
'ionModalDidPresent',
'ionModalWillPresent',
'ionModalWillDismiss',
'ionModalDidDismiss',
'didPresent',
'willPresent',
'willDismiss',
'didDismiss',
]);
}
}
82 changes: 37 additions & 45 deletions angular/src/directives/overlays/popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,14 @@ import {
ElementRef,
EventEmitter,
NgZone,
Output,
TemplateRef,
} from '@angular/core';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { ProxyCmp } from '../angular-component-lib/utils';
import { Components } from '@ionic/core';
export declare interface IonPopover extends Components.IonPopover {
/**
* Emitted after the popover has presented.
*/
ionPopoverDidPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the popover has presented.
*/
ionPopoverWillPresent: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has dismissed.
*/
ionPopoverWillDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has dismissed.
*/
ionPopoverDidDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
*/
didPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the popover has presented. Shorthand for ionPopoverWillPresent.
*/
willPresent: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
*/
willDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has dismissed. Shorthand for ionPopoverDidDismiss.
*/
didDismiss: EventEmitter<CustomEvent>;
}
export declare interface IonPopover extends Components.IonPopover {}
@ProxyCmp({
tagName: 'ion-popover',
inputs: [
'alignment',
'animated',
Expand Down Expand Up @@ -95,6 +64,39 @@ export declare interface IonPopover extends Components.IonPopover {
],
})
export class IonPopover {
/**
* Emitted after the popover has presented.
*/
@Output() ionPopoverDidPresent = new EventEmitter<CustomEvent>();
/**
* Emitted before the popover has presented.
*/
@Output() ionPopoverWillPresent = new EventEmitter<CustomEvent>();
/**
* Emitted after the popover has dismissed.
*/
@Output() ionPopoverWillDismiss = new EventEmitter<CustomEvent>();
/**
* Emitted after the popover has dismissed.
*/
@Output() onPopoverDidDismiss = new EventEmitter<CustomEvent>();
/**
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
*/
@Output() didPresent = new EventEmitter<CustomEvent>();
/**
* Emitted before the popover has presented. Shorthand for ionPopoverWillPresent.
*/
@Output() willPresent = new EventEmitter<CustomEvent>();
/**
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
*/
@Output() willDismiss = new EventEmitter<CustomEvent>();
/**
* Emitted after the popover has dismissed. Shorthand for ionPopoverDidDismiss.
*/
@Output() didDismiss = new EventEmitter<CustomEvent>();

@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;

isCmpOpen: boolean = false;
Expand All @@ -113,15 +115,5 @@ export class IonPopover {
this.isCmpOpen = false;
c.detectChanges();
});
proxyOutputs(this, this.el, [
'ionPopoverDidPresent',
'ionPopoverWillPresent',
'ionPopoverWillDismiss',
'ionPopoverDidDismiss',
'didPresent',
'willPresent',
'willDismiss',
'didDismiss',
]);
}
}