Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js: recalculate and re-render 'data' based on 'prop' change. Is this the better way to do it?

I have a component that has a 2 way data binding on some value from data. For example:

Vue.component ({
    data: ()=>({
        destinationValue: "1000"
    }),
    template: '<input v-model="destinationValue">'
})

Now, let's say this component receives a prop named originValue from which I want to calculate my destinationValue. If for example, the conversion ratio is equal to 2, then I can do something like this:

Vue.component ({
    props: ['originValue'],
    data: () => ({
        destinationValue: this.originValue* 2
    }),
    template: '<input v-model="destinationValue">'
})

The problem here is that, if this component receives a new value for the originValue prop, the value of destinationValue will not be updated.

I need destinationValue to be updateable from the < input> tag but also overwritten every time the component receives a new originValue.

To try and solve this, at first I tried:

Vue.component ({
    props: ['originValue'],
    data: ()=>({
        destinationValue: this.originValue* 2
    }),
    watch: {
        originValue: function ( val, oldVal ) {
            this.destinationValue= val * 2
        }
    }
    template: '<input v-model="destinationValue">'
})

But this would not work. If a new value for originValue is received, the value will not be updated in the template. This is because the imperative reassignment of destinationValue inside the watcher function will overwrite Vue's internal watchers on that key breaking its reactivity.

This is my workaround:

Vue.component ({
    props: ['originValue'],
    data: ()=>({
        destinationValue: {
            auxiliarKey: this.originValue* 2
        }
    }),
    watch: {
        originValue: function ( val, oldVal ) {
            this.destinationValue.auxiliarKey = val
        }
    }
    template: '<input v-model="destinationValue.auxiliarKey">'
})

What I have done here is make destinationValue an object with an auxiliarKey inside to hold the result value and then only modify that subkey in the watcher function for the originValue prop. This works because Vue's internal watchers on destinationValue keys are preserved when my watcher's function overwrites auxiliarKey inside it.

I could not use a computed property because the 2way data binding (v-model) does not seem to work on computed properties, only on data.

Do you guys know if there is a cleaner way to do this? One that would not force me to create the auxiliarKey subkey?

Ty in advance.

like image 454
Juan Martín Migliorini Avatar asked Nov 21 '25 07:11

Juan Martín Migliorini


1 Answers

Use a computed property.

Vue.component ("x-two", {
    props: ['originValue'],
    computed: {
        destinationValue(){
            return this.originValue * 2;
        }
    },
    template: '<span>{{destinationValue}}</span>'
})

After your comments/edits

As I understand it, you want to bind to your custom component using v-model. v-model is just sugar for :value="myValue" and @input="myValue = $event". To allow your component to support v-model, just implement those two things.

Vue.component ("x-two", {
    props: ['originValue', 'value'],
    computed: {
        destinationValue(){
            const newValue = this.originValue * 2;
            this.$emit('input',newValue);
            return newValue;
        }
    },
    template: '<span>{{destinationValue}}</span>'
})

Then in your parent template:

<x-two :origin-value="count" v-model="calculatedCount"></x-two>

where calculatedCount is some data value on your parent.

Working example.

I should mention, the very latest version of Vue (as of today) added the notion of customizing the property and event v-model bind to, so that you could use something other than the value property and the input event.

like image 97
Bert Avatar answered Nov 23 '25 05:11

Bert