| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
| } | ||
|
|
||
| const fixture = TestBed.createComponent(App); | ||
| fixture.autoDetectChanges(); |
There was a problem hiding this comment.
NIT:
| fixture.autoDetectChanges(); | |
Sorry, something went wrong.
There was a problem hiding this comment.
Thanks for taking a look! I tried removing it, but this file runs with provideZoneChangeDetection() (in the beforeEach at the top), so unlike the @if/@switch specs the fixture doesn't render on its own. Without autoDetectChanges() the test fails with Expected 'svg: (), math: ()' to be 'svg: (12), math: ()'.
Sorry, something went wrong.
There was a problem hiding this comment.
Maybe we can replace it with detectChanges in that case.
Sorry, something went wrong.
There was a problem hiding this comment.
done
Sorry, something went wrong.
|
I was looking at this change and noticed a potential bug that will cause the Angular compiler to crash with a TypeError on valid template syntax. In packages/compiler/src/template/pipeline/src/ingest.ts, the new logic assumes root.tagName is always a string: const [, tagName] = splitNsName(root instanceof t.Element ? root.name : root.tagName!);However, if a developer places a structural directive directly on an <ng-template> at the root of a control flow block, root.tagName is null. As documented in packages/compiler/src/render3/r3_ast.ts, tagName is explicitly set to null to distinguish synthetic templates from actual <ng-template> elements. The CrashBecause of the non-null assertion (!), splitNsName(null) gets called. Inside splitNsName (in packages/compiler/src/ml_parser/tags.ts), it evaluates if (elementName[0] != ':'), throwing a TypeError: Cannot read properties of null (reading '0') and crashing the compiler. Failing Test CaseHere is a test case (e.g. for packages/core/test/acceptance/control_flow_if_spec.ts) that demonstrates the crash during compilation: it('should not crash the compiler when an @if block root is an ng-template with a structural directive', () => {
@Component({
selector: 'test-cmp',
template: 'Main: <ng-content></ng-content> Slot: <ng-content select="svg"></ng-content>',
})
class TestComponent {}
@Component({
imports: [TestComponent, NgIf],
template: `
<test-cmp>
@if (true) {
<ng-template *ngIf="true">
<svg><text>foo</text></svg>
</ng-template>
}
</test-cmp>
`,
})
class App {}
// This will throw TypeError: Cannot read properties of null (reading '0')
expect(() => TestBed.createComponent(App)).not.toThrow();
});Suggested FixYou might want to handle the null case gracefully before passing it to splitNsName, similar to how the old code handled it: const rawTagName = root instanceof t.Element ? root.name : root.tagName;
const tagName = rawTagName ? splitNsName(rawTagName)[1] : null;
// Don't pass along `ng-template` tag name since it enables directive matching.
return tagName === NG_TEMPLATE_TAG_NAME ? null : tagName; |
Sorry, something went wrong.
Thanks for checking! I don't think it can get there though. A template only becomes root when its tagName isn't null (the check in the loop over node.children a few lines above), so for <ng-template *ngIf> the function returns null before splitNsName is called, same as before this change. I ran your test case against this branch to be sure and it passes, and the block renders the same as before. |
Sorry, something went wrong.
…inst ng-content tag selectors When a control flow block has a single root element, its tag name is copied onto the block's template so the block can be projected into the matching `ng-content` slot. The tag name was copied with its namespace prefix, so an `<svg>` or `<math>` root ended up as `:svg:svg` or `:math:math` and never matched a tag selector like `select="svg"`, falling back to the default slot. Strip the namespace prefix like we already do for regular elements and structural templates. Fixes angular#71162
Pushed your version anyway, it reads better without the non-null assertion and doesn't depend on the check in the loop. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
When an <svg> or <math> element is the only root node of an @if, @for or @switch branch, it isn't projected into an ng-content slot with a tag selector such as select="svg". It goes to the default slot instead. The compiler passes the root's tag name to ɵɵconditionalCreate / ɵɵrepeaterCreate with its namespace prefix (":svg:svg"), so the runtime never matches it against svg.
Issue Number: Fixes #71162
What is the new behavior?
The namespace prefix is stripped in ingestControlFlowInsertionPoint, the same way it already is for regular elements and structural templates, so the generated tag name is "svg" / "math" and the block lands in the right slot.
I added acceptance tests for @if/@else, @for/@empty and @switch with SVG and MathML roots, plus one with an <svg *ngIf> root since that goes through the template branch of the same code. There's also a compliance test that checks the emitted tag names and that the branch templates still call ɵɵnamespaceSVG/ɵɵnamespaceMathML. Without the change in ingest.ts, all six new tests fail.
Does this PR introduce a breaking change?
Other information
Strictly speaking, apps that rely on the current behavior (an <svg> root inside a control flow block ending up in the default slot next to a select="svg" slot) will see it move to the svg slot, which is where it would go without the control flow block. The workaround people may have used, ngProjectAs="svg", keeps working.