Laravel + Vue 制作一款标签选择器
前言
最近在撸一款个人的博客,虽然是第一次使用 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 协议》,转载必须注明作者和本文链接
推荐文章: