Skip to content

Instantly share code, notes, and snippets.

@elpete
Last active October 15, 2020 12:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elpete/4209addd834d2590e2723166d643fcf6 to your computer and use it in GitHub Desktop.
Save elpete/4209addd834d2590e2723166d643fcf6 to your computer and use it in GitHub Desktop.
v-model binding for custom components and special input types

Vue allows for v-model binding on custom components. This is because v-model is just syntactic sugar for v-bind:value and v-on:input.

This is mostly true — there seems to be some magic around radio, select, and checkbox inputs. This is important to note for when you want to wrap one of these input types in a custom component. You can't pass your value prop to v-model because then you are mutating props.

Instead, you can use a computed property with both a get and a set method to proxy the v-model to the input. This example shows all three input types along with a mixin to make this easier (which may or may not be that useful in practice).

You can see the code in action at CodeSandbox.

<template>
<div>
<radio-set
v-model="selectedValue"
:options="options"
></radio-set>
<select-box
v-model="selectedValue"
:options="options"
></select-box>
<checkbox-grid
v-model="selectedValues"
:options="options"
></checkbox-grid>
<pre>{{ selectedValue }}</pre>
<pre>{{ selectedValues }}</pre>
</div>
</template>
<script>
import RadioSet from "./components/RadioSet";
import SelectBox from "./components/SelectBox";
import CheckboxGrid from "./components/CheckboxGrid";
export default {
components: {
RadioSet,
SelectBox,
CheckboxGrid
},
data() {
return {
selectedValue: "two",
selectedValues: [ "two" ],
options: [ "one", "two", "three" ]
};
}
}
</script>
<template>
<div>
<template v-for="option in options">
<input
v-model="selected"
type="checkbox"
:value="option"
/>
<label v-text="option"></label>
</template>
</div>
</template>
<script>
import ComputedModel from "../mixins/ComputedModel";
export default {
mixins: [ ComputedModel ],
props: {
options: {
type: Array,
default() {
return [];
}
}
}
}
</script>
export default {
props: {
value: { required: true }
},
computed: {
selected: {
get() {
return this.value;
},
set( newValue ) {
this.$emit( "input", newValue );
}
}
}
}
<template>
<div>
<label v-for="option in options">
<input
:value="option"
v-model="selected"
type="radio"
/> {{ option }}
</label>
</div>
</template>
<script>
import ComputedModel from "../mixins/ComputedModel";
export default {
mixins: [ ComputedModel ],
props: {
options: {
type: Array,
default() {
return [];
}
}
}
}
</script>
<template>
<div>
<select v-model="selected">
<option
v-for="option in options"
:value="option"
v-text="option"
></option>
</select>
</div>
</template>
<script>
import ComputedModel from "../mixins/ComputedModel";
export default {
mixins: [ ComputedModel ],
props: {
options: {
type: Array,
default() {
return [];
}
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment