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

attributes/properties transformation & custom elements defaulting to attributes #373

Merged
merged 2 commits into from
Feb 5, 2025

Conversation

titoBouzout
Copy link
Contributor

@titoBouzout titoBouzout commented Oct 21, 2024

It includes two changes:

  1. avoids transforming case and dashes for attributes/properties.
  2. It doesn't default to properties in custom elements. It defaults to attributes.

Rationality: Seems like numerous people expect custom elements to just receive attributes 1, 2, 3, 4, and they will use prop: when they want to set a property. It seems strange wanting to use prop:, but it looks to me, they are used to dealing with this sort of problem. Kind of makes sense as attributes can only receive strings.

Avoiding transformation of case and dashes, makes the issue easier to deal with it. I do not think this will cause any problem, as one won't mix up case when using attr/props, so normalizing it is unnecessary and could cause some other issues

If I do not miss anything, this only affects custom-elements. I suggest merging it whenever possible, as this is something people seem to expect to behave this way. I also would like to recommend merging it as v1.10.0 to not break and alienate people using the previous behaviour.

else if (isCE && !isProp && !isChildProp) node[toPropertyName(prop)] = value;
else node[propAlias || prop] = value;
else if (isCE && !isProp && !isChildProp) {
node.setAttribute(prop, value);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is nice, unlike React's which forces a string coercion of value before passing it to setAttribute. The native DOM setAttribute method already will do that coercion, but! a custom element class can override the setAttribute method to also handle non-string values and for example map them directly to JS properties, which is not possible with React at time of this writing.

This means that, even if Solid JSX calls el.setAttribute, a custom element can itself be designed to pass the value directly to JS and skip the native setAttribute, therefore achieving the same thing as prop: without the user having to use prop: syntax. The A-Frame library, for example, overrides setAttribute like this for all its elements. I'm contemplating adding this to Lume Element. In my case, I could map JS value types to the JSX attribute types. But other people's CE libraries don't have to, they can map only string types for non-prop: attributes for example.

Copy link
Contributor

@trusktr trusktr Oct 21, 2024

Choose a reason for hiding this comment

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

In fact, had React at least removed that stupid string coercion, they'd have largely eliminated the need for React 19 CE support because all CEs would have had a way to accept non-string values.

But they didn't listen.

Copy link
Owner

Choose a reason for hiding this comment

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

Overriding setAttribute is interesting and something I hadn't thought of. I'm sure it has other implications/inconsistency but it definitely is interesting.

Copy link
Contributor

Choose a reason for hiding this comment

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

Overriding setAttribute is interesting

Indeed, for example:

class MyEl extends HTMLElement {
  setAttribute(attr, value) {
    if (typeof value === string) super.setAttribute(value) // attributeChangedCallback can handle parsing, or etc.
    else this[attr] = value
  }
}

but React had get in the way by doing this:

theElement.setAttribute(jsxProp, String(theValueFromJSX))

forcing strings on everyone for no reason at all.

Luckily React 19 is finally out now and things are better.

) {
if (forceProp) {
prop = prop.slice(5);
isProp = true;
} else if (isHydrating(node)) return value;
if (prop === "class" || prop === "className") className(node, value);
else if (isCE && !isProp && !isChildProp) node[toPropertyName(prop)] = value;
else node[propAlias || prop] = value;
else if (isCE && !isProp && !isChildProp) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like we wouldn't need isCE anymore. The logic would be for any element (doesn't matter if it is CE or not) set the attribute (except for the special cases).

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah that is the benefit here. Not treating CE's special. I think that is directionally where we go. I sort of over compensated because I wanted WC development to be pleasant but we shouldn't really be trying to pave over the awkward parts. They are the way they are and its up to the specs to handle that.

@titoBouzout
Copy link
Contributor Author

Just as a reminder, it may be a need to also update component-register for solid-element
https://github.com/ryansolid/component-register/blob/master/src/utils.ts#L141-L152

@ryansolid
Copy link
Owner

So are @titoBouzout and @trusktr in agreement? If you guys are good with this we can merge it into the next branch. This won't be Solid 1.0 time period but lets start 2.0 right.

@titoBouzout
Copy link
Contributor Author

@ryansolid I spoke with @trusktr a few days ago asking for this, and we are in agreement.

@ryansolid ryansolid changed the base branch from main to next February 5, 2025 23:27
@ryansolid ryansolid merged commit 5996bfc into ryansolid:next Feb 5, 2025
2 checks passed
@ryansolid
Copy link
Owner

Awesome thank you. Merged into next.

@trusktr
Copy link
Contributor

trusktr commented Mar 7, 2025

Is this coming out in Solid 2.0? Or earlier? I'm wondering how breaking it is.

What are the steps to try this with Solid 1.x? I'm guessing I'll just npm link it into an npm linked solid-js, and build solid-js with it.

I can report back if anything breaks. In particular I think the type helpers I made for @lume/element will need updates, for which I have a type tests here:

https://github.com/lume/element/blob/f431740df30efd674a69a14ae1cfcfc905394c8d/src/framework-types/solid.test.tsx

@titoBouzout
Copy link
Contributor Author

Is this coming out in Solid 2.0? Or earlier? I'm wondering how breaking it is.

This is coming out in Solid 2.0, not earlier, as we decided to introduce the breaking changes in one step instead of gradually (to make it less confusing). There are some deprecations notices tho.

For a custom-element is pretty breaking because we have been coding custom elements with properties instead of attributes. So unless these been reflecting everything and also not using the prop transformation cash-case to camelCase it will be most likely be broken.

What are the steps to try this with Solid 1.x? I'm guessing I'll just npm link it into an npm linked solid-js, and build solid-js with it.

There is v2.0.0-experimental.0 https://github.com/solidjs/solid/releases/tag/v2.0.0-experimental.0
but be aware that some set of patches for everything related to attribute/properties are missing. (I will see of sending these this week)

Basically what we agreed and should expect at release is:

  • set everything as attributes except for some special properties (innerHTML, textContent, and friends) [so attributes are lowercase, no more autoplay vs autoPlay]
  • use prop: for properties
  • event handlers in cameCase (with native handlers as on:)
  • no props/attrs transformation

Think this is a good middle ground and we are pretty happy about that, right?

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

Successfully merging this pull request may close these issues.

3 participants