2

I have several of these which follow the same pattern.

<div class="form-control">
  <label for="foo">foo</label>
  <input type="text" id="foo" bind:value={foo} />
</div>
<div class="form-control">
  <label for="bar">bar</label>
  <input type="text" id="bar" bind:value={bar} />
</div>

I tried to create the same thing using this loop.

{#each [ foo, bar ] as v}
<div class="form-control">
  <label for="{v}">{v}</label>
  <input type="text" id="{v}" bind:value={v} />
</div>
{/each}

However this does not create a 2-way binding. I found that if I change either of those variables somewhere else then the changes will be reflected in the input fields. However if I type something in the input field, the variables are not updated to match. How can this be made to work? Why does the binding only work in one direction?

3
  • Have a look at this example - Is that what you are trying to achieve?
    – Corrl
    Commented Oct 31, 2021 at 21:08
  • No, in that example there should be two variables declared with the same names as what's in the array. However even if I do that those variables don't get bound to the inputs.
    – JSNinja
    Commented Nov 1, 2021 at 4:22
  • I see, you could add the declarations of the variables to your examples as well, to make this clearer
    – Corrl
    Commented Nov 1, 2021 at 7:01

2 Answers 2

3

Something like this will reflect input changes:

<script>
  let obj = { foo: 1,  bar: 2 }
</script>

<p>{Object.values(obj).join(', ')}</p>

{#each Object.keys(obj) as k}
  <div class="form-control">
    <label for="{k}">{k}</label>
    <input type="text" id="{k}" bind:value={obj[k]} />
  </div>
{/each}
1
  • Your answer is so simple but seriously gives credence to prototypical effect of object property inheritance. Never forget that an object has properties that can be bound. Commented Sep 12, 2023 at 6:37
1

To understand why it sometimes works and why it sometimes doesn't, we have to look at what actually happens.

{#each [foo, bar] as v}

In this case, when either foo or bar changes, the array has changed and the each is re-evaluated, this explains why changing the variables elsewhere triggers an update in your input field.

Now if foo and bar are simply regular variables, adding them to an array like this will add a copy of this variable and not the variable itself. This would explain why updating it inside the each loop doesn't trigger an update.

Note that if both would have been objects instead, v would hold a reference to this object instead and it would work as expected:

<script>
 let A = { name: 'AA' };
 let B = { name: 'BB' };
</script>

{#each [A, B] as v}
  <input type="text" bind:value={v.name}>
{/each}

The above code would actually work as you expect, because of this reference.

The solution to this is to have your array start of as an actual array instead of constructing it on the fly:

<script>
 let arr = ['foo', 'bar']
</script>

{#each arr as v}
  <input type="text" bind:value={v}>
{/each}

Not the answer you're looking for? Browse other questions tagged or ask your own question.