Skip to content
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

Boolean attributes #2158

Closed
trusktr opened this issue May 12, 2024 · 6 comments
Closed

Boolean attributes #2158

trusktr opened this issue May 12, 2024 · 6 comments
Labels
enhancement New feature or request

Comments

@trusktr
Copy link
Contributor

trusktr commented May 12, 2024

Continued from

Describe the bug

If I write

return <custom-element foo={someBoolean}></custom-element>

there is no guarantee that the custom element will have a JS property that is designed to handle the boolean value or to reflect it back to an attribute. We need explicit control.

Your Example Website or App

n/a

Steps to Reproduce the Bug or Issue

  1. try using various custom elements and realize you can't explicitly control boolean attributes

Expected behavior

As a user we expect to have more control over boolean attributes vs boolean JS properties.

Screenshots or Videos

No response

Platform

  • OS: any
  • Browser: any
  • Version: 1.8.17

Additional context

No amount of automatic rules or heuristics can solve this. We need bool:foo= to make it explicitly pickable by the user.

For example,

  • one custom element might be able to handle a .foo = bool JS property and reflect it to an attribute, another one might not (it might only detect the presence of an attribute in its CSS style), and another might accept the JS property but not reflect.
  • a user of any of the elements in the previous point might want to set the JS property, or they might need to explicitly set the HTML attribute for the element's features to work if the element has no JS property, or they might need to use the HTML attribute even if the custom element supports a JS property because the user may want to use that in their own CSS selector, etc.

There's absolutely no way we can determine a rule for this that works for everyone.

The only thing we can do is provide bool:foo= to give users the choice that they need.

Lit's html template tag supports the following:

  • foo= - always sets an attribute (the element may independently map this to a JS property)
  • .foo= - always sets a JS property (the element may independently reflect this back to an attribute)
  • ?foo - always adds the attribute if the value is truthy, always removes the attribute if the value is falsy (the element may independently map this to a JS property)

The Lit templating is fully decoupled from the concept of how any element underneath handles attributes and properties, it is 100% up to the user to decide if they set attribute strings, JS properties, or add/remove boolean attributes. This makes Lit's templating 100% compatible with 100% of all possible built-in and custom elements that could ever exist.

Solid's JSX and html template does the following:

  • foo= - automatic heuristic: set the string attribute on built-in elements by default (with special cases), or set the JS property on custom elements
  • prop:foo= - always set the JS property
  • attr:foo= - always set the attribute

But Solid has no bool:foo=, which is the missing piece. We 100% need this piece if we want to be fully compatible with custom elements (we will never know the set of all boolean attributes that arbitrary custom elements can define, and not all custom elements will automatically map boolean JS properties to boolean attributes).

In comparison, React's new custom elements support,

is not as good as Lit's or Solid's above because it is automatic with no manual control (cc @josepharhar, @sebmarkbage, @gaearon, @rickhanlonii), although this is a much better state than before that at least allows custom elements to have a path such that their implementation fully works in React when CE authors follow certain rules (always handle JS properties, and liberally reflect primitive values back to attributes). But not all CE authors are going to follow these rules; some of them may not even know or care about React.

To get this support or lack of support recognized across frameworks, we need to update custom-elements-everywhere so we can test this in all frameworks:

@josepharhar
Copy link

If the custom element is upgraded and has a setter for the property, then when react renders it should assign the boolean you're providing in the JSX into the custom element's property instead of attribute.

@ryansolid ryansolid added the enhancement New feature or request label Jun 27, 2024
@sparecycles
Copy link

sparecycles commented Aug 15, 2024

Not sure if this is the right place to comment but I recently hit the
<button draggable>...</> doesn't work snag which was discussed in the previous issue.

I'm perfectly happy with the runtime behavior but I was wondering if we could change

interface HTMLAttributes {
    ...
-    draggable?: boolean | "false" | "true" | undefined;
+    draggable?: "false" | "true" | undefined;

just for the DX?

image

@ryansolid
Copy link
Member

Not sure if this is the right place to comment but I recently hit the <button draggable>...</> doesn't work snag which was discussed in the previous issue.

I'm perfectly happy with the runtime behavior but I was wondering if we could change just for the DX?

I don't believe so. It would be inconsistent special casing. The DOM has boolean and psuedo boolean attributes. And the problem with the psuedo booleans like draggable is that the way we set boolean attributes won't hold up for them. I think we used to have it this way and it would lead to behavior that was unexpected. So this is where we ended on the last issue. If memory serves the problem was that false wouldn't set it to false but actually just not make the attribute. It was something like that.

@trusktr
Copy link
Contributor Author

trusktr commented Aug 16, 2024

@ryansolid what are your thoughts on the syntax proposed in the OP, which would allow explicit control over this no matter what element a boolean attribute is being used on?

TLDR:

// true means add the attribute
return <button bool:draggable={true}>...</button>
// false means remove the attribute
return <button bool:draggable={false}>...</button>

This would work regardless of the underlying element, regardless if a custom element has or does not have a draggable JS property (basically the behavior of Lit html, but different syntax).

It is also worth noting that Pota provides JSX and html alternatives for Solid.js, including Lit-style syntax (f.e. ?draggable={true}) as well as the bool: syntax described above.

This is all thanks to @titoBouzout. We have been keen on making a PR to update html to use Pota's technique, which is much more stable than Solid's, but we haven't had a chance to do that yet. It is also more flexible, f.e. it allows <Show>...</Show> instead of the more cumbersome <${Show}>...</${Show}>. These are some goodies that can be brought to Solid's!

@trusktr
Copy link
Contributor Author

trusktr commented Aug 16, 2024

I forgot to mention, that explicit values can currently be achieved with attr:, f.e. <button attr:draggable={"false"}> or <button attr:draggable={"true"}> should always work for the above use case. The key here is that a framework that tries to special case everything can never capture every case, which is why syntax giving them 100% coverage would be great.

@ryansolid
Copy link
Member

Will be in 1.9.

Added in commit: 2a3a198

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants