feat(ivy): add support to template local refs in the compiler (#25576)
Fixes #23316 PR Close #25576
This commit is contained in:
parent
b05d4a5007
commit
11e2d9da1a
|
@ -359,4 +359,45 @@ describe('compiler compliance: template', () => {
|
|||
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
|
||||
it('should support local refs on <ng-template>', () => {
|
||||
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<ng-template #foo>some-content</ng-template>';
|
||||
})
|
||||
export class MyComponent {}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
const _c0 = ["foo", ""];
|
||||
|
||||
function Template_0(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtext(0, "some-content");
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵtemplate(0, Template_0, 1, null, null, _c0, i0.ɵtemplateRefExtractor);
|
||||
}
|
||||
}`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -118,6 +118,9 @@ export class Identifiers {
|
|||
|
||||
static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE};
|
||||
|
||||
static templateRefExtractor:
|
||||
o.ExternalReference = {name: 'ɵtemplateRefExtractor', moduleName: CORE};
|
||||
|
||||
static defineBase: o.ExternalReference = {name: 'ɵdefineBase', moduleName: CORE};
|
||||
|
||||
static BaseDef: o.ExternalReference = {
|
||||
|
|
|
@ -457,30 +457,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
o.TYPED_NULL_EXPR;
|
||||
parameters.push(attrArg);
|
||||
|
||||
if (element.references && element.references.length > 0) {
|
||||
const references = flatten(element.references.map(reference => {
|
||||
const slot = this.allocateDataSlot();
|
||||
// Generate the update temporary.
|
||||
const variableName = this._bindingScope.freshReferenceName();
|
||||
const retrievalLevel = this.level;
|
||||
const lhs = o.variable(variableName);
|
||||
this._bindingScope.set(
|
||||
retrievalLevel, reference.name, lhs, DeclarationPriority.DEFAULT,
|
||||
(scope: BindingScope, relativeLevel: number) => {
|
||||
// e.g. x(2);
|
||||
const nextContextStmt =
|
||||
relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
|
||||
|
||||
// e.g. const $foo$ = r(1);
|
||||
const refExpr = lhs.set(o.importExpr(R3.reference).callFn([o.literal(slot)]));
|
||||
return nextContextStmt.concat(refExpr.toConstDecl());
|
||||
});
|
||||
return [reference.name, reference.value];
|
||||
}));
|
||||
parameters.push(this.constantPool.getConstLiteral(asLiteral(references), true));
|
||||
} else {
|
||||
parameters.push(o.TYPED_NULL_EXPR);
|
||||
}
|
||||
// local refs (ex.: <div #foo #bar="baz">)
|
||||
parameters.push(this.prepareRefsParameter(element.references));
|
||||
|
||||
const wasInNamespace = this._namespace;
|
||||
const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
||||
|
@ -730,6 +708,14 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
|
||||
if (attributeNames.length) {
|
||||
parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributeNames), true));
|
||||
} else {
|
||||
parameters.push(o.TYPED_NULL_EXPR);
|
||||
}
|
||||
|
||||
// local refs (ex.: <ng-template #foo>)
|
||||
if (template.references && template.references.length) {
|
||||
parameters.push(this.prepareRefsParameter(template.references));
|
||||
parameters.push(o.importExpr(R3.templateRefExtractor));
|
||||
}
|
||||
|
||||
// e.g. p(1, 'forOf', ɵbind(ctx.items));
|
||||
|
@ -858,6 +844,34 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
return value instanceof Interpolation || skipBindFn ? valExpr :
|
||||
o.importExpr(R3.bind).callFn([valExpr]);
|
||||
}
|
||||
|
||||
private prepareRefsParameter(references: t.Reference[]): o.Expression {
|
||||
if (!references || references.length === 0) {
|
||||
return o.TYPED_NULL_EXPR;
|
||||
}
|
||||
|
||||
const refsParam = flatten(references.map(reference => {
|
||||
const slot = this.allocateDataSlot();
|
||||
// Generate the update temporary.
|
||||
const variableName = this._bindingScope.freshReferenceName();
|
||||
const retrievalLevel = this.level;
|
||||
const lhs = o.variable(variableName);
|
||||
this._bindingScope.set(
|
||||
retrievalLevel, reference.name, lhs, DeclarationPriority.DEFAULT,
|
||||
(scope: BindingScope, relativeLevel: number) => {
|
||||
// e.g. x(2);
|
||||
const nextContextStmt =
|
||||
relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
|
||||
|
||||
// e.g. const $foo$ = r(1);
|
||||
const refExpr = lhs.set(o.importExpr(R3.reference).callFn([o.literal(slot)]));
|
||||
return nextContextStmt.concat(refExpr.toConstDecl());
|
||||
});
|
||||
return [reference.name, reference.value];
|
||||
}));
|
||||
|
||||
return this.constantPool.getConstLiteral(asLiteral(refsParam), true);
|
||||
}
|
||||
}
|
||||
|
||||
class ValueConverter extends AstMemoryEfficientTransformer {
|
||||
|
|
|
@ -34,6 +34,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
|||
'ɵinjectElementRef': r3.injectElementRef,
|
||||
'ɵinjectTemplateRef': r3.injectTemplateRef,
|
||||
'ɵinjectViewContainerRef': r3.injectViewContainerRef,
|
||||
'ɵtemplateRefExtractor': r3.templateRefExtractor,
|
||||
'ɵNgOnChangesFeature': r3.NgOnChangesFeature,
|
||||
'ɵPublicFeature': r3.PublicFeature,
|
||||
'ɵInheritDefinitionFeature': r3.InheritDefinitionFeature,
|
||||
|
|
Loading…
Reference in New Issue