0

I have a simple component that takes a value prop and shows a textfield for editing it. If the number is below zero I want it to just be zero, if the text is deleted out of the textfield I want it to be zero. In my emit event, I can see that I am emitting the correct value, it's just not reflected in the textfield itself.

I have a fiddle link here but, the main parts are listed below here:

<div id="app">
  <test-component v-model="foo"></test-component>
</div>
const TestComponent = {
    props: ['value'],
    template: `<div>{{value}}
    <input type="number" :value="value" @input="update($event.target.value)" />
  </div>`,
  methods: {
    update(value) {
      this.$emit("input", value <= 0 ? 0 : value)
    }
  }
}
new Vue({
  el: "#app",
  components: {
    'test-component': TestComponent
  },
  data: {
    foo: 1
  },

})

Basically what's happening is that I can see my event is being emitted with the value 0, but, and you can see the {{value}} is reflecting what is in the prop, however the printed value in the textfield itself is empty (or less than zero)

There's definitely something fundamental I'm misunderstanding here, I thought that the :value should reflect reality, but, obviously not. Any help is most appreciated!

patrickdavey
  • 1,966
  • 2
  • 18
  • 25
  • You will have to do two way binding if you want your v-model reflecting the emitted value. You could either change your `foo` in `update()` or simply consider using `min` attr. – blackcityhenry Oct 22 '19 at 07:04
  • Thanks for the reply @blackcityhenry , but, I don't quite follow you. Did you run the fiddle link? I thought the correct way to do it was pass props down and events back up? The input basically works fine, except for when you delete all the text out of the textfield, at that point, I really want the _empty textfield_ to show the number zero. Could you update the fiddle to show what you mean? – patrickdavey Oct 22 '19 at 07:17
  • Check if the `v-model` is added to the input field – Olotin Temitope Jul 22 '22 at 11:48

2 Answers2

1

You need to put a key on your TestComponent then each time foo is updated change the key, this should trigger a re-render which is the issue here.

Michael
  • 4,538
  • 5
  • 31
  • 58
  • :) Thanks, I was just testing that out and you're absolutely right, the key does fix it up. I'm not sure of the nicest way to do this with `v-model` . I ended up (in test) using a custom input event [new fiddle](https://jsfiddle.net/u6xe9Lq3/2/ ). Seems like there must be a nicer way to do this. Anyway, thanks for the fast response. – patrickdavey Oct 22 '19 at 07:47
  • Personally I like keys, another way would be to use a computed property, and have the `v-model` use that as a dependancy. – Michael Oct 22 '19 at 07:50
  • @Michael why is a :key required? Won't change in props trigger the reactivity system? – Varun Agarwal Oct 22 '19 at 08:10
  • Not 100% clear on this but the short answer is no, it depends how and where the props are changed in this case the changed in the parent component does not trigger a rerender. – Michael Oct 22 '19 at 08:18
  • Isn't this an anti-pattern, mutating props directly? I was thinking a computed setter-getter might be more correct way of doing things? – Varun Agarwal Oct 22 '19 at 08:22
0

As per @Michael's response, the trick was to use key to trigger a re-render.

there's a new fiddle here , it's not particularly elegant, I just wanted to get it going first :)

Updated elements here:

<div id="app">
  <test-component :key="key" :value="foo" @input="input"></test-component>
</div>

const TestComponent = {
    props: ['value'],
    template: `<div>
    <input type="number" :value="value" @input="update($event.target.value)" />
  </div>`,
  methods: {
    update(value) {
      this.$emit("input", value <= 0 ? 0 : value)
    }
  }
}
new Vue({
  el: "#app",
  components: {
    'test-component': TestComponent
  },
  data: {
    key: 0,
    foo: 1
  },
  methods: {
    input (value) {
      this.foo = value;
      this.key += 1;
    }
  }

})
patrickdavey
  • 1,966
  • 2
  • 18
  • 25