前言
最近在擼一款個人的部落格,雖然是第一次使用 Laravel 寫專案,但心中的原則不能背棄——就是要擼一款能愉快寫作,賞心悅目閱讀的部落格應用。所以,一款能多選,又能同時建立標籤又能隨時刪除標籤的選擇器當然少不了。話不多少,直接開始吧。
選一個輪子
作為非專業前端,當然是直接拿起輪子就開幹,jQuery 的輪子是不少,但 Vue 實在太好用了,所以這裡就選基於 Vue 的 Vue-multiselect。
安裝
本文假設你已經安裝好 Laravel 的 Vue腳手架。安裝 Vue-multiselect:
npm install vue-multiselect --save
封裝成自己需要的 Vue 元件
新建一個檔案:resources/js/component/MultiSelectComponent.vue,寫入如下程式碼:
<template>
<div>
<input type="hidden" :name="fieldName" :value="myTagIds">
<multiselect
v-model="value"
tag-placeholder="新增為新標籤"
placeholder="請新增文章標籤(選擇或者直接輸入)"
select-label="按 Enter 選擇"
selected-label="已選"
label="name"
track-by="id"
:options="options"
:multiple="true"
:taggable="true"
:hide-selected="true"
@tag="addTag"
></multiselect>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
export default {
components: { Multiselect },
props: {
fieldName: {
type: String,
required: true
},
tagIds: {
type: Array,
default: () => [],
},
tags: {
type: Array,
required: true,
},
},
data() {
return {
value: [],
options: this.tags,
myTagIds: this.tagIds
}
},
methods: {
addTag(newTag) {
const tag = {
name: newTag,
id: newTag + '~' + Math.random().toString(36).substring(2)
}
this.options.push(tag);
this.value.push(tag);
}
},
mounted() {
if (this.myTagIds && this.options.length > 0) {
this.options.map((item) => {
if (this.myTagIds.includes(item.id)) {
this.value.push(item);
}
})
}
},
watch: {
value(n, o) {
let tagIds = [];
n.map((item) => {
tagIds.push(item.id);
});
this.myTagIds = tagIds;
}
}
}
</script>
幾點說明:
<input type="hidden" :name="fieldName" :value="myTagIds">
這裡新增一個表單欄位,可傳入欄位名,myTagIds
為表單需要的值;v-model="value"
,value
為選中的標籤物件;tag-placeholder
,placeholder
,select-label
等為一些提示,原來是英文的這裡替換為中文,更多屬性值得設定可以參考該擴充套件的文件;track-by
需是一個唯一值,這裡使用標籤的id;:options="options"
的options
為已有的標籤資料,後面說明如何從 laravel 模板中傳過來;multiple
為true
表示支援多選,taggable
為true
表示可以直接輸入建立標籤,而@tag
是輸入標籤名建立標籤時觸發的事件,後面實現該事件(addTag
方法);props
屬性接收的值分別為:表單欄位名、已選的標籤名和標籤資料;data
中,myTagIds: this.tagIds
,將外部傳進來的資料傳給myTagIds
,後面可以直接操作改變myTagIds
。(直接在元件裡面改變外部傳入的資料(tagIds
)Vue會報出警告);addTag
方法中,給新增的標籤生成一個唯一的id,且用一個特殊符號連線,這個是計劃myTagsId
(所有選中的標籤ID,包括新建立的)傳到後端後,對於這些帶特殊符號的ID值分割出標籤名(也就是新建的標籤);mounted()
方法,載入的時候,顯示出文章已有的標籤;watch
中value
方法,可用於監聽value
的變動,第一個引數為新的值,第二個為舊的值。這裡把新的value
值迴圈取出id
值儲存到myTagIds
。
元件的使用
註冊元件
封裝好元件,就可以到處使用了。首先,是要引入元件:
修改/resources/js/app.js
.
.
.
window.Vue = require('vue');
.
.
.
// Vue.component('example-component', require('./components/ExampleComponent.vue').default);
// ** 註冊元件,新增這一行 **
Vue.component('multi-select-component', require('./components/MultiSelectComponent.vue').default);
.
.
.
// ** 這一段也註釋掉,我們將在具體的頁面例項化Vue例項 **
/*const app = new Vue({
el: '#app',
});*/
程式碼修改注意看上面的註釋說明。
Laravel 準備資料
接著,要準備好元件需要的資料,本例項的場景是文章和標籤多對多的關聯模型。在控制器中,大概是這樣輸出資料:
public function create(Post $post)
{
$tags = Tag::all(['id', 'name'])->toArray();
return view('posts.create_and_edit', compact('post', 'tags'));
}
另外,前端頁面還需要一個文章所有標籤的id資料,所以,在文章模型中新增一個獲取器:
(假定多對多關聯已經定義好)
public function getTagIdsAttribute()
{
return $this->tags()->allRelatedIds();
}
這樣,透過文章的例項就可以獲取到所有的標籤id,像這樣:$post->tag_ids
。
Blade 模板中使用元件並繫結資料
<div class="form-group">
<multi-select-component
field-name = "tag_ids"
:tags = "tags"
@if(old('tag_ids', $post->tag_ids))
:tag-ids="{{ old('tag_ids', $post->tag_ids) }}"
@endif
></multi-select-component>
</div>
在Blade模板末尾的JS部分,這樣寫:
@section('script')
<script>
const app = new Vue({
el: "#app",
data: {
tags: {!! json_encode($tags) !!},
}
})
</script>
@endsection
以上程式碼的幾點說明:
@if(old('tag_ids', $post->tag_ids))
判斷,如果tag_ids
沒有任何值,就不要傳值了,這樣讓元件預設顯示「請選擇標籤」提示;tags: {!! json_encode($tags) !!},
,這使用{!! json_encode($tags) !!}
給元件傳值。當然,還有另一種方法,是使用@json
指令,在上面的元件標籤裡面,使用:tags = @json($tags)
也是可以的。
成果驗收
寫完收工,測試一下結果。
元件中的資料結構:
最後,限於個人水平有限,有任何不足之處,敬請指出。
本作品採用《CC 協議》,轉載必須註明作者和本文連結