作りながら学ぶVue.js パート6【条件付きレンダリング/エラーハンドリング】
このnoteは、JavaScriptのフレームワークであるVue.jsを初心者でも扱えるようになることを目指して、実際にアプリケーションを作りながら学んでいくチュートリアルです。
前のパートでは、フロントエンド開発におけるAPI通信、そしてJavaScriptにおいて重要な要素である非同期処理に関する簡単な解説などを行いました。
今回は前回実装したタスク作成処理がよりユーザーフレンドリーな形になるよう手直しをしながら新しくVue.jsでよく使われるv-ifについても学んでいきましょう。
Formを画面に表示する
実際にアプリケーションを円滑に動作させるためにはまだ弄るところがありますが、一旦このフォームを画面に表示させてみましょう。src/App.vueのtemplateとscriptを以下のように修正します。
<template>
<div id="app">
<Header/>
<Form/>
</div>
</template>
<script>
import Header from "./components/Header"
import Form from "./components/Form"
export default {
name: 'app',
components: {
Header,
Form
}
}
</script>
1.対象のファイルをimport
2.scriptタグのcomponentsにimportしたコンポーネント名を記載
3.表示したい箇所にタグとして追加する
と言う手順でFormコンポーネントを組み込みます。
まだまだブサイクな画面ではありますが、フォームが画面に表示されていることが確認できるはずです。
そしてこの状態でフォームに適当な値を入力して、作成ボタンを押すと、デベロッパーツールにてサーバーからのレスポンスが確認できるはずです。
さて、これでとりあえずフォームに入力された値をAPIサーバーに送信して、レスポンスを取得する、と言うところまでは実装できたわけですが、実用的なアプリケーションにするためには手直ししなければならない部分がいくつかあります。
1つずつ見ていきましょう。
フォーム入力内容のチェック
もしタイトルのないタスクが存在する場合、タスク管理アプリケーションとしてまともに機能しなくなってしまいます。
そのため新規作成時にタイトルが必ず入力されているかをチェックする必要があるわけです。このチェックをバリデーションとも言います。
サーバーの方でチェックされていれば問題ないと言えば無いのですが、フロンエンド側でもチェックしておくことで無駄な通信を減らしたり、ユーザーフレンドリーなフォームに仕上げることができます。
完了期限や詳細などについては、場合によっては無くても問題無いケースもあると思うので、チェックは行いません。
async createTask() {
if (this.inputTitle === "") {
return alert("タイトルを入力してください");
}
const result = await api.post("/tasks", {
title: this.inputTitle,
period: this.inputPeriod,
detail: this.inputDetail
});
console.log(result);
}
単純にdataのinputTitleに値が入力されていない場合、アラートを表示して、returnで後続の処理を行わないようにしています。
実際のアプリケーションの場合、alertを使うのでは無く、エラー情報用のdataを用意して、更新することで画面表示を切り替えてユーザーにメッセージを伝える、と言う手法の方が取られることが多いでしょう。
v-ifについて
そのようにエラーが発生した場合のみ、その内容をユーザーに伝えたいといったようなケースにおいて活用できるVue.jsの機能、条件付きレンダリングについて説明しておきましょう。
条件付きレンダリングとは言葉の通り、条件に応じて画面の表示を切り替えたい時に使える機能です。
今回はv-ifを用いて条件付きレンダリングを実装し、エラーが発生した時のみ表示される領域を作成してみましょう。
まずは条件を判定するために用いるための値をdataの部分に追加します。とりあえずvalidationErrorと言う名前で説明しやすいようにtrue or falseで用意しておきます。
data() {
return {
inputTitle: "",
inputPeriod: "",
inputDetail: "",
validationError: false
};
},
そしてtemplateの部分に移動し、エラーの時だけ表示させたい要素をv-ifディレクティブを適用させたタグで囲って記載します。上から3行目の部分ですね。
<template>
<div class="container">
<div v-if="validationError">タイトルを入力してください</div>
<div class="item">
<input type="text" name="title" v-model="inputTitle">
</div>
<div class="item">
<input type="text" name="period" v-model="inputPeriod">
</div>
<div class="item">
<textarea name="detail" id cols="30" rows="10" v-model="inputDetail"></textarea>
</div>
<div class="item">
<button v-on:click="createTask">作成</button>
</div>
</div>
</template>
この書き方によって「validationErrorの値がtrueの時だけ表示する」と言うことを示します。
JavaScriptや他の言語でifを使った分岐処理を書いたことのある人であればイメージしやすいのではないかと思います。
ちなみにそういったifによる条件分岐と同様に、elseif/elseも表現することが可能です。
<div v-if="foo">
FOO
</div>
<div v-else-if="bar">
BAR
</div>
<div v-else>
Other
</div>
またtrue/falseだけではなく、文字列や数値を使った条件を用いることも可能ですので覚えておいてください。
<div v-if="foo === 'A'">
Aだよ
</div>
<div v-if="ba < 1">
1より小さいよ
</div>
templateへのv-ifを使った記述が完了したら、エラーが発生した際に、アラートを出していた箇所を、この判定に使われているvalidationErrorをtrueに変更する処理に変更します。
if (this.inputTitle === "") {
// return alert("タイトルを入力してください");
this.validationError = true
}
これでタイトルを入力しなかった際にフォーム上部にメッセージが表示されるようになります。
APIとのHTTP通信でエラーが発生した場合
次の改善点はAPIサーバーにトラブルがあったりしてaxiosを利用したHTTP通信に失敗した場合のハンドリングの追加です。
このハンドリングを行わないと、トラブルが発生したことがユーザーに伝わら無いことから不安を与えてしまったり、予期せぬ挙動を招きかねません。
そのため今回のような処理を行なった際はエラー発生時の分岐処理も行なっておくのが吉です。
async/awaitを使った際の基本的なエラーハンドリングは以下の通り。
methods: {
async createTask() {
if (this.inputTitle === "") {
this.validationError = true
return
}
let result;
try {
result = await api.post("/tasks", {
title: this.inputTitle,
period: this.inputPeriod,
detail: this.inputDetail
});
} catch (err) {
this.apiError = true
}
}
}
tryとcatchと言う、プログラミング言語を触ったことのある人は見覚えのあるかもしれないワードが出てきました。
今回のような書き方をした場合、awaitで待機している処理においてエラーが発生した場合、catchのなかの処理を実行してくれるようになります。
またconstの代わりにletでresultを宣言されるよう修正されています。この変更は成功した場合と失敗した場合の両方でresultを扱うことができるようにするためです。
async/awaitを用いた処理でエラーハンドリングを行う場合、このtry/catchを主に使う形ではあるのですが、await/catchというアプローチも存在することを知っておくといいでしょう。
またまた別記事の紹介で恐縮の極みではありますが、以下の記事でそのアプローチが紹介されていますので、気になった方・Promiseの理解がある程度進んだ方はご覧になってみてください。
さてdataへのフラグ用dataの追加とtemplateへのメッセージ表示部分も忘れないようにしましょう。
こちらも先ほど説明したv-ifの分岐を活用して、エラーメッセージの表示制御を行なっています。
data() {
return {
inputTitle: "",
inputPeriod: "",
inputDetail: "",
validationError: false,
apiError: false
};
},
<template>
<div class="container">
<div v-if="validationError">タイトルを入力してください</div>
<div v-if="apiError">サーバーでエラーが発生しました</div>
<div class="item">
<input type="text" name="title" v-model="inputTitle">
</div>
<div class="item">
<input type="text" name="period" v-model="inputPeriod">
</div>
<div class="item">
<textarea name="detail" id cols="30" rows="10" v-model="inputDetail"></textarea>
</div>
<div class="item">
<button v-on:click="createTask">作成</button>
</div>
</div>
</template>
これでNode.jsで起動していたAPIサーバーを停止させてから、フォームを作成しようとするとエラーメッセージが表示されるようになりました。
エラーメッセージのお掃除
APIリクエストやバリデーションでエラーが発生した場合、メッセージを表示していますが、今のままだとユーザーが入力し直して処理が成功した後にもこのメッセージが残ったままになってしまいます。
ユーザーの混乱を招くことにも繋がりますので、処理が成功した場合にエラーメッセージ表示を判定するためのフラグをfalseに戻しておくべきでしょう。
エラーの条件の反対の状況になった時にフラグを元に戻す、といったイメージですね。
async createTask() {
if (this.inputTitle === "") {
this.validationError = true;
return;
} else {
this.validationError = false;
}
let result;
try {
result = await api.post("/tasks", {
title: this.inputTitle,
period: this.inputPeriod,
detail: this.inputDetail
});
} catch (err) {
this.apiError = true;
return;
}
this.apiError = false;
}
タスク作成が無事完了したあとの処理
またユーザー体験を高めるためにも、作成したタスクがすぐに画面に表示された方が望ましいです。
この部分の処理についてはタスクの一覧部分を作成したのちに対応していく形にしましょう。
課題
鋭い方なら気づいたかも知れませんが、バリデーションエラーとAPIエラーの表示って必ずしも分ける必要はなさそうですよね。
あとはエラーが発生したところでエラーメッセージを指定できるようにした方がソースコードが分かりやすくなりそうです。
そこで今回学んだことをベースに、1つのdataをエラーメッセージの格納先として、フォーム部分について修正を加えてみてください。
以下のデータを用意する形で、今回実装したものと同様の機能を実現してみてください。
data() {
return {
inputTitle: "",
inputPeriod: "",
inputDetail: "",
errorMessage: ""
};
},
押さえておくべきポイントとしてはtrue or falseで用意されていたvalidationErrorとapiErrorの2つがテキスト型であるerrorMessageに変更されている点です。
少し難しいかも知れませんが、次回冒頭で解説しますので、ぜひ挑戦してみてください。
次回は作成されたタスクの一覧表示を実装しながらVue.jsでよく使われる機能について解説していきます。
この記事が気に入ったらサポートをしてみませんか?