Help us understand the problem. What is going on with this article?

Vue.jsでフォームバリデーションをつくろう!ー実装編ー

Vue.jsでフォームバリデーションをつくろう!ー実装編ー

by matsumana07384
1 / 16

はじめに

こちらはサポーターズColab で開催の勉強会の説明資料その2です。
その1は、Vue.jsでフォームバリデーションをつくろう!ー環境構築編ーです。

Vue.jsでフォームバリデーションを作ってみよう! の内容を分割、アップデートしたものです。


この記事に書いてあること

  • 簡易アンケートフォームの作成
    • VueRouterを使ったリンクの作成
    • コンポーネントの実装
    • フォームの選択を解除したときのフォームバリデーション実装

この記事で省いていること

  • コードの一部処理
    • サーバーへのデータ送信
    • セキュリティ面のケア
  • Vue.jsの深いお話

環境

  • 端末
    • Mac OS X 10.15.1
  • インストール済みのライブラリ

単一ファイルコンポーネントの記述方法について

実装に入る前に単一ファイルコンポーネントついて説明します。
単一ファイルコンポーネントは、Vueプロジェクトにおける拡張子が.vueのファイルのことを指します。
<template><script><style> のタグで構成されています。

.vue
<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を作成し、下記を記述します。

src/views/Done.vue
<template>
  <div class="sucess">
    <p>完了しました!</p>
  </div>
</template>

<script>
export default {
}
</script>

<style scoped>
</style>

ボタンリンクの追加

Aboutページにボタンリンクを追加し、リンクが遷移できるように設定します。
src/views/About.vueに下記を記述します。

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では、パスを存在しないためです。

screencapture-localhost-8080-done-2019-12-02-23_57_45

そのため、前述で作成したsrc/views/Done.vuerouter/index.jsに設定します。

Javascript: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にあるボタンリンクをクリックで画面が遷移し、設定した文字が表示されます。

be3cc373579717c6c2690d55a7154e73


ここからアンケートをつくる準備を進めていきます。

コンポーネントを作成

src/components/ の配下に Questionnaire.vue をファイルを作成し、下記を記述します。

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を用い、 子から親へデータを渡す際はカスタムイベントを用います。


About.vueからコンポーネントを使用

src/views/About.vue から src/components/Questionnaire.vue を呼び出します。

src/views/About.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 にフォームを作り、入力した値をバインディングする処理を追加します。

フォームの作成とデータバインディングを設定

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を参照ください。

現時点ではこのような状態かと思います。
screencapture-localhost-8080-about-2019-12-04-23_11_42


ボタンの削除

前述でsrc/views/About.vueにて作成したボタンリンクですが、使用しないため下記箇所を削除します。

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文字以上の場合
src/components/Questionnaire.vue
<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文字以上を入力した状態でフォームの選択を外して、下記の挙動が再現されます。

3b64e42643e2116ceaa97106946ca0af.gif


このアンケートフォームにはいろいろな機能が足りていません。
ここからカスタマイズをして、自分なりの最高のUIのアンケートを実装してみてください!


その他のオプション

今回は使用していませんが、Vue.jsを理解する上で重要なオプションをご紹介します。

computed
任意のデータを処理を返す算出プロパティ。

watch
特定のデータや算出プロパティの状態を監視、データの変化で処理を実行するプロパティ。


さいごに

ここまで頑張ってフォームのバリデーションを実装しましたが、世の中にはかんたんにバリデーションを設定できる素晴らしいライブラリが提供されています。

これらを使えば、よりよいUXのフォームバリデーションをスムーズに開発ができます。
本当にいい時代に生まれました。


他にも、Vue.jsにはたくさんのサポートライブラリがあり、いろんなことが実現できる環境が整っています。

今回のハンズオンでは、Vue.jsの中でも一部機能にしか触れておりません。
もっと知りたい!と思って頂けた方は、ぜひ公式ドキュメントを読んだり、コミュニティに参加したりしてみてください!

ここまでご参加(お読み)頂き、ありがとうございました🙏🏻

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした