Vue.js 3 メールフォームサンプル -フォーム入力バインディング
Vue.js 3.x.xを使ったメールフォームのサンプルを作ってみました。
https://studio-key.com/Sample2/vuejs/sample1/sample1.html
フォーム入力バインディングは公式に説明が有りますので、詳しい説明はそちらを見た方が早いと思いますが、ここではエラーメッセージを個々に表示するなど、少し工夫したフォームをご紹介したいと思います。
Javascript
セレクトボックスのオプション展開及びバリデーションを行っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | function init(){ return { name: false , email: false , emailno: false , select: false , picked: false , checkbox: false } } createApp({ data() { return { //エラーメッセージを非表示にする errors_msg:init(), //ボタンを表示する button : true , //各フォーム部品の初期値を空へ name: null , email: null , select: "" , //groupになっていない箇所の選択肢 options: [ { text: '▽' , value: '' }, { text: '肉料理' , value: '肉料理' }, { text: '魚料理' , value: '魚料理' }, ], //group箇所の選択肢 optionGroups: { 果物: [ { text: 'りんご' , value: 'りんご' }, { text: 'みかん' , value: 'みかん' }, { text: 'ぶどう' , value: 'ぶどう' }, ], }, } }, methods: { onchange: function (e) { let val = e.target.value; if (val){ document.getElementById(e.target.id).classList.remove( "inputerr" ); let id= e.target.id; this .errors_msg[id] = false ; } }, //onchangeイベント発生時 onchange: function (e) { this .onchangeEvent(e); }, //フォーム部品の背景を赤色にする errChangeBg: function (id){ document.getElementById(id).classList.add( "inputerr" ); }, //ボタン押下で処理 checkForm: function (e) { this .errors = []; let err = 0 ; //エラーメッセージフラグをいったんリセットする this .errors_msg = init(); //テキストエリア等の背景を赤くするclassを一括削除 let elements = document.getElementsByClassName( "inputs" ); Array .prototype.forEach.call(elements, function (element) { element.classList.remove( "inputerr" ); }); //各要素ごとに入力エラーを判定 if (! this .name) { err++; this .errors_msg.name = true ; this .errChangeBg( "name" ); } if (! this .email) { err++; this .errors_msg.email = true ; this .errChangeBg( "email" ); } else if (! this .validEmail( this .email)) { err++; this .errors_msg.emailno = true ; this .errChangeBg( "email" ); } if (! this .select) { err++; this .errors_msg.select = true ; this .errChangeBg( "select" ); } if (! this .picked) { err++; this .errors_msg.picked = true ; } if (! this .checkbox) { err++; this .errors_msg.checkbox = true ; } //エラーが無ければPOST if (!err) { this .button = false ; return true ; } e.preventDefault(); }, validEmail: function (email) { var re = /^(([^<>()[\]\\.,;:\s@ "]+(\.[^<>()[\]\\.,;:\s@" ]+)*)|( ".+" ))@((\[[ 0 - 9 ]{ 1 , 3 }\.[ 0 - 9 ]{ 1 , 3 }\.[ 0 - 9 ]{ 1 , 3 }\.[ 0 - 9 ]{ 1 , 3 }\])|(([a-zA-Z\- 0 - 9 ]+\.)+[a-zA-Z]{ 2 ,}))$/; return re.test(email); } } }).mount( '#app' ) |
それでは個別にどのようなことをやっているのかを説明していきたいと思います。
エラーメッセージの初期化
1 2 3 4 5 6 7 8 9 10 | function init(){ return { name: false , email: false , emailno: false , select: false , picked: false , checkbox: false } } |
各フォーム部品の下に有るエラーメッセージの表示を初期化するための変数です。
Vue.jsではdata(){…}へエラーメッセージの表示をコントロールするv-if(v-show)の初期値を設定します。ただ、送信ボタン押下時に入力済のものはエラーメッセージを消す必要が有り、フォーム部品それぞれで表示可否を判定するのは非効率と考えました。そこでボタン押下時にいったん全てを初期化し、その後各エラー判定を行うようにしています。
dataへ初期値を追加
1 | errors_msg:init(), |
ボタン押下時にエラーメッセージを初期化
1 | this .errors_msg = init(); |
dataへフォームの初期値を設定する
フォーム部品の初期化及び、ボタンの表示(送信時にボタン自身を非表示にするため)を設定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | data() { return { //エラーメッセージを非表示にする errors_msg:init(), //ボタンを表示する button : true , //各フォーム部品の初期値を空へ name: null , email: null , select: "" , //groupになっていない箇所の選択肢 options: [ { text: '▽' , value: '' }, { text: '肉料理' , value: '肉料理' }, { text: '魚料理' , value: '魚料理' }, ], //group箇所の選択肢 optionGroups: { 果物: [ { text: 'りんご' , value: 'りんご' }, { text: 'みかん' , value: 'みかん' }, { text: 'ぶどう' , value: 'ぶどう' }, ], }, } }, |
余談ですが、セレクトボックスをわざとグループ化(optgroup)+通常の二種類混合にしています。公式にはこの展開方法の記載は無いようで少し苦労しました)ちなみにVue.js ver2には有志が作成されたコンポーネントが有りました)
HTML側
1 2 3 4 5 6 7 8 9 10 | <select class = "inputs" @change= "onchange" v-model= "select" name= "select" id= "select" > <!-- 通常タイプ --> <option v- for = "option in options" v-bind:value= "option.value" >{{ option.text }}</option> <!-- グループ --> <optgroup v- for = "(group, name) in optionGroups" : label = "name" > <option v- for = "option in group" :value= "option.value" > {{ option.text }} </option> </optgroup> </select> |
なんのことはなく、通常のオプション展開とグループ化されたものを2つ作り、それぞれ展開するだけで表示できました。
ボタン押下時に入力内容をチェックする
フォーム開始タグに以下のように設定しておくと、ボタン押下時にメソッドが実行されます。
1 | <form @submit= "checkForm" |
以下が実際に実行されるメソッドの冒頭部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //ボタン押下で処理 checkForm: function (e) { this.errors = []; let err = 0; //エラーメッセージフラグをいったんリセットする this.errors_msg = init(); //テキストエリア等の背景を赤くするclassを一括削除 let elements = document.getElementsByClassName("inputs"); Array.prototype.forEach.call(elements, function (element) { element.classList.remove("inputerr"); }); //各要素ごとに入力エラーを判定 if (!this.name) { err++; this.errors_msg.name = true; this.errChangeBg("name"); } |
このあたりは公式サイトにサンプルが有りますが、
- エラーメッセージを初期化
- 赤くなった背景色を初期化
- 各要素ごとにエラー判定
といった処理を行っています。
エラーメッセージ
1 | <Transition name= "slide-fade" ><p v- if = "errors_msg.name" class = "err-message" >氏名をご記入ください</p></Transition> |
こんな感じでフォーム部品の下へ書いておき、これを表示・非表示しています。トランジションを使って少しだけ見栄えを良くしています。これはお好みで。
再入力時 ~ onchangeイベント
エラーメッセージはボタン押下時に初期化していますが、エラーが表示された箇所への再入力時、次にボタンを押すまでエラーメッセージが表示されているのが気になりますので消しています。
1 2 3 4 5 6 7 8 | onchange: function (e) { let val = e.target.value; if (val){ document.getElementById(e.target.id).classList.remove( "inputerr" ); let id= e.target.id; this .errors_msg[id] = false ; } }, |
HTML
1 | <input type= "email" name= "email" @change= "onchange" id="/> |
フォーム部品へ@change=”xxxxxxxx”を設定しておけばメソッドを実行してくれますので、入力・選択が終わればエラーメッセージと背景色を初期します。個々では内容の判定を行っていませんので、不備があってもエラーメッセージは消えます。入力チェック判定を絡めることもそう難しくは無いと思います。
あとは指定のPHPなりにPOSTすればOKだと思います。実際の現場ではフォームから送信される値が正しい保証は有りませんので、メール送信等の処理の前にもう一度チェックします。
脱Jqueryと言われて久しいですが、私が扱う案件ではまだまだ現役というか、例えば写真スライダーなど格好いいものがたくさんあって「これを使って欲しい」と指定されるケースが結構あります。そういったものでjQueryベースというのはまだまだ多い印象です。ただ、HTMLやCSSとの親和性が高いこういったjavascriptフレームワークが今後のスタンダードになっていくと思います。
ただ、この歳になると新しいものを覚えるのが大変です(^-^;