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>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* 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
|
||||
****************************************************************************************************/
|
||||
|
|
|
@ -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",
|
||||
"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);
|
||||
}
|
||||
|
||||
const tagName = sanitizeIdentifier(template.tagName || '');
|
||||
const contextName = `${this.contextName}${tagName ? '_' + tagName : ''}_${templateIndex}`;
|
||||
const tagNameWithoutNamespace =
|
||||
template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
|
||||
const contextName = `${this.contextName}${
|
||||
template.tagName ? '_' + sanitizeIdentifier(template.tagName) : ''}_${templateIndex}`;
|
||||
const templateName = `${contextName}_Template`;
|
||||
|
||||
const parameters: o.Expression[] = [
|
||||
o.literal(templateIndex),
|
||||
o.variable(templateName),
|
||||
|
||||
// We don't care about the tag's namespace here, because we infer
|
||||
// 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
|
||||
|
@ -937,7 +937,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
this.templatePropertyBindings(templateIndex, template.templateAttrs);
|
||||
|
||||
// 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] =
|
||||
partitionArray<t.BoundAttribute, t.BoundAttribute>(template.inputs, hasI18nMeta);
|
||||
|
||||
|
|
|
@ -1990,6 +1990,27 @@ describe('acceptance integration tests', () => {
|
|||
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', () => {
|
||||
function isFirstUpdatePass() {
|
||||
const lView = getLView();
|
||||
|
|
Loading…
Reference in New Issue