Here is a piece of functionality that all of us either have or will run into when developing web apps:
You have an interactive component through which the user can change a value, but there is some discrepancy between how you store the value and how you present it to the user.
That was a mouthful, so let's understand the use-case better with a concrete example:
Let's say you want to make a slider that will control the amount of blur on an image. The value of the blur you pass to the filter on the image should be between 0 and 8 pixels, with decimal values allowed as well. For the sake of the user, you want the slider to show values between 0 and 100% instead, and take care of the conversion yourself.
(Check out the original link for the interactive version and a bonus example at the end)
The common way to tackle this would be to define a method that gets called on every change to the input, and then modify an instance variable like so:
<range-slider :value="percentageBlur" @input="sliderInput" />
<img
src="https://placeimg.com/1000/480/arch"
:style="`filter: blur(${blur}px)`"
/>
export default {
data() {
return {
blur: 0, // Value in pixels, passed to the image blur filter
percentageBlur: 0, // Value in percentages, passed to the slider
};
},
methods: {
sliderInput(value) {
this.percentageBlur = parseInt(value);
this.blur = (value / 100) * 8; // Converting from 0-100 to 0-8
},
},
};
While the above code is perfectly fine, and does what it's supposed to, I've personally noticed that it brings with it a few inconveniences.
First of all, when a component gets more complicated, with more methods and data properties, the part of the code that handles the "blur logic" is now scattered across the component. You have your values in the data section, and the code setting them in the methods section. It might not seem like a big deal, but later on, when you don't remember how you implemented it, you'll be jumping up and down in the file trying to track all of the places where you're handling the blurring.
And second of all, you can't use v-model
on your input element. If the range-slider
component was from a library you installed and it emitted a change
event instead of an input
event, you would need to track the documentation down and see why your code is not working, whereas v-model
would automatically check the settings and wrap the right event. Plus, it just looks nicer to have a single v-model
argument in the template instead of 2 separate ones.
Get/Set computed props to the rescue
The way to solve the inconveniences above is to use the alternative syntax for computed properties, get/set.
While it is mentioned in the Vue documentation, not many developers know or use this syntax, probably because the docs don't fully specify when it makes sense to use it.
If you don't already know about it, the get/set looks like this:
computed: {
myProp: {
get() {
// When you try to get the value this.myProp in your component
// you will get the return of this function.
return this.value;
},
set(value) {
// When you set the value like: this.myProp = 10
// the prop this.value will be set to 10
this.value = value;
}
}
}
The code above essentially defines a transparent wrapper around the this.value
prop.
To make things more interesting, lets apply this new knowledge to the use case of the image blurring:
<range-slider v-model="percentageBlur" />
<img
src="https://placeimg.com/1000/480/arch"
:style="`filter: blur(${blur}px)`"
/>
export default {
data() {
return {
blur: 0, // Value in pixels, passed to the image blur filter
};
},
computed: {
percentageBlur: {
get() {
// Converts the 0-8 blur values to percentages
return Math.round((this.blur / 8) * 100);
},
set(value) {
// Converts the percentages to 0-8 values and assigns
// them to the original blur
this.blur = (value / 100) * 8;
},
},
},
};
Notice the difference? We can now enclose the functionality for the blurring conversion into one "unit" of code, the computed property. Doing it this way allows us to also declutter the template by using the v-model
directive.
Top comments (0)