-
-
Notifications
You must be signed in to change notification settings - Fork 407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic tags in glimmer templates #389
Merged
Merged
Changes from 2 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
- Start Date: 2018-10-14 | ||
- RFC PR: (leave this empty) | ||
- Ember Issue: (leave this empty) | ||
|
||
# Dynamic tag names in glimmer templates. | ||
|
||
## Summary | ||
|
||
With the transition from inner-html semantics to outer-html semantics in components, we lost one feature: Being | ||
able dynamically define the tag name of components. | ||
|
||
I think it was an useful feature and we should find a way to bring it back. | ||
|
||
## Motivation | ||
|
||
Although not something we use every day, there is a need for some components to have a dynamic tag name. | ||
|
||
This is most often used for certain low-level _presentational_ components. | ||
|
||
Take for instance a component named `<Panel>` that is used to encapsulate some presentation concerns. | ||
The template of that component could be like this: | ||
|
||
```hbs | ||
<div class="pt-10 pb-10 ps-20 box-shadow" ...attributes> | ||
{{yield}} | ||
</div> | ||
``` | ||
|
||
For accessibility and semantic reasons, sometimes a `<div>` may not be the best kind of tag. | ||
We might want the panel to be a `<section>` element, or an `<aside>` when it's content is somewhat unrelated | ||
with the rest of the page. Or maybe a `<legend>` if it's and the end of a form. | ||
|
||
With the `element` helper proposed in this RFC, this can be accomplished with something like this: | ||
|
||
```hbs | ||
{{#let (element @tagName) as |Tag|}} | ||
<Tag class="pt-10 pb-10 ps-20 box-shadow" ...attributes> | ||
{{yield}} | ||
</Tag> | ||
{{/let}} | ||
``` | ||
|
||
## Detailed design | ||
|
||
We propose to add a new `element` helper that takes a tag name and generates a contextual component that, when invoked, renders the selected element. | ||
|
||
Example: | ||
|
||
```hbs | ||
{{#let (element @htmlTag) as |Tag|}} | ||
<Tag>...</Tag> | ||
{{/let}} | ||
``` | ||
|
||
Unlike ids, classes or other attributes, the tag name of DOM element cannot be changed in runtime. | ||
|
||
To help the user understand that changing the tag of an element in runtime is an expensive operation, | ||
the syntax is intentionally chosen to express that changes in the tag name will turn down the given block | ||
and recreate it again. | ||
|
||
Having dynamic tag names can also open the door to possible XSS vulnerabilities if developers allow user-input | ||
to become tag names (e.g. `xlink:`) . To prevent that, this helper will throw an error for any tag name containing anyting | ||
but lowercase letters and dashes. | ||
|
||
A working proof of concept of this approach has been created in https://github.com/tildeio/ember-element-helper | ||
|
||
Shall this RFC me accepted, that helper would be ported to Ember.js itself, perhaps making it more efficient | ||
on the process. | ||
|
||
## How we teach this | ||
|
||
_It's vitally important that developers have awareness the negative impact that misuse of dynamic tags can have on the accessibility of an Ember application._ | ||
|
||
While dynamic tags enable a great deal of flexibility in components, it's essential to recognize that these can also negatively impact accessibility. Developers should be keenly aware of the appropriate role that should be applied per HTML element (as specified in the [WAI-ARIA specification](https://www.w3.org/WAI/PF/aria/roles)), and ensure that the role is also updated, if necessary, as the tag name is changed. In some cases, the use of native, semantic HTML elements may eliminate the need to apply a role at all, so developers should consult the WAI-ARIA specification until they are certain of the correct use for their specific use cases. | ||
|
||
This new helpers will be added to the list of [built-in helpers](https://emberjs.com/api/ember/release/classes/Ember.Templates.helpers) along | ||
with its cousins `component`, `array`, `each`, etc... | ||
|
||
Some meaningful example for the docs could look like this (extracted from a real use case): | ||
|
||
```hbs | ||
{{!-- sidebar.hbs, a template-only component --}} | ||
{{#let (element (or @htmlTag "aside")) as |Tag|}} | ||
<Tag ...>...</Tag> | ||
{{/let}} | ||
``` | ||
|
||
```hbs | ||
<Sidebar @htmlTag="nav">...</Sidebar> | ||
``` | ||
|
||
Since this feature is not very commonly used it is debatable if it should be mentioned in the more beginner-friendly guides, | ||
but if deemed appropriate the _Components > Customizing a Component's Element_ section is a perfect | ||
fit. | ||
|
||
## Drawbacks | ||
|
||
Admittedly this syntax is somewhat convoluted, as it involves using the `let` helper, the new `element` | ||
helper that yields a contextual component that is then invoked using angle-bracket syntax. | ||
|
||
This syntax is intentional to make clear that any change in the tag name would yield a new contextual component, | ||
effectively tearing down the previous one before rendering the new one, but I can see perceiving this | ||
as unnecessarily complex. | ||
|
||
## Alternatives | ||
|
||
We can decide not to do anything and leave this problem to be solved in user space, as it can be | ||
solved using only public APIs. | ||
|
||
We can also propose other alternative syntaxes. For instance, we could have a special built-in component for this: | ||
|
||
```hbs | ||
<DynamicElement @tagName={{@tagName}} class="some-class" ...attributes> | ||
</DynamicElement> | ||
``` | ||
|
||
## Unresolved questions | ||
|
||
--- |
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.
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.
Let's add something like