perf(ivy): chain listener instructions (#33720)
Chains multiple listener instructions on a particular element into a single call which results in less generated code. Also handles listeners on templates, host listeners and synthetic host listeners. PR Close #33720
This commit is contained in:
parent
ccee818034
commit
e31f62045d
|
@ -1134,6 +1134,113 @@ describe('compiler compliance: bindings', () => {
|
||||||
expectEmit(result.source, template, 'Incorrect template');
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should chain multiple host listeners into a single instruction', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'example.ts': `
|
||||||
|
import {Directive, HostListener} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[my-dir]',
|
||||||
|
host: {
|
||||||
|
'(mousedown)': 'mousedown()',
|
||||||
|
'(mouseup)': 'mouseup()',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class MyDirective {
|
||||||
|
mousedown() {}
|
||||||
|
mouseup() {}
|
||||||
|
|
||||||
|
@HostListener('click')
|
||||||
|
click() {}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const template = `
|
||||||
|
…
|
||||||
|
hostBindings: function MyDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵlistener("mousedown", function MyDirective_mousedown_HostBindingHandler($event) { return ctx.mousedown(); })("mouseup", function MyDirective_mouseup_HostBindingHandler($event) { return ctx.mouseup(); })("click", function MyDirective_click_HostBindingHandler($event) { return ctx.click(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should chain multiple synthetic host listeners into a single instruction', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'example.ts': `
|
||||||
|
import {Component, HostListener} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '',
|
||||||
|
host: {
|
||||||
|
'(@animation.done)': 'done()',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
@HostListener('@animation.start')
|
||||||
|
start() {}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const template = `
|
||||||
|
…
|
||||||
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵcomponentHostSyntheticListener("@animation.done", function MyComponent_animation_animation_done_HostBindingHandler($event) { return ctx.done(); })("@animation.start", function MyComponent_animation_animation_start_HostBindingHandler($event) { return ctx.start(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should chain multiple regular and synthetic host listeners into two instructions', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'example.ts': `
|
||||||
|
import {Component, HostListener} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '',
|
||||||
|
host: {
|
||||||
|
'(mousedown)': 'mousedown()',
|
||||||
|
'(@animation.done)': 'done()',
|
||||||
|
'(mouseup)': 'mouseup()',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
@HostListener('@animation.start')
|
||||||
|
start() {}
|
||||||
|
|
||||||
|
@HostListener('click')
|
||||||
|
click() {}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const template = `
|
||||||
|
…
|
||||||
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵcomponentHostSyntheticListener("@animation.done", function MyComponent_animation_animation_done_HostBindingHandler($event) { return ctx.done(); })("@animation.start", function MyComponent_animation_animation_start_HostBindingHandler($event) { return ctx.start(); });
|
||||||
|
$r3$.ɵɵlistener("mousedown", function MyComponent_mousedown_HostBindingHandler($event) { return ctx.mousedown(); })("mouseup", function MyComponent_mouseup_HostBindingHandler($event) { return ctx.mouseup(); })("click", function MyComponent_click_HostBindingHandler($event) { return ctx.click(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('non bindable behavior', () => {
|
describe('non bindable behavior', () => {
|
||||||
|
|
|
@ -229,4 +229,119 @@ describe('compiler compliance: listen()', () => {
|
||||||
expectEmit(source, MyComponentFactory, 'Incorrect MyComponent.ɵfac');
|
expectEmit(source, MyComponentFactory, 'Incorrect MyComponent.ɵfac');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should chain multiple listeners on the same element', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<div (click)="click()" (change)="change()"></div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
…
|
||||||
|
consts: [[${AttributeMarker.Bindings}, "click", "change"]],
|
||||||
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵelementStart(0, "div", 0);
|
||||||
|
$r3$.ɵɵlistener("click", function MyComponent_Template_div_click_0_listener($event) {
|
||||||
|
return ctx.click();
|
||||||
|
})("change", function MyComponent_Template_div_change_0_listener($event) {
|
||||||
|
return ctx.change();
|
||||||
|
});
|
||||||
|
$r3$.ɵɵelementEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should chain multiple listeners across elements', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`
|
||||||
|
<div (click)="click()" (change)="change()"></div>
|
||||||
|
<some-comp (update)="update()" (delete)="delete()"></some-comp>
|
||||||
|
\`
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
…
|
||||||
|
consts: [[${AttributeMarker.Bindings}, "click", "change"], [${AttributeMarker.Bindings}, "update", "delete"]],
|
||||||
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵelementStart(0, "div", 0);
|
||||||
|
$r3$.ɵɵlistener("click", function MyComponent_Template_div_click_0_listener($event) { return ctx.click(); })("change", function MyComponent_Template_div_change_0_listener($event) { return ctx.change(); });
|
||||||
|
$r3$.ɵɵelementEnd();
|
||||||
|
$r3$.ɵɵelementStart(1, "some-comp", 1);
|
||||||
|
$r3$.ɵɵlistener("update", function MyComponent_Template_some_comp_update_1_listener($event) { return ctx.update(); })("delete", function MyComponent_Template_some_comp_delete_1_listener($event) { return ctx.delete(); });
|
||||||
|
$r3$.ɵɵelementEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should chain multiple listeners on the same template', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<ng-template (click)="click()" (change)="change()"></ng-template>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
…
|
||||||
|
consts: [[${AttributeMarker.Bindings}, "click", "change"]],
|
||||||
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", 0);
|
||||||
|
$r3$.ɵɵlistener("click", function MyComponent_Template_ng_template_click_0_listener($event) { return ctx.click(); })("change", function MyComponent_Template_ng_template_change_0_listener($event) { return ctx.change(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -277,8 +277,7 @@ describe('compiler compliance: styling', () => {
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelementStart(0, "div");
|
||||||
$r3$.ɵɵlistener("@myAnimation.start", function MyComponent_Template_div_animation_myAnimation_start_0_listener($event) { return ctx.onStart($event); });
|
$r3$.ɵɵlistener("@myAnimation.start", function MyComponent_Template_div_animation_myAnimation_start_0_listener($event) { return ctx.onStart($event); })("@myAnimation.done", function MyComponent_Template_div_animation_myAnimation_done_0_listener($event) { return ctx.onDone($event); });
|
||||||
$r3$.ɵɵlistener("@myAnimation.done", function MyComponent_Template_div_animation_myAnimation_done_0_listener($event) { return ctx.onDone($event); });
|
|
||||||
$r3$.ɵɵelementEnd();
|
$r3$.ɵɵelementEnd();
|
||||||
} if (rf & 2) {
|
} if (rf & 2) {
|
||||||
$r3$.ɵɵproperty("@myAnimation", ctx.exp);
|
$r3$.ɵɵproperty("@myAnimation", ctx.exp);
|
||||||
|
@ -337,8 +336,7 @@ describe('compiler compliance: styling', () => {
|
||||||
hostBindings: function MyAnimDir_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyAnimDir_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(1);
|
$r3$.ɵɵallocHostVars(1);
|
||||||
$r3$.ɵɵcomponentHostSyntheticListener("@myAnim.start", function MyAnimDir_animation_myAnim_start_HostBindingHandler($event) { return ctx.onStart(); });
|
$r3$.ɵɵcomponentHostSyntheticListener("@myAnim.start", function MyAnimDir_animation_myAnim_start_HostBindingHandler($event) { return ctx.onStart(); })("@myAnim.done", function MyAnimDir_animation_myAnim_done_HostBindingHandler($event) { return ctx.onDone(); });
|
||||||
$r3$.ɵɵcomponentHostSyntheticListener("@myAnim.done", function MyAnimDir_animation_myAnim_done_HostBindingHandler($event) { return ctx.onDone(); });
|
|
||||||
} if (rf & 2) {
|
} if (rf & 2) {
|
||||||
$r3$.ɵɵupdateSyntheticHostBinding("@myAnim", ctx.myAnimState);
|
$r3$.ɵɵupdateSyntheticHostBinding("@myAnim", ctx.myAnimState);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2041,9 +2041,7 @@ runInEachFileSystem(os => {
|
||||||
const hostBindingsFn = `
|
const hostBindingsFn = `
|
||||||
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick(); });
|
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick(); })("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onDocumentClick($event.target); }, false, i0.ɵɵresolveDocument)("scroll", function FooCmp_scroll_HostBindingHandler($event) { return ctx.onWindowScroll(); }, false, i0.ɵɵresolveWindow);
|
||||||
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onDocumentClick($event.target); }, false, i0.ɵɵresolveDocument);
|
|
||||||
i0.ɵɵlistener("scroll", function FooCmp_scroll_HostBindingHandler($event) { return ctx.onWindowScroll(); }, false, i0.ɵɵresolveWindow);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -2136,9 +2134,7 @@ runInEachFileSystem(os => {
|
||||||
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
i0.ɵɵallocHostVars(3);
|
i0.ɵɵallocHostVars(3);
|
||||||
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event); });
|
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event); })("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onBodyClick($event); }, false, i0.ɵɵresolveBody)("change", function FooCmp_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg1, ctx.arg2, ctx.arg3); });
|
||||||
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onBodyClick($event); }, false, i0.ɵɵresolveBody);
|
|
||||||
i0.ɵɵlistener("change", function FooCmp_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg1, ctx.arg2, ctx.arg3); });
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
i0.ɵɵhostProperty("prop", ctx.bar);
|
i0.ɵɵhostProperty("prop", ctx.bar);
|
||||||
|
@ -3144,7 +3140,7 @@ runInEachFileSystem(os => {
|
||||||
|
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: '<div #ref="unknownTarget"></div>',
|
template: '<div #ref="unknownTarget"></div>',
|
||||||
})
|
})
|
||||||
|
|
|
@ -733,17 +733,35 @@ function getBindingNameAndInstruction(binding: ParsedProperty):
|
||||||
}
|
}
|
||||||
|
|
||||||
function createHostListeners(eventBindings: ParsedEvent[], name?: string): o.Statement[] {
|
function createHostListeners(eventBindings: ParsedEvent[], name?: string): o.Statement[] {
|
||||||
return eventBindings.map(binding => {
|
const listeners: o.Expression[][] = [];
|
||||||
|
const syntheticListeners: o.Expression[][] = [];
|
||||||
|
const instructions: o.Statement[] = [];
|
||||||
|
|
||||||
|
eventBindings.forEach(binding => {
|
||||||
let bindingName = binding.name && sanitizeIdentifier(binding.name);
|
let bindingName = binding.name && sanitizeIdentifier(binding.name);
|
||||||
const bindingFnName = binding.type === ParsedEventType.Animation ?
|
const bindingFnName = binding.type === ParsedEventType.Animation ?
|
||||||
prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
|
prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
|
||||||
bindingName;
|
bindingName;
|
||||||
const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
|
const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
|
||||||
const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
|
const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
|
||||||
const instruction =
|
|
||||||
binding.type == ParsedEventType.Animation ? R3.componentHostSyntheticListener : R3.listener;
|
if (binding.type == ParsedEventType.Animation) {
|
||||||
return o.importExpr(instruction).callFn(params).toStmt();
|
syntheticListeners.push(params);
|
||||||
|
} else {
|
||||||
|
listeners.push(params);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (syntheticListeners.length > 0) {
|
||||||
|
instructions.push(
|
||||||
|
chainedInstruction(R3.componentHostSyntheticListener, syntheticListeners).toStmt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listeners.length > 0) {
|
||||||
|
instructions.push(chainedInstruction(R3.listener, listeners).toStmt());
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function metadataAsSummary(meta: R3HostMetadata): CompileDirectiveSummary {
|
function metadataAsSummary(meta: R3HostMetadata): CompileDirectiveSummary {
|
||||||
|
|
|
@ -684,11 +684,14 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Listeners (outputs)
|
// Generate Listeners (outputs)
|
||||||
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
if (element.outputs.length > 0) {
|
||||||
this.creationInstruction(
|
const listeners = element.outputs.map(
|
||||||
outputAst.sourceSpan, R3.listener,
|
(outputAst: t.BoundEvent) => ({
|
||||||
this.prepareListenerParameter(element.name, outputAst, elementIndex));
|
sourceSpan: outputAst.sourceSpan,
|
||||||
});
|
params: this.prepareListenerParameter(element.name, outputAst, elementIndex)
|
||||||
|
}));
|
||||||
|
this.creationInstructionChain(R3.listener, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
// Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
|
// Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
|
||||||
// listeners, to make sure i18nAttributes instruction targets current element at runtime.
|
// listeners, to make sure i18nAttributes instruction targets current element at runtime.
|
||||||
|
@ -912,11 +915,14 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
// Add the input bindings
|
// Add the input bindings
|
||||||
this.templatePropertyBindings(templateIndex, template.inputs);
|
this.templatePropertyBindings(templateIndex, template.inputs);
|
||||||
// Generate listeners for directive output
|
// Generate listeners for directive output
|
||||||
template.outputs.forEach((outputAst: t.BoundEvent) => {
|
if (template.outputs.length > 0) {
|
||||||
this.creationInstruction(
|
const listeners = template.outputs.map(
|
||||||
outputAst.sourceSpan, R3.listener,
|
(outputAst: t.BoundEvent) => ({
|
||||||
this.prepareListenerParameter('ng_template', outputAst, templateIndex));
|
sourceSpan: outputAst.sourceSpan,
|
||||||
});
|
params: this.prepareListenerParameter('ng_template', outputAst, templateIndex)
|
||||||
|
}));
|
||||||
|
this.creationInstructionChain(R3.listener, listeners);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,6 +1098,16 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend);
|
this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private creationInstructionChain(reference: o.ExternalReference, calls: {
|
||||||
|
sourceSpan: ParseSourceSpan | null,
|
||||||
|
params: () => o.Expression[]
|
||||||
|
}[]) {
|
||||||
|
const span = calls.length ? calls[0].sourceSpan : null;
|
||||||
|
this._creationCodeFns.push(() => {
|
||||||
|
return chainedInstruction(reference, calls.map(call => call.params()), span).toStmt();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private updateInstructionWithAdvance(
|
private updateInstructionWithAdvance(
|
||||||
nodeIndex: number, span: ParseSourceSpan|null, reference: o.ExternalReference,
|
nodeIndex: number, span: ParseSourceSpan|null, reference: o.ExternalReference,
|
||||||
paramsOrFn?: o.Expression[]|(() => o.Expression[])) {
|
paramsOrFn?: o.Expression[]|(() => o.Expression[])) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {assertNodeOfPossibleTypes} from '../node_assert';
|
||||||
import {getLView, getPreviousOrParentTNode} from '../state';
|
import {getLView, getPreviousOrParentTNode} from '../state';
|
||||||
import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils';
|
import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils';
|
||||||
|
|
||||||
import {getCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared';
|
import {TsickleIssue1009, getCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,11 +38,12 @@ import {getCleanup, handleError, loadComponentRenderer, markViewDirty} from './s
|
||||||
*/
|
*/
|
||||||
export function ɵɵlistener(
|
export function ɵɵlistener(
|
||||||
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
|
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
|
||||||
eventTargetResolver?: GlobalTargetResolver): void {
|
eventTargetResolver?: GlobalTargetResolver): TsickleIssue1009 {
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
const tNode = getPreviousOrParentTNode();
|
const tNode = getPreviousOrParentTNode();
|
||||||
listenerInternal(
|
listenerInternal(
|
||||||
lView, lView[RENDERER], tNode, eventName, listenerFn, useCapture, eventTargetResolver);
|
lView, lView[RENDERER], tNode, eventName, listenerFn, useCapture, eventTargetResolver);
|
||||||
|
return ɵɵlistener;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,11 +69,12 @@ export function ɵɵlistener(
|
||||||
*/
|
*/
|
||||||
export function ɵɵcomponentHostSyntheticListener(
|
export function ɵɵcomponentHostSyntheticListener(
|
||||||
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
|
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
|
||||||
eventTargetResolver?: GlobalTargetResolver): void {
|
eventTargetResolver?: GlobalTargetResolver): TsickleIssue1009 {
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
const tNode = getPreviousOrParentTNode();
|
const tNode = getPreviousOrParentTNode();
|
||||||
const renderer = loadComponentRenderer(tNode, lView);
|
const renderer = loadComponentRenderer(tNode, lView);
|
||||||
listenerInternal(lView, renderer, tNode, eventName, listenerFn, useCapture, eventTargetResolver);
|
listenerInternal(lView, renderer, tNode, eventName, listenerFn, useCapture, eventTargetResolver);
|
||||||
|
return ɵɵcomponentHostSyntheticListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -107,8 +107,7 @@ describe('event listeners', () => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
ɵɵlistener('custom', function() {
|
ɵɵlistener('custom', function() {
|
||||||
return ctx.onDocumentCustomEvent();
|
return ctx.onDocumentCustomEvent();
|
||||||
}, false, ɵɵresolveDocument as GlobalTargetResolver);
|
}, false, ɵɵresolveDocument as GlobalTargetResolver)('click', function() {
|
||||||
ɵɵlistener('click', function() {
|
|
||||||
return ctx.onBodyClick();
|
return ctx.onBodyClick();
|
||||||
}, false, ɵɵresolveBody as GlobalTargetResolver);
|
}, false, ɵɵresolveBody as GlobalTargetResolver);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,44 +32,34 @@ function testTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
ɵɵelementStart(0, 'div');
|
ɵɵelementStart(0, 'div');
|
||||||
ɵɵelementStart(1, 'button', 0);
|
ɵɵelementStart(1, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(2, 'button', 0);
|
ɵɵelementStart(2, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(3, 'button', 0);
|
ɵɵelementStart(3, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(4, 'button', 0);
|
ɵɵelementStart(4, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(5, 'button', 0);
|
ɵɵelementStart(5, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(6, 'button', 0);
|
ɵɵelementStart(6, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(7, 'button', 0);
|
ɵɵelementStart(7, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(8, 'button', 0);
|
ɵɵelementStart(8, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(9, 'button', 0);
|
ɵɵelementStart(9, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementStart(10, 'button', 0);
|
ɵɵelementStart(10, 'button', 0);
|
||||||
ɵɵlistener('click', function clickListener() {});
|
ɵɵlistener('click', function clickListener() {})('input', function inputListener() {});
|
||||||
ɵɵlistener('input', function inputListener() {});
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
}
|
}
|
||||||
|
|
|
@ -732,7 +732,7 @@ export declare type ɵɵComponentDefWithMeta<T, Selector extends String, ExportA
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}, QueryFields extends string[]> = ComponentDef<T>;
|
}, QueryFields extends string[]> = ComponentDef<T>;
|
||||||
|
|
||||||
export declare function ɵɵcomponentHostSyntheticListener(eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean, eventTargetResolver?: GlobalTargetResolver): void;
|
export declare function ɵɵcomponentHostSyntheticListener(eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean, eventTargetResolver?: GlobalTargetResolver): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵcontainer(index: number): void;
|
export declare function ɵɵcontainer(index: number): void;
|
||||||
|
|
||||||
|
@ -897,7 +897,7 @@ export declare function ɵɵinjectPipeChangeDetectorRef(flags?: InjectFlags): Ch
|
||||||
|
|
||||||
export declare function ɵɵinvalidFactory(): never;
|
export declare function ɵɵinvalidFactory(): never;
|
||||||
|
|
||||||
export declare function ɵɵlistener(eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean, eventTargetResolver?: GlobalTargetResolver): void;
|
export declare function ɵɵlistener(eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean, eventTargetResolver?: GlobalTargetResolver): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵloadQuery<T>(): QueryList<T>;
|
export declare function ɵɵloadQuery<T>(): QueryList<T>;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue