Vue 3 custom element's prop default value (as attr) not reflected once rendered
Vue version
3.2.45
Link to minimal reproduction
https://github.com/funkso2010/vue3-cus-el-emit
Steps to reproduce
How to run the issue:
dist folder is already prebuild. You need to leave index.html as is, since custom elements are hardcoded into html file. Vite config 'emptyOutDir' is set to 'false'. Node 14.20.0.
npm install
npm run preview
If index.html is not opening please use something like http-server (npm package) to run: http-server ./dist
Steps to reproduce:
npm run preview
or open index.html in dist folder (see above instructions if you don't see any buttons).- Two buttons: one with hardcoded values, second one without hardcoded props and should show default values.
- Click 'add text and red 1'. Result as expected: text will change to 'button text 1' and red background will be red. Click 'remove text and red 1'. Result as expected: the button text will be removed and the background will be black. This is expected custom element's behavior.
- Reload the page. Click 'remove text and red 1'. Result as expected: text will be removed and background changed to black. This is expected custom element's behavior.
- Click 'remove text and red 2'. Result: nothing happens. Expected: the text to be removed and background changed to back. Click 'add text back and red 2'. Result: button's text is changed. Now, click 'remove text and red 2'. Result: text is removed and background changed to black. This is not expected behavior (or might need clarification).
This doesn't seem to be explained in the following default props behavior definition: https://vuejs.org/guide/extras/web-components.html#props
What is expected?
When custom element with prop defaults is initially rendered and props are not provided, default props value should be shown and custom element should have attributes generated with defaulted values.
What is actually happening?
When custom element is initially loaded the default values are working but the attributes for default values are not reflected.
We are basing our request per docs below: https://vuejs.org/guide/extras/web-components.html#props
System Info
System:
OS: macOS 12.6
CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 93.47 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 14.20.0 - /usr/local/bin/node
Yarn: 1.22.17 - ~/.npm-global/bin/yarn
npm: 6.14.17 - /usr/local/bin/npm
Browsers:
Chrome: 108.0.5359.124
Safari: 16.2
npmPackages:
vue: ^3.2.45 => 3.2.45
Any additional comments?
No response
The current behavior is pretty much intended the way it is. We considered defaults to be internal an not requiring reflection on the element.
The docs you link may not be specific enough in that regard, but refer to props being reflected eher appropriate, and in our implementation, that means "when the user sets one, we reflect it on the other".
Is there prior art n other web component implementations to reflect default values like you are asking for? If so could you point is to it?
Not unwilling to implement this, just not sure what the web component community considers good practices here
Hello @LinusBorg , I hope you are doing well.
Thank you for such a speedy reply. We appreciate all your and your team's hard innovative work.
In my opinion props handling is correct if a user passes all the props that override the defaults.
What makes it complicated:
Imagine we have a <custom-video>
web component that consumes html5 <video>
tag. We set video-controls
boolean prop default to true. In this case the custom element (once rendered) with default would look like this: <custom-video></custom-video>
.
The controls will be displayed in browser, but we would not be able to remove them unless we set them (element.setAttribute('video-controls', '')) and then remove element.removeAttribute('video-controls').
If we try to set: element.setAttribute('video-controls', false), vue gives an error since vue checks the type and reads false as a string.
Some workarounds probably exist, but not sure if those are efficient. One of which is always to set defaults to false and set it to true
when needed. Other is to check if tag exists (element.hasAttribute('video-controls')) and then to element.videoControls = false if it doesn't.
I think it makes sense to only set default string, number, and boolean types values as attributes on initial render if no own values are provided. Objects and arrays are handled more complicated, so those would have to be targeted separately.
I haven't seen any art on web components, I would have to read up on it. I don't think many people are using web-components these days, but it for sure gets more popular.
Could you please discuss this matter with your team members and let us know your thoughts? If you decide to stick to the current implementation, that is fine with us, but I was hoping we could make the experience slightly better.
Best regards, Taras Savka
Thanks for the explanation. However, I think your own explanation makes it clear that this is not really an issue:
the situation only applies to boolean attributes, and specifically to a case where you would use true
as the default for its absence. I would rate this an anti-pattern. You say:
The controls will be displayed in browser, but we would not be able to remove them unless we set them (element.setAttribute('video-controls', '')) and then remove element.removeAttribute('video-controls').
That is true, but regardless of what we fix, you now have created a situation where a developer using your web component also can't create an instance of it that starts with hidden controls:
- Creating the element without the boolean attribute will render the controls as it defaults to
true
. - Creating the element with the boolean attribute will render the controls as it is now explicitly set to
true
.
So in my mind, boolean props should never have true
as their default. And other primitive types can't suffer from similar problems to the one you described above.
So in summary I would argue:
- this is not a real issue in need of a fix.
- yet we could still entertain the idea of adding default attributes, it seems interesting. However it's not an urgent matter taking priority for us.