Formの各Input属性の部品をコンポーネント化してみたので、メモとして記事にしました。
テキスト
text.vue
<template>
<input
type="text"
:value="value"
:placeholder="placeholder"
@input="changeValue"
:disabled="disabled"
/>
</template>
<script>
export default {
props: {
value: {
type: String,
required: true
},
type: {
type: String,
required: true
},
placeholder: {
type: String,
required: false
},
disabled: {
type: Boolean,
default: false,
required: false
}
},
methods: {
changeValue: function(event) {
this.$emit("input", event.target.value);
}
}
};
</script>
親コンポーネント
<template>
<InputText
v-model="text"
placeholder="InputText"
/>
</template>
export default {
data() {
return {
text: ""
};
}
}
チェックボックス
checkbox.vue
<template>
<div>
<template v-for="(option, index) in options">
<label :key="index">
<input
type="checkbox"
:name="name"
:value="option.value"
@change="updateValue"
:disabled="disabled"
/>
{{ option.label }}
</label>
</template>
</div>
</template>
<script>
export default {
props: {
options: {
type: Array,
required: true
},
name: {
type: String,
required: true
},
disabled: {
type: Boolean,
default: false,
required: false
}
},
data() {
return {
values: []
};
},
methods: {
updateValue: function(event) {
if (event.target.checked) {
this.values.push(event.target.value);
} else {
this.values = this.values.filter(v => v !== event.target.value);
}
this.$emit("input", this.values);
}
}
};
</script>
親コンポーネント
<template>
<InputCheckbox
v-model="checkbox"
name="InputCheckbox"
:options="INPUT_CHECKBOX_OPTION"
/>
</template>
export default {
data() {
return {
checkbox: []
};
}
}
INPUT_CHECKBOX_OPTION: [
{ label: "hoge", value: 1 },
{ label: "huga", value: 2 }
]
セレクトボックス
select.vue
<template>
<div>
<select :name="name" @change="updateValue" :disabled="disabled">
<template v-for="(option, index) in options">
<option :value="option.value" :key="index">
{{ option.label }}
</option>
</template>
</select>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
value: {
required: true,
validator: prop => typeof prop === "string" || typeof prop === "number"
},
options: {
type: Array,
required: true
},
disabled: {
type: Boolean,
default: false,
required: false
}
},
methods: {
/**
* @return {String}
*/
updateValue: function(event) {
this.$emit("input", event.target.value);
}
},
mounted() {
this.$emit("input", this.options[0].value);
}
};
</script>
親コンポーネント
<template>
<InputSelect
v-model="formData.select"
name="InputSelect"
:options="INPUT_SELECT_OPTION"
/>
</template>
export default {
data() {
return {
select: 0
};
}
}
INPUT_SELECT_OPTION: [
{ label: "選択してください", value: 0 },
{ label: "hoge", value: 1 },
{ label: "huga", value: 2 }
]
ラジオボタン
radio.vue
<template>
<div>
<div
v-for="(option, index) in options"
:key="index"
>
<input
type="radio"
:id="`${name}-${index}`"
:checked="value === option.value"
:value="option.value"
@change="changeRadioValue"
:disabled="disabled"
/>
<label :for="`${name}-${index}`">
{{option.label}}
</label>
</div>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
value: {
type: String | null,
required: true
},
options: {
type: Array,
required: true
},
disabled: {
type: Boolean,
default: false,
required: false
}
},
methods: {
changeRadioValue (event) {
const targetId = event.target.id
const newVal = event.target.value
this.$emit("input", newVal, targetId)
}
}
};
</script>
親コンポーネント
<template>
<InputRadio
name="hoge"
v-model="radio"
:options="INPUT_RADIO_OPTION"
:disabled="isDisabled"
/>
</template>
export default {
data() {
return {
radio: null
};
}
}
INPUT_RADIO_OPTION: [
{ label: "有り", value: 1 },
{ label: "無し", value: 0 }
]
解除可能なラジオボタン
チェック済みのボタンをもう一度選択すると、チェックが解除されます。
radioDeletable.vue
<template>
<div>
<div
v-for="(option, index) in options"
:key="index"
>
<input
type="radio"
:id="`${name}-${index}`"
:checked="value === option.value"
:value="option.value"
@click="changeRadioValue"
:disabled="disabled"
/>
<label :for="`${name}-${index}`">
{{option.label}}
</label>
</div>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
value: {
type: String | null,
required: true
},
options: {
type: Array,
required: true
},
disabled: {
type: Boolean,
default: false,
required: false
}
},
methods: {
/**
* @削除できるラジオボタン
* v-model.numberを考慮して、厳密等価演算子「===」ではなく、等価演算子「==」
* event.target.value: String
* newVal: number | null
*/
changeRadioValue (event) {
let newVal = event.target.value
if (newVal == this.value) {
newVal = null
}
this.$emit("input", newVal);
}
}
};
</script>
親コンポーネント
<template>
<InputRadioDeletable
name="hoge"
v-model="hoge"
:options="INPUT_RADIO_OPTION"
:disabled="isDisabled"
/>
</template>
export default {
data() {
return {
radio: null
};
}
}
INPUT_RADIO_OPTION: [
{ label: "有り", value: 1 },
{ label: "無し", value: 0 }
]
上記コードは、こちらのリポジトリにありますので、よかったら参考にしてみてください。