Skip to content

Commit 729212e

Browse files
toha-markoАнтон Мариненко
authored andcommitted
fix(compiler): report error for property/event bindings on ng-content
Previously, adding property bindings like [foo]="bar" or event bindings like (click)="handler()" to <ng-content> was silently ignored. These bindings have no effect since ng-content is not a real DOM element. This commit adds a compile-time error to warn the developer. Fixes #24259
1 parent 642fd43 commit 729212e

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

packages/compiler/src/render3/r3_template_transform.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,12 @@ class HtmlAstToIvyAst implements html.Visitor {
190190
let parsedElement: t.Content | t.Template | t.Element | undefined;
191191
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
192192
const selector = preparsedElement.selectAttr;
193-
const attrs: t.TextAttribute[] = element.attrs.map((attr) => this.visitAttribute(attr));
193+
194+
this.reportUnsupportedNgContentBindings(parsedProperties, boundEvents);
195+
194196
parsedElement = new t.Content(
195197
selector,
196-
attrs,
198+
attributes,
197199
children,
198200
element.isSelfClosing,
199201
element.sourceSpan,
@@ -298,6 +300,27 @@ class HtmlAstToIvyAst implements html.Visitor {
298300
: this._visitTextWithInterpolation(text.value, text.sourceSpan, text.tokens, text.i18n);
299301
}
300302

303+
private reportUnsupportedNgContentBindings(
304+
properties: ParsedProperty[],
305+
events: t.BoundEvent[],
306+
): void {
307+
const reported = new Set<string>();
308+
309+
for (const binding of [...properties, ...events]) {
310+
const sourceSpan = binding.sourceSpan;
311+
const key = `${sourceSpan.start.offset}:${sourceSpan.end.offset}`;
312+
if (reported.has(key)) {
313+
continue;
314+
}
315+
reported.add(key);
316+
317+
this.reportError(
318+
`Property and event bindings are not supported on <ng-content>. Binding "${sourceSpan.toString()}" will be ignored.`,
319+
sourceSpan,
320+
);
321+
}
322+
}
323+
301324
visitExpansion(expansion: html.Expansion): t.Icu | null {
302325
if (!expansion.i18n) {
303326
// do not generate Icu in case it was created

packages/compiler/test/render3/r3_template_transform_spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,36 @@ describe('R3 template transform', () => {
305305
]);
306306
});
307307

308+
it('should report an error for property binding on ngContent', () => {
309+
const res = parse('<ng-content [foo]="bar"></ng-content>', {ignoreError: true});
310+
expect(res.errors.length).toBe(1);
311+
expect(res.errors[0].msg).toContain(
312+
'Property and event bindings are not supported on <ng-content>',
313+
);
314+
expect(res.errors[0].msg).toContain('[foo]');
315+
expectFromR3Nodes(res.nodes).toEqual([['Content', '*']]);
316+
});
317+
318+
it('should report an error for event binding on ngContent', () => {
319+
const res = parse('<ng-content (click)="handler()"></ng-content>', {ignoreError: true});
320+
expect(res.errors.length).toBe(1);
321+
expect(res.errors[0].msg).toContain(
322+
'Property and event bindings are not supported on <ng-content>',
323+
);
324+
expect(res.errors[0].msg).toContain('(click)');
325+
});
326+
327+
it('should report an error for each binding on ngContent', () => {
328+
const res = parse('<ng-content [foo]="a" (bar)="b()"></ng-content>', {ignoreError: true});
329+
expect(res.errors.length).toBe(2);
330+
});
331+
332+
it('should report a single error for two-way binding on ngContent', () => {
333+
const res = parse('<ng-content [(foo)]="bar"></ng-content>', {ignoreError: true});
334+
expect(res.errors.length).toBe(1);
335+
expect(res.errors[0].msg).toContain('[(foo)]');
336+
});
337+
308338
it('should indicate whether an element is void', () => {
309339
const nodes = parse('<input><div></div>').nodes as t.Element[];
310340
expect(nodes[0].name).toBe('input');

0 commit comments

Comments
 (0)