Implement (element) helper for dynamic tag names (RFC #0389)#21230
Merged
NullVoxPopuli merged 10 commits intoemberjs:mainfrom Apr 8, 2026
Merged
Implement (element) helper for dynamic tag names (RFC #0389)#21230NullVoxPopuli merged 10 commits intoemberjs:mainfrom
(element) helper for dynamic tag names (RFC #0389)#21230NullVoxPopuli merged 10 commits intoemberjs:mainfrom
Conversation
packages/@ember/-internals/glimmer/tests/integration/helpers/element-test.js
Show resolved
Hide resolved
NullVoxPopuli
previously approved these changes
Mar 26, 2026
Contributor
|
Tested on the big app at work, and aside from ember not owning the |
Contributor
Contributor
|
Feedback:
|
Implements the `element` helper as specified in RFC emberjs#389, allowing dynamic tag names in Glimmer templates. The helper returns a component definition that renders the specified HTML element around yielded content. Behavior: - `(element "h1")` renders an `<h1>` wrapping the block content - `(element "")` renders block content without a wrapping element - `(element null)` / `(element undefined)` renders nothing - Invalid types (number, boolean, object) throw in development mode - Tag name changes trigger teardown/recreation of the element Available in both loose mode (built-in) and strict mode (importable from `@ember/helper`). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Departing from the RFC: instead of rendering nothing for null/undefined, throw an assertion error for all non-string values. Falsey values returned from (element) can't actually be rendered, so it's better to fail early. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ad1aa36 to
97517bf
Compare
…te/precompileTemplate Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
97517bf to
6ed27e1
Compare
|
|
||
| const ELEMENT_COMPONENT_MANAGER = new ElementComponentManager(); | ||
|
|
||
| class ElementComponentDefinition { |
Contributor
There was a problem hiding this comment.
this is basically @glimmer/component's Component, but way more constrained
Strip explicit interface annotations, unused imports, and verbose type parameters. Reduce capabilities to only the 4 truthy flags (missing properties default to falsy in capabilityFlagsFrom). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c1c421a to
179124a
Compare
Per review feedback: don't register element in loose mode (users with ember-element-helper may have different behavior), and use strict mode templates exclusively in tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
packages/@ember/-internals/glimmer/tests/integration/helpers/element-test.js
Outdated
Show resolved
Hide resolved
packages/@ember/-internals/glimmer/tests/integration/helpers/element-test.js
Outdated
Show resolved
Hide resolved
packages/@ember/-internals/glimmer/tests/integration/helpers/element-test.js
Outdated
Show resolved
Hide resolved
packages/@ember/-internals/glimmer/tests/integration/helpers/element-test.js
Outdated
Show resolved
Hide resolved
- Remove getTag helper wrapper; access state.htmlTag directly in scope - Restore "requires at least one argument" test - Restore "requires no more than one argument" test - Restore "does not take any named arguments" test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ef4
requested changes
Apr 8, 2026
Contributor
ef4
left a comment
There was a problem hiding this comment.
Some suggested edits to reduce the component manager to the capabilities it actually needs.
…icLayout Per review: dynamicTag does nothing, and dynamicLayout is not needed here. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ef4
approved these changes
Apr 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Implements the
elementhelper as specified in RFC #0389, continuing the work started in #21048.(element "h1")— renders an<h1>wrapping the block content, with full support for attributes, modifiers, and...attributes(element "")— renders block content without a wrapping element(element null)/(element undefined)/ non-string values — throws an assertion error in development mode (departure from RFC, which suggested rendering nothing for null/undefined)The helper is available in strict mode only (importable from
@ember/helper). It is not registered as a loose-mode builtin to avoid conflicts withember-element-helper.Implementation approach
Uses the Glimmer VM's
WrappedBuilderwith a minimalElementComponentManager(4 capabilities:dynamicLayout,dynamicTag,createInstance,wrapped). Theelementhelper returns a cached component definition per tag name:ElementComponentDefinitionwithgetTagName()returning the tag name. The VM's default template ({{yield}}) is used viaasWrappedLayout().getTagName()returnsnull, so theWrappedBuilderskips the element and just yields the block.Files changed
packages/@ember/-internals/glimmer/lib/helpers/element.ts— Core implementationpackages/@ember/-internals/glimmer/index.ts— Re-exportpackages/@ember/helper/index.ts— Public export for strict modepackages/@ember/-internals/glimmer/tests/integration/helpers/element-test.js— 14 strict-mode testssmoke-tests/scenarios/basic-test.ts— Smoke testTest plan
nullandundefinedthrow assertion errors{{on "click" ...}}) work on the dynamic element@tagargument with...attributes🤖 Generated with Claude Code