GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — refactor(compiler): disallow unsupported bindings and attributes on ng-content and ng-template by JeanMeche · Pull Request #71887 · angular/angular

Conversation

@JeanMeche

@JeanMeche JeanMeche commented Nov 8, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This PR extends compiler and template type-checking validation for virtual elements to <ng-content> and <ng-template>:

  • Template transformation (r3_template_transform.ts):
    • <ng-template> does not support attribute bindings ([attr.foo]).
    • <ng-content> does not support property bindings ([id]), event bindings ((click)), or template references (#ref).
  • Type Check Block (TcbContentAttributesOp in ngtsc and ngp):
    • <ng-content> enforces an allowlist for static attributes:
      • select (case-insensitive)
      • ngProjectAs
      • ngSkipHydration
      • ngPreserveWhitespaces
    • Also allows static attributes on <ng-content> when re-projecting into a parent component if the attribute selects a specific content projection slot of that parent component.
    • If selection or selector is used on <ng-content> (and does not match a parent projection slot), a helpful suggestion is provided: 'selection'/'selector' is not supported on ng-content. Did you mean 'select'?.

Also removes legacy compliance tests in r3_view_compiler_template that asserted unsupported attribute and class bindings on <ng-template> degraded to property bindings, and fixes invalid attributes found in existing tests and examples where slot="foo", selector="...", or class="..." were mistakenly used on <ng-content>.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@angular-robot angular-robot Bot added the area: compiler Issues related to `ngc`, Angular's template compiler label Nov 8, 2026
@ngbot ngbot Bot added this to the Backlog milestone Nov 8, 2026
@JeanMeche JeanMeche mentioned this pull request Nov 8, 2026
Report bindings that have no effect on ng-container, ng-template, and ng-content #71881
Open
@JeanMeche
JeanMeche force-pushed the compiler-virtual-element-bindings branch from b7a8b82 to f3dfcfe Compare November 8, 2026 20:11
@JeanMeche JeanMeche mentioned this pull request Nov 9, 2026
feat(compiler-cli): add ignoredBinding check #71882
Closed
6 of 14 tasks
@JeanMeche
JeanMeche force-pushed the compiler-virtual-element-bindings branch from f3dfcfe to e99d42b Compare November 9, 2026 14:16
@JeanMeche JeanMeche changed the title refactor(compiler): disallow unsupported bindings on ng-content and ng-template refactor(compiler): disallow unsupported bindings and attributes on ng-content and ng-template Nov 9, 2026
@JeanMeche
JeanMeche force-pushed the compiler-virtual-element-bindings branch 6 times, most recently from fdcecd6 to af14208 Compare November 10, 2026 15:12
JeanMeche

This comment was marked as outdated.

Sign in to view

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@JeanMeche
JeanMeche force-pushed the compiler-virtual-element-bindings branch 2 times, most recently from dbf45c7 to 363c2ef Compare November 10, 2026 17:35
JeanMeche
JeanMeche commented Nov 10, 2026
View reviewed changes

@JeanMeche JeanMeche left a comment

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement! Splitting the validation between r3_template_transform.ts (for syntactic errors that don't depend on component metadata: [attr.foo] on <ng-template>, property/event/reference bindings and selection/selector typos on <ng-content>) and the TCB (TcbContentAttributesOp for static attributes on <ng-content> that don't match a parent component's content projection slot) fits cleanly into the existing architecture and works across both ngtsc and ngp.

Extracting validateNgContentElement and validateNoAttributeBindings also keeps visitElement much easier to read.

I left a few comments on edge cases around multi-class attributes / comma-separated slot selectors, namespaced attributes, legacy .d.ts metadata, and reporting multiple invalid bindings.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread packages/compiler/src/typecheck/ops/content_projection.ts
const attributes = new Set<TextAttribute>();
for (const attr of child.attributes) {
const withoutAttr = removeAttributeFromSelector(selector, attr.name);
if (findProjectionSlot(matcher, ngContentSelectors, withoutAttr) !== slot) {

@JeanMeche JeanMeche Nov 10, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two subtle edge cases with checking each attribute by removing it individually (withoutAttr = removeAttributeFromSelector(selector, attr.name)):

  1. Multiple classes in class="...": Because removeAttributeFromSelector strips the entire class attribute and all selector.classNames at once (lines 271–275), if a parent has <ng-content select=".used" /> and a child writes <ng-content class="used unused" />, removing class changes the matched slot so the whole TextAttribute is marked as used—allowing the redundant unused class token through without a diagnostic.
  2. Comma-separated / redundant selectors on the same slot: If a parent slot uses a selector list like <ng-content select="[slot], .foo" /> and a child writes <ng-content slot class="foo" />, removing slot alone still matches slot 0 via .foo, and removing class alone still matches slot 0 via [slot]. Because neither attribute changes slot when removed on its own, both attributes will be flagged as unsupported even though at least one is required to match the slot.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread packages/compiler/src/typecheck/ops/content_projection.ts
function createContentCssSelector(node: Content): CssSelector {
const selector = new CssSelector();
for (const attr of node.attributes) {
const name = splitNsName(attr.name)[1];

@JeanMeche JeanMeche Nov 10, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

splitNsName(attr.name)[1] strips the namespace prefix and adds the unprefixed name to CssSelector. However, at runtime Angular's projection matcher (findAttrIndexInNode in packages/core/src/render3/node_selector_matcher.ts) explicitly skips AttributeMarker.NamespaceURI attributes altogether. If <ng-content> has a namespaced attribute (e.g. xlink:title), createContentCssSelector will match a parent's [title] slot even though it won't match at runtime. Consider skipping attributes where splitNsName(attr.name)[0] !== null.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread packages/compiler/src/typecheck/ops/scope.ts
if (
meta === undefined ||
meta.ngContentSelectors === null ||
meta.ngContentSelectors.length === 0

@JeanMeche JeanMeche Nov 10, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ngtsc's DtsMetadataReader (packages/compiler-cli/src/ngtsc/metadata/src/dts.ts), a .d.ts component with no <ng-content> slots (NgContentSelectors = never) has ngContentSelectors: [], whereas ngContentSelectors: null indicates a legacy .d.ts declaration (typeArguments.length <= 6) where projection slots were not recorded in the type definition.

Returning early here when meta.ngContentSelectors === null treats unknown slots the same as zero slots, which could produce false-positive diagnostics if <ng-content someAttr> is projected into a component from an older .d.ts.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread packages/compiler/src/render3/r3_template_transform.ts
references: t.Reference[],
attributes: t.TextAttribute[],
): void {
if (boundEvents.length > 0) {

@JeanMeche JeanMeche Nov 10, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: boundEvents, parsedProperties, and references only report the first item ([0]), whereas attributes below (and validateNoAttributeBindings) loop over every item. Iterating over all elements in boundEvents, parsedProperties, and references would report all unsupported bindings on <ng-content> in one pass rather than requiring the user to fix them one at a time.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@JeanMeche
JeanMeche force-pushed the compiler-virtual-element-bindings branch 12 times, most recently from 797374e to f6014b5 Compare November 12, 2026 09:16
@JeanMeche
refactor(compiler): disallow unsupported bindings and attributes on n…
41017f3
…g-content and ng-template

Virtual elements such as <ng-content> and <ng-template> do not support
certain bindings and attributes:
- <ng-template> does not support attribute bindings ([attr.foo]).
- <ng-content> does not support property bindings ([id]), event bindings ((click)),
  or template references (#ref).
- <ng-content> only supports an allowlist of static attributes ('select',
  'ngProjectAs', 'ngSkipHydration', 'ngNonBindable', 'ngPreserveWhitespaces',
  and i18n attributes 'i18n', 'i18n-*'), as well as static attributes that
  select a content projection slot of a parent component when re-projecting
  <ng-content>. If 'selection' or 'selector' is encountered on <ng-content>,
  a helpful 'Did you mean select?' suggestion is provided.

Syntactic binding and reference checks are enforced during template
transformation (r3_template_transform.ts), while static attribute checks on
<ng-content> are performed during TCB generation (TcbContentAttributesOp) so
that parent component projection slots can be taken into account across both
ngtsc and ngp.

Also removes legacy compliance tests in r3_view_compiler_template that asserted
unsupported attribute and class bindings on ng-template degraded to property
bindings, and fixes tests and examples where invalid attributes were used on
<ng-content>.
@JeanMeche
JeanMeche force-pushed the compiler-virtual-element-bindings branch from f6014b5 to 41017f3 Compare November 12, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

area: compiler Issues related to `ngc`, Angular's template compiler

Projects

None yet

Milestone

Backlog

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@JeanMeche
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.