通过过滤器对 Vue 组件中的模型属性值进行格式化
一、过滤器简介
在 Vue 组件中,我们可以通过过滤器对模型属性值进行格式化,注意这里不要望文生义哈,过滤器不是传统的对列表数据按条件进行「过滤」,而是对某个具体属性值进行「过滤」。
我们以上篇教程编写的文章列表组件为例进行演示。
Vue 组件支持全局过滤器和本地过滤器,我们先以文章发布日期格式化为例演示全局过滤器的定义和使用,再以文章状态格式化为例演示本地过滤器的定义和使用。
二、准备工作
文章列表接口数据调整
开始之前,我们先在后台返回的文章列表接口数据中新增 created_at
和 status
两个字段,分别表示文章发布时间和状态:
Route::get('/get_posts', function () {
return [
[
'id' => 1,
'url' => url('/post/1'),
'title' => '测试文章1',
'created_at' => '2019-10-15 17:51:32',
'status' => 0
],
[
'id' => 2,
'url' => url('/post/2'),
'title' => '测试文章2',
'created_at' => '2020-01-21 15:53:06',
'status' => 1
],
[
'id' => 3,
'url' => url('/post/3'),
'title' => '测试文章3',
'created_at' => '2020-09-07 10:31:06',
'status' => 0,
],
[
'id' => 4,
'url' => url('/post/4'),
'title' => '测试文章4',
'created_at' => '2020-05-17 01:29:24',
'status' => 1
],
[
'id' => 5,
'url' => url('/post/5'),
'title' => '测试文章5',
'created_at' => '2020-10-20 15:26:43',
'status' => 1
]
];
});
props 属性传递字段调整
我们将以卡片视图为例进行演示,因为这里可展示字段的空间更大一些。为了避免每次从 PostList
父级作用域传递多个 props 属性到 CardItem
,我们将之前逐个传递字段属性的方式调整为传递整个 post
对象:
<CardItem v-for="post in posts" :key="post.id" :post="post">
<template #title>{{ post.title }}</template>
<template #action-label>点击查看</template>
</CardItem>
三、通过全局过滤器定义日期过滤器
接下来,我们先来演示全局日期过滤器的定义和使用。
显示文章发布日期字段
修改 CardItem
组件代码如下以便展示日期字段:
<template>
<div class="col mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title"><slot name="title"></slot></h5>
<p class="card-text">
<small class="text-muted">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-clock" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14zm8-7A8 8 0 1 1 0 8a8 8 0 0 1 16 0z"/>
<path fill-rule="evenodd" d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5z"/>
</svg>
{{ post.created_at }}
</small>
</p>
<a :href="post.url" class="btn btn-primary"><slot name="action-label"></slot></a>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['post']
}
</script>
在未使用日期过滤器之前,渲染效果如下:
接下来,我们使用过滤器对日期格式进行格式化。
引入 Moment 包
在此之前,先通过 NPM 安装一个 JavaScript 中功能与 PHP Carbon 包类似的 Moment 包,我们可以使用它提供的 API 对指定日期时间进行格式化:
npm install moment --save
然后在 component-practice/resources/js/app.js
中引入它:
window.moment = require('moment');
日期过滤器的定义和使用
接下来,就可以基于 Moment 提供的 API 在 Vue 实例初始化之前定义一个全局的日期过滤器了,我们将这个过滤器的名称命名为 diff_for_human
:
Vue.filter('diff_for_human', datetime => {
return moment(datetime).fromNow();
});
const app = new Vue({
el: '#app'
});
非常简单。这样一来,就可以在 CardItem
组件中应用这个过滤器对日期属性进行格式化了。和 Linux 管道符一致,我们可以在模型属性值上通过 |
连接要使用的过滤器,然后模型属性值会作为第一个参数传递给后面的过滤器函数,最后将函数返回结果作为整个表达式的输出值:
{{ post.created_at | diff_for_human }}
刷新列表视图,切换到卡片视图,可以看到应用日期过滤器后的格式化效果:
五、通过本地过滤器定义状态过滤器
除了全局定义过滤器外,还可以在组件内部定义本地过滤器。
我们以文章状态字段为例进行演示,首先在 CardItem
组件中渲染状态字段:
<style scoped>
.card-text {
vertical-align: middle;
}
.card-text small {
margin-right: 5px;
}
</style>
<template>
<div class="col mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title"><slot name="title"></slot></h5>
<p class="card-text">
<small class="text-muted">
<svg v-show="post.status === 1" width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-file-check" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M4 0h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H4z"/>
<path fill-rule="evenodd" d="M10.854 6.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 8.793l2.646-2.647a.5.5 0 0 1 .708 0z"/>
</svg>
<svg v-show="post.status === 0" width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-file-x" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M4 0h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H4z"/>
<path fill-rule="evenodd" d="M6.146 6.146a.5.5 0 0 1 .708 0L8 7.293l1.146-1.147a.5.5 0 1 1 .708.708L8.707 8l1.147 1.146a.5.5 0 0 1-.708.708L8 8.707 6.854 9.854a.5.5 0 0 1-.708-.708L7.293 8 6.146 6.854a.5.5 0 0 1 0-.708z"/>
</svg>
{{ post.status === 0 ? '草稿' : '已发布' }}
</small>
<small class="text-muted">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-clock" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14zm8-7A8 8 0 1 1 0 8a8 8 0 0 1 16 0z"/>
<path fill-rule="evenodd" d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5z"/>
</svg>
{{ post.created_at | diff_for_human }}
</small>
</p>
<a :href="post.url" class="btn btn-primary"><slot name="action-label"></slot></a>
</div>
</div>
</div>
</template>
可以看到,在未使用过滤器之前,我们使用了三元运算符对状态显示文本进行优化,但是这只能应付比较简单的场景,如果文章状态值多于 3 个,代码的可读性就会越来越差,容易出错,如果涉及到一些更复杂的业务逻辑判断,则只能使用更好地方式来处理,比如过滤器。
我们在 CardItem
组件中定义一个本地过滤器 status_human_readable
,用于根据不同的文章状态值显示对应的可读性更好的文本,然后将其应用到 post.status
属性值上面:
...
{{ post.status | status_human_readable }}
...
<script>
export default {
props: ['post'],
filters: {
status_human_readable(status) {
switch(status) {
case 0:
return '草稿';
case 1:
return '已发布';
default:
return '未知状态';
}
}
}
}
</script>
可以看到,本地过滤器和全局过滤器只是定义方式不同而已,使用方式完全一致。
刷新列表视图,切换到卡片视图,可以看到如下渲染效果:
关于 Vue 过滤器学院君就简单介绍到这里,更多关于 Vue 过滤器的功能和使用(比如多个过滤器的串联、额外传递过滤器参数等),请参考官方文档。
本系列教程首发在Laravel学院(laravelacademy.org),你可以点击页面左下角阅读原文链接查看最新更新的教程。