fix(ivy): support directive outputs on ng-template (#25717)

Compiler part of #25698
Fixes #25697

PR Close #25717
This commit is contained in:
Pawel Kozlowski 2018-08-29 12:26:24 +02:00 committed by Misko Hevery
parent 34be51898d
commit 6def18a95e
3 changed files with 75 additions and 23 deletions

View File

@ -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');
});
});

View File

@ -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 {

View File

@ -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