Vue.jsで色々書き方のルールがあるようで、備忘録として実際に使ったものをここにまとめていきます。
最初は少ないですが随時追加していく予定です。
##1.propsで受け取った変数の中身を表示
{{ }}
を使う
<template lang="html">
<div>
<p>{{ message.message }}</p>
<small>{{ message.user.name }}</small>
</div>
</template>
<script>
export default {
props:["message"]
}
</script>
##2.propsで受け取った変数をimgタグのsrc属性に指定
v-bind
を使う。 {{}}
で囲わずそのまま指定する。
<template lang="html">
<div>
<img v-bind:src="message.user.img_path">
<!--<img :src="message.user.img_path">-->
</div>
</template>
<script>
export default {
props:["message"]//ChatLog.vueからmesssageを受け取る
}
</script>
##3.Laravelから受け取ったデータをVueコンポーネントで使う
<!-- room.blade.php -->
<!-- v-bindディレクティブにblade記法で使いたい値を入れる -->
<chat-log v-bind:user-id="{{ $user->id }}"></chat-log>
ここで v-bind
に入れた値がVueコンポーネント ChatLog.vue
に伝わる。
<!-- ChatLog.vue -->
<!-- propsで受け取ってtemplate内で表示 -->
<template lang="html">
<div class="chat-log">
<p>{{ userId }}</p>
</div>
</template>
<script>
export default {
props:["userId"]
}
</script>
※ v-bindを使う際の注意点
v-bindに指定する変数名はケバブケース
v-bind:user-id
propsで受け取る際はキャメルケース
props["userId"]
で表現する。
##4.Vueテンプレート描画後に描画された要素に対して処理を行う
以下のライフサイクルを参考に、描画が終了したタイミングで処理を行うよう記述する。
mounted
でVueインスタンス生成時に指定したel
とvm.$el
(ルートな DOM 要素)が入れ替わるようなので、その後のbeforeUpdate
に記述する。
以下は、描画後に画面下部にスクロールする処理
//resources/assets/js/app.js
const app = new Vue({
el: '#app',
beforeUpdate(){
var bodyHeight = $('body').height();
$('body').scrollTop(bodyHeight);
}
});
これでうまくいきました。
ちなみにmounted()
に記述しても動作しなかった。
チャット等でメッセージが追加された後に一番下の最新メッセージを表示したい場合は、updated()
に上記のコードを記述する。
##5.入力フォームの値の取得 + 非同期で送信
フォームに入力された値を取得するにはv-model
を利用します。
v-modelにキー名を指定することで指定したキーで入力値を取得可能です。
以下の例では、<button>
をクリックすることでeditName()
という非同期で入力値をPOSTするメソッドを呼び出し、その中でv-model
で指定したname
という名前でデータを取得しています。
<!-- resources/assets/js/components/ProfName.vue -->
<template>
<div>
<input v-model="name" placeholder="ニックネーム" required>
<button v-on:click="editName">変更</button>
</div>
</template>
<script>
data: function(){
return{
//postで送信するためのリクエストデータ初期化
request:{
name:''
}
}
},
methods: {
editName(){
//リクエストデータに入力値を代入
this.request.name = this.name;
//axiosでリクエストデータ送信
axios.post('/users', this.request).then(res => {
console.log(res.data);
});
}
}
</script>
ちなみに、既に登録済みのデータを初期値としてフォームに表示することも可能です。
<!-- profile.blade.php(Laravel) -->
<!-- usersテーブルから取得した$userをv-bindでコンポーネントに渡す-->
<prof-name v-bind:user="{{ $user }}"></prof-name>
<!-- resources/assets/js/components/ProfName.vue -->
<!-- templateはさっきと同じ -->
<template>
<div>
<input v-model="name" placeholder="ニックネーム" required>
<button v-on:click="editName">変更</button>
</div>
</template>
<script>
//propsでバインドされた$userのデータを受け取る
props:["user"]
data: function(){
return{
//nameにpropsで受け取ったuser.nameを格納
//これで入力フォームには初期値としてuser.nameの値が表示されます
name:this.user.name,
//postで送信するためのリクエストデータ初期化
request:{
name:''
}
}
},
methods: {
editName(){
//リクエストデータに入力値を代入
this.request.name = this.name;
//userのidを指定してpatchで送信(patchも出来るようです)
axios.patch('/users/' + this.user.id, this.request).then(res => {
console.log(res.data);
});
}
}
</script>
##6.v-on:clickに指定したメソッドに引数を渡す
template
内の要素にv-on:click
でクリックイベントが発生した際に実行するメソッドを指定できるが、そのメソッドに引数を渡す例です。
v-for
でitemリストを表示し、一つ一つのリストにクリックイベントを設定しています。
メソッドの括弧内に引数としてitem
を渡しています。
<div>
<p v-for="item in items">
<span v-on:click="addAct(item)">{{ item.name }}</span>
</p>
</div>
##7.axiosでdeleteメソッドを使う時にデータを送りたい
以下のように送信するデータをdataオブジェクトにして送る必要があるみたい。
axios.delete('/users' + item.id, {data:{item:'item'}}).then(res => {
console.log('成功');
});
##8.非同期で追加した要素を削除(見た目とレコード両方)
以下のようにv-forで表示したitemリストをクリックすると、そのリストのみ削除するdeleteItem
メソッドを実行するようにしています。
deleteItem
は非同期でクリックしたItemを削除します。
<div>
<p v-for="item in items">
<span v-on:click="deleteItem(item)">{{ item.name }}</span>
</p>
</div>
deleteItem
メソッドで非同期でDBからItemを削除すると同時に、表示されているアイテムも削除する例がこちらです。
javascriptには配列を値から削除する関数が無いらしいので、一旦indexOfでキーを取得した上で、キーを元に配列から要素を削除します。
deleteItem(item){
// 引数に渡したitemのitemsの中でのキーを取得しdelIdに格納
this.delId = this.items.indexOf(item);
// delIdをキーにitemsの配列から該当のitemを取り除く
this.items.splice(this.delId,1);
axios.delete('/users/' + item.id, {data:{idol:'idol'}}).then(res => {
console.log('成功');
});
},
##9.都道府県等のselectボックスをv-forで作る
template
に全てのoptionタグ直接書いても出来ますが、コンポーネント内のdataオブジェクトで定義した上でそれをv-forを使ってtemplateに表示するやり方をしました。
・テンプレート側
<select v-model="selected">
<option v-for="pref in prefs" v-bind:value="pref.value">{{ pref.text }}</option>
</select>
・dataオブジェクト
※selected
の初期値の設定をしないと初期状態で何も表示されないので注意
export default {
data:function(){
return {
//selectタグのv-modelに指定したselectedで初期値のvalueを指定する
//これにより最初に表示される値を指定できる
selected:1,
prefs:[
{text:'東京都',value:1},
{text:'福岡県',value:2},
{text:'長野県',value:3}
]
}
}
}
##10.LaravelからVue.jsにv-bindで配列を渡す
以下のようにして普通に渡そうとしたらエラーになります。
<!-- profile.blade.php -->
<prof> v-bind:arr="{{ $arr }}"></prof>
理由は、Laravelのbladeテンプレートでは{{}}
で囲んだ変数を自動でhtmlspecialchars()
でエスケープするのですが、この関数のエスケープ対象に配列を指定することが出来ないからです。
対策としては、bladeテンプレートに引数を渡す前段階で配列をJSON形式に変換します。
//UserControll.php
$arr = json_encode($arr);
return view('profile')->with('arr',$arr);
これで以下のようにしてもエラーにならなくなります。
<!-- profile.blade.php -->
<prof v-bind:arr="{{ $arr }}"></prof>
##10.LaravelからVue.jsに暗号化された値を渡す
LaravelでSNS認証を導入しており、登録の際にメールアドレスをencrypt
ヘルパで暗号化してからDBに登録しています。
そのメールアドレスをマイページ的な画面で表示したい場合
##11.props検証(オブジェクトor配列の場合)
オブジェクトor配列のpropsのデフォルトを設定する場合は、
ファクトリ関数でretrunするようにする。
export default {
props: {
value: {
type: Object,
default: () => {return new MyObject}
}
}
}
##12.兄弟コンポーネント間でのデータのやり取り
グローバルなイベントハブを使って行う
//空のVueインスタンスをeventHubとして定義
const eventHub = global.eventHub = new Vue()
//コンポネーント(送信側)のメソッド内でイベント+データ送信
methods: {
search () {
eventHub.$emit('search',{
'searchText': this.searchText
});
}
}
//コンポーネント(受信側)では.onでリスナ登録
//createdのタイミングでリスナを登録する
created () {
//'search'というイベント名で受け取る
eventHub.$on('search',(val) => {
//なんらかの処理
console.log(val.searchText)
})
}
##13.算出プロパティ
ユーザー入力値等の変数を用いて計算した結果を別の変数として使える。
computed
で定義する際は関数として定義して計算結果を返り値として返す。
computed
の関数で返した値はキャッシュされ、元の変数が変更された時のみ再計算される。
methods
はキャッシュされない
computed
には変数は渡せない
※filtersには変数は渡せる
※ただしfiltersではthisが使えない?
<template>
<div>
<input type="text" v-model="searchText" placeholder="アーティスト名">
<input type="submit" @click="search">
<p>{{searchText}}</p>
<p>{{fixSearchText}}</p>
</div>
</template>
<script>
export default {
data () {
return {
//ここでfixSearchTextを定義しないようにする。定義しちゃうと、computedの計算のところで'fixSearchText is already defined'って怒られる
searchText: '',
}
},
//ここで計算。複雑なロジックだとより便利さを感じられる。
computed: {
fixSearchText: function() {
return this.searchText + '_fix'
}
},
</script>
computedが変更を検知出来るデータはVueインスタンスのデータのみ。現在日時やDOMの状態等Vueインスタンス外のデータの変更は検知されない。
data: function () {
return {
message: 'hi',
}
},
computed: {
message: function () {
var timestamp = Date.now();
var changedMessage = `${this.message},${timestamp}`;
}
}
vm.message; // hi,1522545486691
// 数秒待って実行
vm.message; // hi,1522545486691 ※timestampはVueインスタンスのデータではないので変更は反映されない
ちなみに、アロー関数で書こうとするとundefined
になってしまうので注意。
//ダメなパツーン
computed: {
fixSearchText: () => {
return this.searchText + '_fix'
}
},
##13.算出プロパティと監視プロパティの違い
//computedは変更前から実行される
computed: {
fixSearchText: function() {
return 'computed:' + this.searchText
}
},
//watchは変更されてから実行される
watch: {
//searchTextの変更を監視
searchText: function(val) {
//searchTextが変更されたらwatchedSearchTextに値を入れる
//watchedSearchTextはdata()で定義しておく
this.watchedSearchText = 'watch:' + val
}
},
#14.Vuexの基本的な書き方
##Storeの定義
// main.js
import Vue from 'vue'
import store from './store'
new Vue({
el: '#app',
//storeをVueインスタンスのプロパティに設定することで紐づくコンポーネントからstoreを使うことが出来る
store,
})
//////////////
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
//状態を管理したいデータを定義
state: {
searchText:""
},
//状態変更の処理を記述
mutations: {
//第1引数はstate,第1引数は処理に使いたい値等を指定
searchTextStore(state,searchText){
state.searchText = searchText
}
},
//状態を取得したい時はgettersを通して取得できる
//(通さなくても取得できる)
getters:{
searchTextGetter: (state) => {
return state.searchText.slice(2,7)
}
}
})
export default store
##状態の変更、取得
// components/example.vue(Vueインスタンスに紐づいたコンポーネント)
<script>
export default {
// 状態の取得
computed: {
searchTextState: function() {
// this.$storeでstoreにアクセス出来る
return this.$store.getters.searchTextGetter
}
},
//状態の変更
methods: {
//searchメソッドを実行した時に状態変更
search (searchText) {
//commitの第1引数にmutationsで定義したミューテーション名を指定
//第2引数でミューテーションに渡す値を指定出来る(第2引数はオブジェクトにすべきらしい)
this.$store.commit('searchTextStore',this.searchText)
}
}
</script>
#15.propsに渡されたデータをtemplate、methods、updated等で使う
const vueComponent = {
props:["property"],
data: function() {
return {
property:'' // ←これはダメ。propsと同じ名前はエラーになる
}
}
template:`
<p>{{ property }}</p>
<p>{{ fixData(property) }}</p>
`,// template内ではthis等はつけず同じ名前の変数でアクセス出来る
// methodsで定義したメソッドの引数にpropertyを渡すことも出来る
methods: {
fixData: function (property) {
// 処理
return property;
}
},
updated: function() {
console.log(this.property);// this.○○○でpropsにアクセス可能
}
}
#16.dataを更新するときの注意点
・updatedの中でデータを更新(this.data = '新しいデータ')等すると無限ループに陥る?
→多分dataを更新するとVueライフサイクルでupdatedイベント発生して、updated内の処理が走るので、その中でdataを更新するといつまでも止まらず無限ループ発生、という流れだと思う。
17.グローバルスコープとローカルスコープでの定義の仕方
・グローバルスコープ・・全インスタンス共通で使える
・ローカルスコープ・・単一のインスタンス・コンポーネントの中のみ
(例)filterの場合
// グローバル
Vue.filter('filterName', function(value) {
/*フィルタ処理*/
})
// ローカル:コンポーネントのオプション内で定義
filters: {
filterName: function(value) {/*フィルタ処理*/}
}
#18.Componentの登録の仕方
components: {
ComponentA,
ComponentB
},
#19.監視プロパティの書き方(watch)
通常
watch: {
flag: function(val, oldVal){/* 処理 */}
}
監視対象がネストしている場合
watch: {
flag: {
// この「handler」の部分をネストした対象データの変数名かと勘違いして少しハマった
// この「handler」は固定
handler: function(val, oldVal){/* 処理 */},
deep: true // これがtrueで無いとネストされたデータの変更を検知出来ない
}
}
監視の開始直後にコールバックを実行したい場合
watch: {
flag: {
handler: function(val, oldVal){/* 処理 */},
immediate: true
}
}
immadiate設定したけど、設定していない時との違いが分からない。
「監視の開始直後」っていつだろう。
もう少し調査が必要。
#20. vm.$nextTickでDOM更新後にコールバック実行
例えばwatchで監視した値が変更された際にcssを変更したい場合、
同時にDOM要素の変更も走っていた場合に、
DOM要素の更新を待ってからcssを変更する、といった時に使える。
this.$nextTick(function() {
/* ここにcssの変更処理 */
});
21. 要素にstyleを指定する方法
<template>
<div :style="styleObject">なにかしら</div>
</template>
<script>
export default {
data () {
return {
styleObject: {
fontSize: '25px', // プロパティはキャメルケース
backgroundImage: '../assets/logo.png', // だと画像は反映されな
backgroundImage: 'https://example.com/image' // これでうまくいく
}
}
}
}
</script>
22.v-modelでチェックボックスのchecked有無を取得
基本
// checkedにはboolean値が入る
<template>
<label>
<input type="checkbox" v-model="checked">
</label>
<p>{{ checked }}</p>
</template>
<script>
export default {
data: function () {
checked: true,// checkedのデフォルト値
}
}
</script>
複数のチェックボックスをループで表示する時
checkedを配列で定義する。
<template>
<label v-for="item in items">
<input type="checkbox" v-model="checked[item.id]" @change="displayIsChecked(item.id)">
</label>
</template>
<script>
export default {
data: function () {
checked: [],// checkedのデフォルト値
},
methods: {
displayIsChecked: function (itemId) {
console.log(this.checked[itemId]);
}
}
}
</script>
23.複数classを1つの要素にバインド
配列構文で複数classをバインド出来る
オブジェクト構文もある
(参考)https://jp.vuejs.org/v2/guide/class-and-style.html
<button :class="[isActive(item), isImportant(item)]">重要</button>
export default {
methods: {
isActive: function (item) {
return item.isActive ? '-active' : '';
},
isImportant: function (item) {
return item.isImportant ? '-important' : '';
}
}
}
24. @click
等で実行するメソッド内でイベント発生元要素を取得
<input type="checkbox" name="checkbox" @change="changeChecked">
<input type="checkbox" name="checkbox" @change="changeSome(item, $event)">
<script>
export default {
methods: {
changeChecked: function (e) { // 引数にeventが入る
$(e.target).prop('checked'); // e.targetでイベント元の要素(input)を取得出来る
},
changeSome: function (item, event) { // 他に引数がある場合は$eventを引数にする
$(e.target).prop('checked');
}
}
}
</script>
25.vue-routerのrouter-linkでリンク先を指定
Routerを定義
// router.js
export default new Router({
mode: 'history',
routes: [
{
path: '/user/:userId, // :userIdはパラメータ this.$route.paramsで値を取得出来る
name: 'user', // ルートに名前をつけられる
component: user // ルートにアクセスした時に表示するコンポーネント
}
]
})
ルートをオブジェクトで指定
<template>
<!-- router.jsの定義に合わせてオブジェクトでrouteを指定出来る -->
<!-- paramsの値の方のuserIdはdataプロパティに指定したuserId -->
<router-link to="{ name: 'user', params: { userId: userId } }">Me</router-link>
</template>
<script>
export default {
data: function () {
return {
userId: 0, // 初期値
}
},
created: function () {
// axiosはmain.jsやbootstrap.jsで定義しておく
axios.get('/api/me').then(res => {
this.userId = res.data.id; // apiで取得したuserIdをdataのuserIdにセット
})
}
}
</script>
26.$mountメソッド使いどころ
マウント先の要素がUI操作や通信の後に遅延して生成される場合
通常
var vm = new Vue({
el: '#app',
// ・・・
});
#appの要素が遅延生成される場合
// Vueインスタンスの生成
var vm = new Vue({
// ・・・
});
// マウント先要素の生成・・・
// マウント先要素を取得
var el = document.getElementById('app');
// マウント
vm.$mount(el);
27. 配列要素の操作
dataで定義した配列そのものを上書きすると変更は検知されないが、push等で配列の中に要素を入れていく形等は変更検知される。
<script>
export default {
data: function() {
return {
// デフォルトで空配列を定義
themes: [],
}
},
methods: {
getThemes: function() {
// APIでThemeオブジェクトの配列を取得している
axios.get('/api/themes').then(response => {
response.data.forEach(theme => {
// jsの配列操作のメソッドが使える
this.themes.push(theme);
})
})
},
},
}
</script>
28. v-forとvm.$refを使ってリストの一部要素に対してのみ操作を行う
<!-- ユニークな値をkeyに設定しないと特定のリストへの操作が、他のリスト要素に影響することがある -->
<div v-for="(item, index) in list" :key="item.id">
<ul>
<li class="a" @click="changeContents($event, item.id)">{{item.text}}リンクA</li>
<li class="b" @click="changeContents($event, item.id)">{{item.text}}リンクB</li>
<li :ref="`a-${item.id}`">{{item.text}}A</li>
<li :ref="`b-${item.id}`">{{item.text}}B</li>
</ul>
</div>
<script>
export default {
data () => {
list: [
{ id: 1, text: 'テキスト1'},
{ id: 2, text: 'テキスト2'},
{ id: 3, text: 'テキスト3'},
{ id: 4, text: 'テキスト4'},
]
},
methods: {
changeContents: (e, key) => {
let isA = e.classList.includes('a');
let refKeyA = `a-${key}`;
// refの名前を変数で指定する場合はthis.$ref[変数名]のように指定出来る
let a = this.$ref[refKeyA];
let refKeyB = `b-${key}`;
let b = this.$ref[refKeyB];
// クリックしたリンクに紐づくコンテンツを表示
if (isA) {
b[0].classList.remove('-show');
a[0].classList.add('-show');
} else {
a[0].classList.remove('-show');
b[0].classList.add('-show');
}
}
}
}
</script>
[番外編]オブジェクトにforEachを使う
// 各要素の前後の空白を除去して配列に格納するコード
const obj = {'a ', ' b', 'c '};
const newArray = [];
Object.keys(obj).forEach(function(key){ // Object.keys(obj)でobjのキーの配列を取得出来る
newArray.push(this[key].trim());
}, obj);// forEachの第2引数にobjを渡すとコールバック内でthisとして使える