Vue: Vue 2 - input value is not updated in some cases

Created on 27 Sep 2016  ·  9Comments  ·  Source: vuejs/vue

Vue.js version

2.0.0.rc.7 and older

Reproduction Link

https://jsfiddle.net/Lonzpg7h/

Steps to reproduce

  1. Type one letter into input field - value won't show up but console logs that event was fired
  2. Type more text - you will see now that value is updating correctly
  3. Now repeat step 1 but with removed this.isDirty = value !== '' or 'is-dirty': this.isDirty declaration - value shows up immediately

    What is Expected?

I think input value should be visible on the first keystroke as well

What is actually happening?

In some case input value doesn't show up until you enter text for the second time.

Any idea what's going on here?

Most helpful comment

So what happens here is this:

  • onInput is triggered for the first time
  • this changes isDirty from true to false
  • this leads to a re-render of the component because isDirty is used in the render function
  • since the prop this.value is still an empty string '', the content of the input is reset.
  • subsequent calls to oInput don't lead to a re-render because this.isDirty is already true

This only happens because you don't $emit the event. If you do,

  • the parent gets the value
  • that triggers an update of the value prop in the child
  • that would lead to the new value being inserted in the input upon that first re-render.

...and everything would be fine. Your example broke the cycle, so to speak.

Edit: ah, Centaur beat me to it.

All 9 comments

Also noticed that when I remove v-model from my component, it's working correctly

Hi, thanks for filling this issue.

For v-model to work on custom components, you need to manually $emit an 'input' event: https://jsfiddle.net/gypskp9h/

Checkout the docs here: http://rc.vuejs.org/guide/components.html#Form-Input-Components-using-Custom-Events

@fnlctrl so I always need to emit this input event?

Maybe i will try to explain what I am trying to do here. Basically i am trying to create custom textfield component but i need to recreate lazy modifier. I thought I'd handle it this way:

onInput(e) {
  const value = e.target.value;

  this.isDirty = value !== ''; // btw. this need to be updated immediately, so I can't use onChange listener

  if (!this.lazy) {
    this.$emit('input', value);
  }
}

if lazy prop is enabled i would emit input event on blur

if (this.lazy) {
  this.$watch('focused', (focused) => {
    if (!focused) {
      this.$emit('input', this.$refs.input.value)
    }
  })
}

but as you see there's this issue when i don't emit input event inside onInput function and I am not sure how to handle this.

@fnlctrl also you didn't explain the part where removing this.isDirty = value !== '' fixes the issue

/cc @yyx990803

I always need to emit this input event

Yes.. this is how using v-model on components work.

you didn't explain the part when removing this.isDirty = value !== '' fixes the issue

Sorry I didn't notice it.. I only noticed the $emit part being missing. I don't quite have the time to dig into it now, so I'll leave this open.. Maybe @posva @LinusBorg would kindly explain what's going on here : )

@sqal

In vue 2.0,

<textfield v-model="text"></textfield>

means if you don't fire input event from textfield, the value prop of textfield will always remain the initial value of text which is ''.

Since you never fire any input event, so value prop never changes, so the only thing that can trigger the re-render of textfield is the isDirty state.

When you input a char for the first time, the char appears first, this is the native behavior of input tag. Then this input causes the change of isDirty from false to true, which in turn causes the re-render of textfield, notice that it's value is still empty, so the content of the input gets cleared.

For the subsequent char inputs, the native behavior of input still takes effect so you can see them as well. But this time, isDirty remains true, so no re-render occurred on textfield so the content is not cleared.

If this.isDirty = value !== '' is removed, even the first re-render does not occur because isDirty has not changed, so you can see what you have input immediately, that is just the consequence of textfield not being re-rendered.

I modified your code into a template-based one to make things clearer.

So what happens here is this:

  • onInput is triggered for the first time
  • this changes isDirty from true to false
  • this leads to a re-render of the component because isDirty is used in the render function
  • since the prop this.value is still an empty string '', the content of the input is reset.
  • subsequent calls to oInput don't lead to a re-render because this.isDirty is already true

This only happens because you don't $emit the event. If you do,

  • the parent gets the value
  • that triggers an update of the value prop in the child
  • that would lead to the new value being inserted in the input upon that first re-render.

...and everything would be fine. Your example broke the cycle, so to speak.

Edit: ah, Centaur beat me to it.

Thank you @Centaur and @LinusBorg for detailed explanation. Your answers helped me to understand Vue a little bit better :)

Have review your code made changes.
you just have to emit the event while changes happens in child
https://jsfiddle.net/f5k0kLob/

Was this page helpful?
0 / 5 - 0 ratings