はじめに
こちらはサポーターズColab で開催の勉強会の説明資料その2です。
その1は、Vue.jsでフォームバリデーションをつくろう!ー環境構築編ーです。
Vue.jsでフォームバリデーションを作ってみよう! の内容を分割、アップデートしたものです。
この記事に書いてあること
- 簡易アンケートフォームの作成
- VueRouterを使ったリンクの作成
- コンポーネントの実装
- フォームの選択を解除したときのフォームバリデーション実装
この記事で省いていること
- コードの一部処理
- サーバーへのデータ送信
- セキュリティ面のケア
- Vue.jsの深いお話
環境
単一ファイルコンポーネントの記述方法について
実装に入る前に単一ファイルコンポーネントついて説明します。
単一ファイルコンポーネントは、Vueプロジェクトにおける拡張子が.vue
のファイルのことを指します。
<template>
、<script>
、<style>
のタグで構成されています。
<template>
<!-- HTMLの記述はこちら -->
</template>
<script>
export default {
// JavaScriptの記述はこちら
}
</script>
<style scoped>
/* CSSの記述はこちら */
</style>
各タグの役割については下記の通りです。
template
HTMLの記述箇所。
HTMLのテンプレートエンジンのPUGを導入することも可能。
script
JavaScriptの記述箇所。
TypeScirptを導入することも可能。
style
CSSの記述箇所。
scoped
をつけることで、styleをコンポーネント内に閉じ込めることができる。
PostCSSやLess、Sass、Scss、Stylusを導入することも可能。
単一コンポーネントのタグの順序に関しては、公式ガイドの単一ファイルコンポーネントのトップレベルの属性の順序を参照ください。
ページを追加
完了ページを作っていきます。
こちらの項目は、Vue.jsよりもvue-router
の話が中心です。
ファイルを追加
src/views/
にDone.vue
を作成し、下記を記述します。
<template>
<div class="sucess">
<p>完了しました!</p>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
ボタンリンクの追加
Aboutページにボタンリンクを追加し、リンクが遷移できるように設定します。
src/views/
のAbout.vue
に下記を記述します。
<template>
<div class="about">
<h1>This is an about page</h1>
<!-- ここから、追加 -->
<router-link to="done">
<button type="button" name="done" value="done">done</button>
</router-link>
<!-- ここまで、追加 -->
</div>
</template>
templete
ボタンリンクを追加しました。
リンクに用いている<router-link>
は、ルーターを使用しているアプリケーションにリンクを追加できるタグです。
デフォルトで、<a>
タグとhref
で描画されます。
今回の下記の記述箇所は、
<router-link to="done">
<button type="button" name="done" value="done">done</button>
</router-link>
下記のようにブラウザに描画されます。
<a href="/done" class="" data-lb-orig-href="http://localhost:8081/done">
<button type="button" name="done" value="done">done</button>
</a>
詳細は、API リファレンス | Vue Routerを参照ください。
追加したページを設定
現時点で先程作成したボタンリンクをクリックしても、真っ白なページが表示されます。
これは本来のSPAでは、パスを存在しないためです。
そのため、前述で作成したsrc/views/Done.vue
をrouter/index.js
に設定します。
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import { doesNotReject } from 'assert'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: function () {
return import(/* webpackChunkName: "about" */ '../views/About.vue')
}
},//カンマを追加
//------ ここから追加
{
path: '/done',
name: 'done',
component: function () {
return import('../views/Done.vue')
}
},
//----- ここまで追加
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
- path:作成したページのpathを設定
- name:ルートを特定するための名前を設定(なくてもOK)
- component:表示するファイルを記述
ここまで設定が完了すると、/about
にあるボタンリンクをクリックで画面が遷移し、設定した文字が表示されます。
ここからアンケートをつくる準備を進めていきます。
コンポーネントを作成
src/components/
の配下に Questionnaire.vue
をファイルを作成し、下記を記述します。
<template>
<div>
<p><b>{{title}}</b></p>
</div>
</template>
<script>
export default {
props: {
title : String
}
}
</script>
<style scoped>
</style>
template
script
で宣言した title
を表示できるよう記述
script
src/views/About.vue
のテンプレートで src/components/Questionnaire.vue
を使用するため、About.vue
が親、Questionnaire.vue
が子という関係になります。
Vue.jsは単方向のデータフローのため、親と子の間でデータをやり取りする際には宣言が必要です。
親から子へデータを渡す際にはprops
を用い、 子から親へデータを渡す際はカスタムイベント
を用います。
-
props
の詳細は、公式ガイドのpropsを参照 -
カスタムイベント
の詳細は、公式ガイドのカスタムイベントを参照
About.vueからコンポーネントを使用
src/views/About.vue
から src/components/Questionnaire.vue
を呼び出します。
<template>
<div class="about">
<h1>This is an about page</h1>
<!-- ここから追加 -->
<Questionnaire title="アンケート"></Questionnaire>
<!-- ここまで追加 -->
<router-link to="done">
<button type="button" name="done" value="done">done</button>
</router-link>
</div>
</template>
<script>
// 上から追加
import Questionnaire from '@/components/Questionnaire.vue'// 追加
export default {
name: 'about',
components: {
Questionnaire
}
}
//下まで追加
</script>
template
script
で登録したコンポーネントを Questionnaire
タグとして使用します。
title
にアンケート
という文字列を親から子に渡しています。
script
components
のオプション内にQuestionnaire
をコンポーネントとして定義しています。
入力フォームバリデーションを作成
ここからsrc/components/Questionnaire.vue
にフォームを作り、入力した値をバインディングする処理を追加します。
フォームの作成とデータバインディングを設定
<template>
<div>
<p><b>{{ title }}</b></p>
<!-- ここから追加 -->
<form>
<p>Hello, {{ questionnaire.nickName }}</p>
<div>
<label>あだ名:</label>
<input type="text" name="nickname" placeholder="呼ばれたい名前をどうぞ" v-model="questionnaire.nickName">
</div>
<p>
<label>TwitterID:</label>
<input type="text" name="belong" value="" placeholder="ないかたはスキップOK">
</p>
<p>
<label>今日の勉強会との関わり:</label>
<input type="text" name="connection" value="" placeholder="思いの丈をどうぞ!">
</p>
<button type="submit">submit</button>
</form>
<!-- ここまで追加 -->
</div>
</template>
<script>
export default {
props: {
title : String
},//カンマを追加
//------ ここから追加
data: function() {
return {
questionnaire: {
nickName: null,
}
}
}
//------ ここまで追加
}
</script>
<style scoped>
</style>
templete
データとフォームの入力項目をバインドするには、 v-model
を使用します。
v-model
の詳細は、公式ガイドのv-modelを参照ください。
script
data
は、アプリケーションで使用するデータを記述します。
data
の詳細は、公式ガイドのdataを参照ください。
ボタンの削除
前述でsrc/views/About.vue
にて作成したボタンリンクですが、使用しないため下記箇所を削除します。
<router-link to="done">
<button type="button" name="done" value="done">done</button>
</router-link>
フォームのバリデーションを設定
フォームから選択が外れた際にバリデーションチェックを行う処理を実装します。
今回はTwitterIDのフォームに追加します。
TwitterIDの条件は、ユーザー名の登録のヘルプを参照すると…
ユーザー名の長さは15文字までです。名前は50文字までですが、ユーザー名は使いやすいように短くなっています。
ユーザー名には英数字(文字A~Z、数字0~9)しか含めることはできません。ただし、上記のとおりアンダースコア(_)は例外です。希望するユーザー名に、記号やダッシュ、スペースが含まれていないことを確認してください。
上記の通りのため、下記の2つをエラーを表示する条件として追加します。
- 半角英数以外で入力されている場合
- 15文字以上の場合
<template>
<div>
<p><b>{{ title }}</b></p>
<!-- ここから追加 -->
<form v-on:submit.prevent="onSubmit">
<div>
<p>Hello, {{ questionnaire.nickName }}</p>
<label>あだ名:</label>
<input type="text" name="nickname" placeholder="呼ばれたい名前をどうぞ" v-model="questionnaire.nickName">
</div>
<div>
<label>TwitterID:</label>
<input type="text" name="belong" value="" placeholder="ないかたはスキップOK" v-model="questionnaire.twitterID" v-on:change="checkForm">
</div>
<div>
<label>今日の勉強会との関わり:</label>
<input type="text" name="connection" value="" placeholder="思いの丈をどうぞ!">
</div>
<p class="error"> {{ validation.result }}</p>
<button v-on:click="checkForm">submit</button>
</form>
<!-- ここまで追加 -->
</div>
</template>
<script>
export default {
props: {
title : String
},
data: function() {
return {
questionnaire: {
nickName: null,
twitterID: null,//追加
},//カンマを追加
//------ ここから追加
validation:{
result: "",
}
//------ ここまで追加
}
},/*ここにカンマを追加*/
//------ ここから追加
methods: {
checkForm: function(event){
var booleanTwitterID = false
var inputTwitterID = this.questionnaire.twitterID
if(!this.checkString(inputTwitterID)){
this.validation.result = "半角英数字および_のみで入力ください"
}
else if(!this.checkMaxLength(inputTwitterID)){
this.validation.result = "15文字以内で入力ください"
}
else {
booleanTwitterID = true
}
if(booleanTwitterID === true){
this.validation.result=""
alert('Hello,' + inputTwitterID + '!')
}
event.preventDefault()
},
checkString: function(inputdata){
var regExp = /^[a-zA-Z0-9_]*$/
return regExp.test(inputdata);
},
checkMaxLength: function(inputdata){
var booleanLength = false
inputdata.length <= 15 ? booleanLength = true : booleanLength = false;
return booleanLength
}
}
//------ ここまで追加
}
</script>
<style scoped>
/* ここから追加 */
.error { color: red; }
/* ここまでを追加 */
</style>
template
v-on:submit.prevent="onSubmit"
は、submit イベントによってページがリロードされないイベント修飾子です。
v-on:change
は、カスタムイベントの一種で変更を取得することができます。
script
<script>
内の methods
は、アプリケーションで使用するメソッドを指定します。
処理の分割やイベントハンドラなどを記述します。
ここまでの記述を該当ファイルに追加すれば、完成です🎉
TwitterIDのフォームに半角英数字以外や15文字以上を入力した状態でフォームの選択を外して、下記の挙動が再現されます。
このアンケートフォームにはいろいろな機能が足りていません。
ここからカスタマイズをして、自分なりの最高のUIのアンケートを実装してみてください!
その他のオプション
今回は使用していませんが、Vue.jsを理解する上で重要なオプションをご紹介します。
computed
任意のデータを処理を返す算出プロパティ。
- 詳細は、算出プロパティとウォッチャ — Vue.js 内の算出プロパティを参照ください。
watch
特定のデータや算出プロパティの状態を監視、データの変化で処理を実行するプロパティ。
- 詳細は算出プロパティとウォッチャ — Vue.js 内のウォッチャを参照ください。
さいごに
ここまで頑張ってフォームのバリデーションを実装しましたが、世の中にはかんたんにバリデーションを設定できる素晴らしいライブラリが提供されています。
これらを使えば、よりよいUXのフォームバリデーションをスムーズに開発ができます。
本当にいい時代に生まれました。
他にも、Vue.jsにはたくさんのサポートライブラリがあり、いろんなことが実現できる環境が整っています。
今回のハンズオンでは、Vue.jsの中でも一部機能にしか触れておりません。
もっと知りたい!と思って頂けた方は、ぜひ公式ドキュメントを読んだり、コミュニティに参加したりしてみてください!
ここまでご参加(お読み)頂き、ありがとうございました🙏🏻