Vue: Potential issue updating an array of strings

Created on 6 Mar 2016  路  6Comments  路  Source: vuejs/vue

Hey,

I am new to Vue and have run into a bit of an issue. At first I thought it was just an issue with my code but after creating a thread over on the forums it seems it may be a bug.

So the bug occurs in the following situation:
1) Their is a parent component that has a computed property that returns an array of stings.
2) This string is used to generate child components with a v-for
3) In the child component I am updating the string with this.string = 'new string'
4) The computed property on the parent is updated but the set function is never called

I have setup a JS Fiddle here that can demonstrate the issue:
https://jsbin.com/vitejugala/edit?html,js,output

In the example Paragraph is the parent component and word is the child component.
To reproduce simple click on one of the 'Update word' buttons. Now if you log the parent array by clicking 'Output words' you can see the array has updated but the alerts on watch or set functions haven't fired.

Most helpful comment

Exactly, we have same usage as @rpkilby. Several reusable form field components using v-model internally. Depreciating/removing .sync would break them so bad :(
I'm not sure if there is some other way to achieve that.

All 6 comments

Your computed "get" property returns a "NEW" array, so it is no longer bound to the data item. Not sure the best way going forward, I guess it depends what you are trying to achieve. Probably better to have the data property as an array of words, then a computed to concatenate them for output as/when required?

This is not a bug. You are using two-way sync on a v-for intermediate scope variable - this simply won't work, consider the following example in JavaScript:

  words.forEach(function (word) {
    word = 'new'
  })

This would not affect the original words array: you are just changing a local variable inside the iterator function. Same in the context of v-for.

I think this is a good example that makes me want to completely deprecate the .sync modifier, because it's too easy for beginners to use it inappropriately.

We use sync in input-like cases where we can't use v-model. eg,

<foo-input :value.sync="someValue"></foo-input>

In our case, foo-input internally uses v-model to bind to its inputs, however the component abstracts some more complicated behavior.

Maybe add an admonition in the docs about its usage and link to expected usage?

Exactly, we have same usage as @rpkilby. Several reusable form field components using v-model internally. Depreciating/removing .sync would break them so bad :(
I'm not sure if there is some other way to achieve that.

Yep. And this is the _only_ place we use two-way binding.

@yyx990803 would it be possible to add a component strategy/handler to v-model that just two-way binds to a prop? eg, something like:

<foo-input v-model="someValue"></foo-input>
// foo-input
Vue.extend({
    props: ['a', 'b'], // would the model prop need to be in props?
    model: 'a',
})

model in the above example kind of does double duty.

  • Signals to v-model that this is an input-like component.
  • Specifies the prop that v-model would bind to.

A more complete example based on a feet/inches height input we're using:

<!-- form.html -->
<height-input v-model="user.height"></height-input>
<!-- height.html -->
<input type="number" min="0" v-model="feet" number />
<input type="number" min="0" max="11" v-model="inches" number/>
// height-input.js
import Vue from 'vue';
import template from './height.html';


Vue.component({
  name: 'height-input',
  model: 'value',
  template: template,

  data: function() {
    return {
      feet: this.value ? Math.trunc(this.value / 12) : '',
      inches: this.value ? this.value % 12 : '',
    };
  },
  ...

});

Was this page helpful?
0 / 5 - 0 ratings