Vue.jsでFormの各要素をComponent化する際の覚え書き
Vue.jsでFormの各要素をComponent化する際の覚え書き:
Vue.jsでFormの各フィールドをcomponent化する際の実装例です。
コンポーネント化することで、デザイン、バリデーションなどが全Formで共通化できます。
流行りのAtomicデザインを適応する際などに良いかと思います。
以下コードは、全てこちらのリポジトリにあります。
https://github.com/kawamataryo/Vue-atomic-form
まず基本のinputのcomponet実装例。
フィールドの値は、
呼び出し元は、、
基本的にinputと同様の実装です。
typeを外してrowsとcolsを追加しました。
呼び出し元は
radioボタンの場合は、要素を
あとはコンポーネント内で
呼び出し元はoptionsに表示したいラベルと値を持つオブジェクトの配列を
基本はRadioボタンと同様に
ただ、selectの場合、初期で選択されているので、何も変更しない場合
それを防ぐため、
呼び出し元はradioボタンの時と同様に
これが一番苦労しました。
一見ラジオボタンと同様なのですが、復数選択可能なので、配列で値を返す必要があります。
そして
呼び出し元はこちらです。
結果としてこのようなcomponentが集約されたフォームができます。
Vue.jsでForm部品をComponent化する
https://qiita.com/wakame_isono_/items/611e51ff965d698bbc7c
Vue.jsでFormの各フィールドをcomponent化する際の実装例です。
コンポーネント化することで、デザイン、バリデーションなどが全Formで共通化できます。
流行りのAtomicデザインを適応する際などに良いかと思います。
以下コードは、全てこちらのリポジトリにあります。
https://github.com/kawamataryo/Vue-atomic-form
Input
まず基本のinputのcomponet実装例。フィールドの値は、
@input
で変更を監視して、$emit
の'input'
を利用し親コンポーネントのv-modelに値を送っています。updateValue
が引数で受け取るe
にはevent
が入っているので、そこからtaget.value
で値を取得してemit
で親コンポーネントに渡しています。MyInput.vue
<template> <input :type="type" :name="name" :value="value" :placeholder="placeholder" @input="updateValue" /> </template> <script> export default { name: "MyInput", props: { value: { type: String, require: true }, type: { type: String, require: true }, name: { type: String, require: true }, placeholder: { type: String, require: false } }, methods: { updateValue: function(e) { this.$emit("input", e.target.value); } } }; </script>
App.vue
<MyInput v-model="sampleForm.text" placeholder="サンプル" name="sample-input" type="text" ></MyInput>
Textarea
基本的にinputと同様の実装です。typeを外してrowsとcolsを追加しました。
MyTextarea.vue
<template> <textarea :name="name" :value="value" :placeholder="placeholder" :rows="rows" :cols="cols" @input="updateValue" ></textarea> </template> <script> export default { name: "MyTextarea", props: { value: { type: String, require: true }, name: { type: String, require: true }, placeholder: { type: String, require: false }, rows: { type: Number, require: false }, cols: { type: Number, require: false } }, methods: { updateValue: function(e) { this.$emit("input", e.target.value); } } }; </script>
App.vue
<MyTextarea v-model="sampleForm.textarea" placeholder="サンプル" name="sample-textarea" :rows="10" :cols="50" ></MyTextarea>
Radio Button
radioボタンの場合は、要素をoptions
として配列で渡すようにしています。あとはコンポーネント内で
v-for
でoptions
を回し、内部のvalue
とlabel
を表示します。updateValue
は同様です。MyRadio
<template> <fieldset> <template v-for="(option, index) in options"> <label :key="index"> <input type="radio" :name="name" :value="option.value" @change="updateValue" />{{ option.label }} </label> </template> </fieldset> </template> <script> export default { name: "MyRadio", props: { value: { type: String, require: true }, options: { type: Array, require: true }, name: { type: String, require: true }, }, methods: { updateValue: function(e) { this.$emit("input", e.target.value); } } }; </script>
options
として渡します。App.vue
// 変数optionsには[{label: "hoge", value: "1"}, {label: "fuga", value: "2"}]のようなオブジェクトの配列を設定 <MyRadio v-model="sampleForm.radio" name="sample-radio" :options="options" ></MyRadio>
Select
基本はRadioボタンと同様にoptions
で値とラベルのオブジェクトの配列を渡しv-for
で表示します。ただ、selectの場合、初期で選択されているので、何も変更しない場合
@change
でのupdateValue
が呼ばれず、値が空になってしまいます。それを防ぐため、
mouted()
で初期選択時の値を$emit
で送っています。select.vue
<template> <fieldset> <select :name="name" @change="updateValue"> <template v-for="(option, index) in options"> <option :value="option.value" :key="index"> {{ option.label }} </option> </template> </select> </fieldset> </template> <script> export default { name: "MySelect", props: { value: { type: String, require: true }, options: { type: Array, require: true }, name: { type: String, require: true }, }, methods: { updateValue: function(e) { this.$emit("input", e.target.value); } }, mounted() { this.$emit("input", this.options[0].value); } }; </script>
options
で表示したい値、ラベルを渡します。App.vue
```App.vue // 変数optionsには[{label: "hoge", value: "1"}, {label: "fuga", value: "2"}]のようなオブジェクトの配列を設定 <MySelect v-model="sampleForm.select" name="sample-select" :options="options" ></MySelect>
Checkbox
これが一番苦労しました。一見ラジオボタンと同様なのですが、復数選択可能なので、配列で値を返す必要があります。
data
でvalues
を持ちupdateValue
では、チェック状態を判定してそのvalues
に値を追加、削除を行っています。そして
emit
で返すのはdata
として持っているvalues
です。MyCheckbox.vue
<template> <fieldset> <template v-for="(option, index) in options"> <label :key="index"> <input type="checkbox" :name="name" :value="option.value" @change="updateValue" />{{ option.label }} </label> </template> </fieldset> </template> <script> export default { name: "MyCheckbox", props: { options: { type: Array, require: true }, name: { type: String, require: true }, }, data() { return { values: [] }; }, methods: { updateValue: function(e) { if (e.target.checked) { this.values.push(e.target.value); } else { this.values = this.values.filter(v => v !== e.target.value); } this.$emit("input", this.values); } } }; </script>
App.vue
// 変数optionsには[{label: "hoge", value: "1"}, {label: "fuga", value: "2"}]のようなオブジェクトの配列を設定 <MyCheckbox v-model="sampleForm.checkbox" name="sample-checkbox" :options="options" ></MyCheckbox>
Form全体
結果としてこのようなcomponentが集約されたフォームができます。App.vue
<template> <div id="app"> <form action=""> <MyLabel>InputText</MyLabel> <MyInput v-model="sampleForm.text" placeholder="サンプル" name="sample-input" type="text" ></MyInput> <MyLabel>TextArea</MyLabel> <MyTextarea v-model="sampleForm.textarea" placeholder="サンプル" name="sample-textarea" :rows="10" :cols="50" ></MyTextarea> <MyLabel>RadioButton</MyLabel> <MyRadio v-model="sampleForm.radio" name="sample-radio" :options="options" ></MyRadio> <MyLabel>Checkbox</MyLabel> <MyCheckbox v-model="sampleForm.checkbox" name="sample-checkbox" :options="options" ></MyCheckbox> <MyLabel>Select</MyLabel> <MySelect v-model="sampleForm.select" name="sample-select" :options="options" ></MySelect> <MyBtn @click="sendForm">send</MyBtn> </form> </div> </template> <script> import MyInput from "./components/MyInput"; import MyTextarea from "./components/MyTextarea"; import MyRadio from "./components/MyRadio"; import MyCheckbox from "./components/MyCheckbox"; import MySelect from "./components/MySelect"; import MyBtn from "./components/MyBtn"; import MyLabel from "./components/MyLabel"; export default { name: "app", components: { MyTextarea, MyInput, MyRadio, MyCheckbox, MySelect, MyBtn, MyLabel }, data() { return { sampleForm: { text: "", radio: "", select: "", textarea: "", checkbox: [] }, options: [ { label: "fizz", value: "3" }, { label: "buzz", value: "5" }, { label: "fizzBuzz", value: "15" } ] }; }, methods: { sendForm() { // formデータの送信処理 } } }; </script>
参考
Vue.jsでForm部品をComponent化するhttps://qiita.com/wakame_isono_/items/611e51ff965d698bbc7c
コメント
コメントを投稿