fix(compiler): not generating update instructions for ng-template inside alternate namespaces (#41669)
We have a check that determines whether to generate property binding instructions for an `ng-template`. The check looks at whether the tag name is exactly `ng-template`, but the problem is that if the tag is placed in a non-HTML namespace (e.g. `svg`), the tag name will actually be `:namespace:ng-template` and the check will fail. These changes resolve the issue by looking at the tag name without the namespace. Fixes #41308. PR Close #41669
This commit is contained in:
parent
364ff96f28
commit
73824d5337
|
@ -38,6 +38,62 @@ export declare class MyModule {
|
||||||
static ɵinj: i0.ɵɵInjectorDeclaration<MyModule>;
|
static ɵinj: i0.ɵɵInjectorDeclaration<MyModule>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************************
|
||||||
|
* PARTIAL FILE: svg_embedded_view.js
|
||||||
|
****************************************************************************************************/
|
||||||
|
import { Component, NgModule } from '@angular/core';
|
||||||
|
import * as i0 from "@angular/core";
|
||||||
|
export class MyComponent {
|
||||||
|
constructor() {
|
||||||
|
this.condition = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||||
|
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
|
||||||
|
<svg>
|
||||||
|
<ng-template [ngIf]="condition">
|
||||||
|
<text>Hello</text>
|
||||||
|
</ng-template>
|
||||||
|
</svg>
|
||||||
|
`, isInline: true });
|
||||||
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{
|
||||||
|
type: Component,
|
||||||
|
args: [{
|
||||||
|
selector: 'my-component',
|
||||||
|
template: `
|
||||||
|
<svg>
|
||||||
|
<ng-template [ngIf]="condition">
|
||||||
|
<text>Hello</text>
|
||||||
|
</ng-template>
|
||||||
|
</svg>
|
||||||
|
`
|
||||||
|
}]
|
||||||
|
}] });
|
||||||
|
export class MyModule {
|
||||||
|
}
|
||||||
|
MyModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
||||||
|
MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] });
|
||||||
|
MyModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule });
|
||||||
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, decorators: [{
|
||||||
|
type: NgModule,
|
||||||
|
args: [{ declarations: [MyComponent] }]
|
||||||
|
}] });
|
||||||
|
|
||||||
|
/****************************************************************************************************
|
||||||
|
* PARTIAL FILE: svg_embedded_view.d.ts
|
||||||
|
****************************************************************************************************/
|
||||||
|
import * as i0 from "@angular/core";
|
||||||
|
export declare class MyComponent {
|
||||||
|
condition: boolean;
|
||||||
|
static ɵfac: i0.ɵɵFactoryDeclaration<MyComponent, never>;
|
||||||
|
static ɵcmp: i0.ɵɵComponentDeclaration<MyComponent, "my-component", never, {}, {}, never, never>;
|
||||||
|
}
|
||||||
|
export declare class MyModule {
|
||||||
|
static ɵfac: i0.ɵɵFactoryDeclaration<MyModule, never>;
|
||||||
|
static ɵmod: i0.ɵɵNgModuleDeclaration<MyModule, [typeof MyComponent], never, never>;
|
||||||
|
static ɵinj: i0.ɵɵInjectorDeclaration<MyModule>;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************************
|
/****************************************************************************************************
|
||||||
* PARTIAL FILE: mathml.js
|
* PARTIAL FILE: mathml.js
|
||||||
****************************************************************************************************/
|
****************************************************************************************************/
|
||||||
|
|
|
@ -27,6 +27,23 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "should handle SVG with an embedded ng-template",
|
||||||
|
"inputFiles": [
|
||||||
|
"svg_embedded_view.ts"
|
||||||
|
],
|
||||||
|
"expectations": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"expected": "svg_embedded_view_template.js",
|
||||||
|
"generated": "svg_embedded_view.js"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"failureMessage": "Incorrect template."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "should handle MathML",
|
"description": "should handle MathML",
|
||||||
"inputFiles": [
|
"inputFiles": [
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: `
|
||||||
|
<svg>
|
||||||
|
<ng-template [ngIf]="condition">
|
||||||
|
<text>Hello</text>
|
||||||
|
</ng-template>
|
||||||
|
</svg>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
condition = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent]})
|
||||||
|
export class MyModule {
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
function MyComponent__svg_ng_template_1_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
i0.ɵɵnamespaceSVG();
|
||||||
|
i0.ɵɵelementStart(0, "text");
|
||||||
|
i0.ɵɵtext(1, "Hello");
|
||||||
|
i0.ɵɵelementEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
…
|
||||||
|
|
||||||
|
// NOTE: AttributeMarker.Bindings = 3
|
||||||
|
consts: [[3, "ngIf"]],
|
||||||
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
i0.ɵɵnamespaceSVG();
|
||||||
|
i0.ɵɵelementStart(0, "svg");
|
||||||
|
i0.ɵɵtemplate(1, MyComponent__svg_ng_template_1_Template, 2, 0, "ng-template", 0);
|
||||||
|
i0.ɵɵelementEnd();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
i0.ɵɵadvance(1);
|
||||||
|
i0.ɵɵproperty("ngIf", ctx.condition);
|
||||||
|
}
|
||||||
|
}
|
|
@ -875,17 +875,17 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
this.i18n.appendTemplate(template.i18n!, templateIndex);
|
this.i18n.appendTemplate(template.i18n!, templateIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagName = sanitizeIdentifier(template.tagName || '');
|
const tagNameWithoutNamespace =
|
||||||
const contextName = `${this.contextName}${tagName ? '_' + tagName : ''}_${templateIndex}`;
|
template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
|
||||||
|
const contextName = `${this.contextName}${
|
||||||
|
template.tagName ? '_' + sanitizeIdentifier(template.tagName) : ''}_${templateIndex}`;
|
||||||
const templateName = `${contextName}_Template`;
|
const templateName = `${contextName}_Template`;
|
||||||
|
|
||||||
const parameters: o.Expression[] = [
|
const parameters: o.Expression[] = [
|
||||||
o.literal(templateIndex),
|
o.literal(templateIndex),
|
||||||
o.variable(templateName),
|
o.variable(templateName),
|
||||||
|
|
||||||
// We don't care about the tag's namespace here, because we infer
|
// We don't care about the tag's namespace here, because we infer
|
||||||
// it based on the parent nodes inside the template instruction.
|
// it based on the parent nodes inside the template instruction.
|
||||||
o.literal(template.tagName ? splitNsName(template.tagName)[1] : template.tagName),
|
o.literal(tagNameWithoutNamespace),
|
||||||
];
|
];
|
||||||
|
|
||||||
// find directives matching on a given <ng-template> node
|
// find directives matching on a given <ng-template> node
|
||||||
|
@ -937,7 +937,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
this.templatePropertyBindings(templateIndex, template.templateAttrs);
|
this.templatePropertyBindings(templateIndex, template.templateAttrs);
|
||||||
|
|
||||||
// Only add normal input/output binding instructions on explicit <ng-template> elements.
|
// Only add normal input/output binding instructions on explicit <ng-template> elements.
|
||||||
if (template.tagName === NG_TEMPLATE_TAG_NAME) {
|
if (tagNameWithoutNamespace === NG_TEMPLATE_TAG_NAME) {
|
||||||
const [i18nInputs, inputs] =
|
const [i18nInputs, inputs] =
|
||||||
partitionArray<t.BoundAttribute, t.BoundAttribute>(template.inputs, hasI18nMeta);
|
partitionArray<t.BoundAttribute, t.BoundAttribute>(template.inputs, hasI18nMeta);
|
||||||
|
|
||||||
|
|
|
@ -1990,6 +1990,27 @@ describe('acceptance integration tests', () => {
|
||||||
expect(logs).toEqual(['Baggins']);
|
expect(logs).toEqual(['Baggins']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render SVG nodes placed inside ng-template', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<svg>
|
||||||
|
<ng-template [ngIf]="condition">
|
||||||
|
<text>Hello</text>
|
||||||
|
</ng-template>
|
||||||
|
</svg>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
condition = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp], imports: [CommonModule]});
|
||||||
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.nativeElement.innerHTML).toContain('<text>Hello</text>');
|
||||||
|
});
|
||||||
|
|
||||||
describe('tView.firstUpdatePass', () => {
|
describe('tView.firstUpdatePass', () => {
|
||||||
function isFirstUpdatePass() {
|
function isFirstUpdatePass() {
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
|
|
Loading…
Reference in New Issue