fix(ivy): support directive outputs on ng-template (#25717)
Compiler part of #25698 Fixes #25697 PR Close #25717
This commit is contained in:
parent
34be51898d
commit
6def18a95e
|
@ -401,4 +401,43 @@ describe('compiler compliance: template', () => {
|
|||
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
|
||||
it('should support directive outputs on <ng-template>', () => {
|
||||
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<ng-template (outDirective)="$event.doSth()"></ng-template>';
|
||||
})
|
||||
export class MyComponent {}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
const $t0_attrs$ = [${AttributeMarker.SelectOnly}, "outDirective"];
|
||||
|
||||
function Template_0(rf, ctx) { }
|
||||
|
||||
// ...
|
||||
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, Template_0, 0, 0, null, $t0_attrs$);
|
||||
$i0$.ɵlistener("outDirective", function MyComponent_Template_ng_template_outDirective_listener($event) { return $event.doSth(); });
|
||||
}
|
||||
}`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -516,28 +516,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
|
||||
// Generate Listeners (outputs)
|
||||
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
||||
const elName = sanitizeIdentifier(element.name);
|
||||
const evName = sanitizeIdentifier(outputAst.name);
|
||||
const functionName = `${this.templateName}_${elName}_${evName}_listener`;
|
||||
|
||||
this.creationInstruction(outputAst.sourceSpan, R3.listener, () => {
|
||||
const listenerScope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel);
|
||||
|
||||
const bindingExpr = convertActionBinding(
|
||||
listenerScope, implicit, outputAst.handler, 'b',
|
||||
() => error('Unexpected interpolation'));
|
||||
|
||||
const statements = [
|
||||
...listenerScope.restoreViewStatement(), ...listenerScope.variableDeclarations(),
|
||||
...bindingExpr.render3Stmts
|
||||
];
|
||||
|
||||
const handler = o.fn(
|
||||
[new o.FnParam('$event', o.DYNAMIC_TYPE)], statements, o.INFERRED_TYPE, null,
|
||||
functionName);
|
||||
|
||||
return [o.literal(outputAst.name), handler];
|
||||
});
|
||||
this.creationInstruction(
|
||||
outputAst.sourceSpan, R3.listener,
|
||||
this.prepareListenerParameter(element.name, outputAst));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -736,6 +717,13 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
o.literal(templateVisitor.getVarCount()));
|
||||
return trimTrailingNulls(parameters);
|
||||
});
|
||||
|
||||
// Generate listeners for directive output
|
||||
template.outputs.forEach((outputAst: t.BoundEvent) => {
|
||||
this.creationInstruction(
|
||||
outputAst.sourceSpan, R3.listener,
|
||||
this.prepareListenerParameter('ng_template', outputAst));
|
||||
});
|
||||
}
|
||||
|
||||
// These should be handled in the template or element directly.
|
||||
|
@ -906,6 +894,31 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
|
||||
return this.constantPool.getConstLiteral(asLiteral(refsParam), true);
|
||||
}
|
||||
|
||||
private prepareListenerParameter(tagName: string, outputAst: t.BoundEvent): () => o.Expression[] {
|
||||
const evName = sanitizeIdentifier(outputAst.name);
|
||||
const functionName = `${this.templateName}_${tagName}_${evName}_listener`;
|
||||
|
||||
return () => {
|
||||
|
||||
const listenerScope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel);
|
||||
|
||||
const bindingExpr = convertActionBinding(
|
||||
listenerScope, o.variable(CONTEXT_NAME), outputAst.handler, 'b',
|
||||
() => error('Unexpected interpolation'));
|
||||
|
||||
const statements = [
|
||||
...listenerScope.restoreViewStatement(), ...listenerScope.variableDeclarations(),
|
||||
...bindingExpr.render3Stmts
|
||||
];
|
||||
|
||||
const handler = o.fn(
|
||||
[new o.FnParam('$event', o.DYNAMIC_TYPE)], statements, o.INFERRED_TYPE, null,
|
||||
functionName);
|
||||
|
||||
return [o.literal(outputAst.name), handler];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ValueConverter extends AstMemoryEfficientTransformer {
|
||||
|
|
|
@ -158,7 +158,7 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S
|
|||
| `<div (keyup.enter)>` | ❌ | ❌ | ❌ |
|
||||
| `<div (hammer.js)>` | ❌ | ❌ | ❌ |
|
||||
| [`<div (directiveOut)>`][gh23560] | ✅ | ✅ | ✅ |
|
||||
| [`<ng-template (directiveOut)>`][gh23561] | ❌ | ❌ | ❌ |
|
||||
| [`<ng-template (directiveOut)>`][gh23561] | ✅ | ✅ | ✅ |
|
||||
| [`<ng-container>`][gh24381] | ✅ | ✅ | ✅ |
|
||||
|
||||
[gh23560]: https://github.com/angular/angular/issues/23560
|
||||
|
|
Loading…
Reference in New Issue