■前提
Laravelベースのアプリでプロフィール登録のようなフォーム送信を行う機能を実装する際に、
Vue.js + axiosを使って、フォーム送信後に「登録完了!」みたいなメッセージが出て数秒後に消える機能を入れてみました。
ベースとなるテンプレートにはLaravelのbladeテンプレートを使用しています。
■処理の流れ
①ProfNameコンポーネント内に定義した入力フォームにデータを入力し、登録ボタンクリック
②クリックイベントをコンポーネント内で補足してリクエストデータを生成し親コンポーネントに送る
③親コンポーネントでイベントを補足したら非同期でDBに登録するメソッドを実行
④非同期処理実行後メッセージを表示する処理を実行
⑤setTimeoutでメッセージ表示の3秒後にメッセージを非表示にする処理を実行
※わざわざリクエストデータを親コンポーネントに送るのは非同期で登録する処理を他の項目でも使うため共通化したかったからです。
■コード
bladeテンプレート
<!-- profile.blade.php -->
<!-- メッセージを表示するmsgコンポーネント -->
<msg></msg>
<!-- 名前を登録するフォームのprof-nameコンポーネント -->
<!-- LaravelからUserのデータを受け取る 子コンポーネントからnamesentというイベントが送られてきたらeditValueメソッド(後ほど記載)を実行する-->
<prof-name v-bind:user="{{ $user }}" v-on:namesent="editValue"></prof-name>
ProfNameコンポーネント
<!-- /resources/assets/js/components/ProfName.vue -->
<template>
<div>
<label><span>ニックネーム</span></label>
<div>
<input name="name" type="text"v-model="name" placeholder="ニックネーム" required>
<!-- buttonをクリックするとsendNameメソッドが実行される -->
<button v-on:click="sendName">+</button>
</div>
</div>
</template>
<script>
export default {
props:["user"],//profile.blade.phpからuserを受け取る
data: function(){
return{
name:this.user.name,
request:{
name:''
}
}
},
methods: {
//リクエストに必要なidとrequestを親コンポーネントに送る
sendName(){
if(this.name){
this.request.name = this.name;
//namesentというイベントを送信
this.$emit("namesent",{
id:this.user.id,
request:this.request
});
}
}
}
}
</script>
Vueインスタンスの生成
//resources/assets/js/app.js
//・・Vueの読み込み等(割愛)
//コンポーネントの読み込み
//プロフィール入力フォーム
Vue.component('prof-name',require('./components/ProfName.vue'));
//登録後に出すメッセージ(色んな場所で使えるようにコンポーネント化)
Vue.component('msg',require('./components/common/Msg.vue'));
//Vueインスタンス生成
const app = new Vue({
el: '#app',
methods: {
//コンポーネントから受け取ったデータを非同期でDBに保存する
editValue(req){
axios.patch('/users/' + req.id, req.request).then(res => {
if(res.data.result === '成功'){
//msgコンポーネントをゆっくり表示
$(".msg_cover").addClass('msg_appear');
//3秒後にゆっくり非表示 setTimeoutを使えば簡単だった
setTimeout(function() {
$('.msg_cover').removeClass('msg_appear');
}, 3000);
}
});
}
},
});
msg_appear
というクラスをつけたり外したりすることで、メッセージを表示させたり非表示にしたり出来ます。(msg_appear
の詳細は下記)
Msgコンポーネント
<!-- /resources/assets/js/components/ProfName.vue -->
<template>
<div class="msg_cover msg_hide">
<p>登録完了!</p>
</div>
</template>
<style type="text/css">
/*基本スタイル*/
.msg_cover {
text-align: center;
width: 100%;
padding: 10px 0;
height: 60px;
line-height: 40px;
font-size: 20px;
background: pink;
color: white;
margin-left: -6%;
z-index: 1000;
}
/*画面の60px上に置いて見えないようにしておく(デフォルト)*/
.msg_hide {
position: fixed;
top: -60px;
/*transitionで変化させる属性や変化速度、変化の仕方を記載(消す時)*/
transition-property:top;
transition-duration:.6s;
transition-timing-function:ease-in-out;
}
/*画面上部に表示させる*/
.msg_appear {
top:0;
/*transitionで変化させる属性や変化速度、変化の仕方を記載(表示させる時)*/
transition-property:top;
transition-duration:.6s;
transition-timing-function:ease-in-out;
}
</style>
Vue.jsを使うとメッセージのコンポーネントを切り出せて、コンポーネント内に全てのスタイルをかけちゃうので便利でした。
子から親にデータを送るときに$emitを使ってイベントとして送る必要があるっぽいので少し面倒に感じますが、慣れたらどーってことないです。