sveltejs/svelte

Support for two-way binding on custom elements

nsaunders opened this issue · 9 comments

Custom Elements (as in Web Components) cannot currently take advantage of two-way binding syntax.

For example, instead of this...

<custom-textbox
  label="Email Address"
  type="email"
  placeholder="Enter your email address"
  style="width: 300px"
  bind:value="email" />

...I have to do this...

<custom-textbox
  label="Email Address"
  type="email"
  placeholder="Enter your email address"
  style="width: 300px"
  value="{{email}}"
  on:input="set({ email: event.target.value })" />

I think this is kind of a bummer. :-(

Wrote a simple example using a web component library that shows this

https://svelte.dev/repl/bc48abd5c41d4dee833e5757745cf57f?version=3.4.4

Seems like this is blocked in the compiler.
But it does not work when bypassing this validation

               this.bindings.forEach(binding => {                                                                                                                                                                 
                    const { name } = binding;
                    if (name === 'value') {
                        if (this.name !== 'input' &&
                            this.name !== 'textarea' &&
                            this.name !== 'select') {
                            component.error(binding, {
                                code: `invalid-binding`,
                                message: `'value' is not a valid binding on <${this.name}> elements`
                            });
                        }

@nsaunders I don't know if this is what you looking for, I try to add bind:value on bootstrap modal, and it works.

Modal.svelte

<script>
  import { onMount } from 'svelte'
  export let value = false

  let modalElement

  const displayModal = value => {
    if (process.browser) {
      value
        ? window.$(modalElement).modal('show')
        : window.$(modalElement).modal('hide')
    }
  }

  onMount(() => {
    window.$(modalElement).on('hidden.bs.modal', () => {
      value = false
    })
  })

  $: process.browser && displayModal(value)'
</script>

Parent.svelte

<script>
  import Modal from '../Modal.svelte'

  let modal
</script>

<div>
  <button
    on:click={() => (modal = true)}
    class="btn btn-label-primary">
    Add New
  </button>

  <Modal bind:value={modal}/>
</div>

If someones comes across this is search of a clearer answer,

If you want to bind to a "regular" html input, you can use the usual bind:value={yourValue}.

If you want to bind to a custom element you created:

<YourComponent bind:yourExportedVarNameHere={yourValue} />

then in your custom component:

<script>
    export let yourExportedVarNameHere;
</script>

Cheers,

@sudomaxime any hint while this is not working for ui5-input element?

The propname in ui5-input is "value"

https://codesandbox.io/s/divine-architecture-3qvlc?file=/App.svelte

It seems the method mentioned by @sudomaxime is not working when customElement: true is set in rollup.config.js.

HelloWorld.svelte (child)

<script>
    import Hello from './components/Hello'
    import World from './components/World'
    export let value;
</script>

<svelte:options tag={'x-app-helloworld'}/>
<input type="text" bind:value={value} >

<input>
<x-app-hello />
<x-app-world />

App.svslte(parent)

<x-app-helloworld bind:value={value}/>

Then the parent will show an error: 'value' is not a valid binding on <x-app-helloworld> elements.

How can I solve this?

@MAXI0008 I opened another issue... #4838

Same issue with ionic ion-input element. A webcomponent too.

Hi @antony - just curious if it is possible to reopen this one? You closed it maybe because of @sudomaxime comment, but that solution only works if you control the webcomponent

As you can see per later comments and still there - looking at my comment - if you don't control the webcomponent you can still not bind:value to get the binding to the elements value property.

Or do we need a workaround like mentioned here - https://css-tricks.com/using-custom-elements-in-svelte/

Wrapping the custom element in another custom element and then making the property reflective or something like that? Or even use:action for it?