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');
|
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 directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE};
|
||||||
|
|
||||||
|
static templateRefExtractor:
|
||||||
|
o.ExternalReference = {name: 'ɵtemplateRefExtractor', moduleName: CORE};
|
||||||
|
|
||||||
static defineBase: o.ExternalReference = {name: 'ɵdefineBase', moduleName: CORE};
|
static defineBase: o.ExternalReference = {name: 'ɵdefineBase', moduleName: CORE};
|
||||||
|
|
||||||
static BaseDef: o.ExternalReference = {
|
static BaseDef: o.ExternalReference = {
|
||||||
|
|
|
@ -457,30 +457,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
o.TYPED_NULL_EXPR;
|
o.TYPED_NULL_EXPR;
|
||||||
parameters.push(attrArg);
|
parameters.push(attrArg);
|
||||||
|
|
||||||
if (element.references && element.references.length > 0) {
|
// local refs (ex.: <div #foo #bar="baz">)
|
||||||
const references = flatten(element.references.map(reference => {
|
parameters.push(this.prepareRefsParameter(element.references));
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wasInNamespace = this._namespace;
|
const wasInNamespace = this._namespace;
|
||||||
const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
||||||
|
@ -730,6 +708,14 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
|
|
||||||
if (attributeNames.length) {
|
if (attributeNames.length) {
|
||||||
parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributeNames), true));
|
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));
|
// 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 :
|
return value instanceof Interpolation || skipBindFn ? valExpr :
|
||||||
o.importExpr(R3.bind).callFn([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 {
|
class ValueConverter extends AstMemoryEfficientTransformer {
|
||||||
|
|
|
@ -34,6 +34,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
||||||
'ɵinjectElementRef': r3.injectElementRef,
|
'ɵinjectElementRef': r3.injectElementRef,
|
||||||
'ɵinjectTemplateRef': r3.injectTemplateRef,
|
'ɵinjectTemplateRef': r3.injectTemplateRef,
|
||||||
'ɵinjectViewContainerRef': r3.injectViewContainerRef,
|
'ɵinjectViewContainerRef': r3.injectViewContainerRef,
|
||||||
|
'ɵtemplateRefExtractor': r3.templateRefExtractor,
|
||||||
'ɵNgOnChangesFeature': r3.NgOnChangesFeature,
|
'ɵNgOnChangesFeature': r3.NgOnChangesFeature,
|
||||||
'ɵPublicFeature': r3.PublicFeature,
|
'ɵPublicFeature': r3.PublicFeature,
|
||||||
'ɵInheritDefinitionFeature': r3.InheritDefinitionFeature,
|
'ɵInheritDefinitionFeature': r3.InheritDefinitionFeature,
|
||||||
|
|
Loading…
Reference in New Issue