概述
在开发中发现发现有好多关于搜索的表单的业务,所以想封装一个搜索的组件,只用通过声明式的配置就可以自动生成我想要的搜索表单。样子就是下图这个样子:
以上组件封装基于Ant Design of Vue
组件库,原理都一样,可根据自己需求更改。因为在给form对象动态添加属性值不是响应式的,导致表单无法双向绑定form对象的值,form对象的值能改变,但是视图不会改变。这个bug尤其体现在select选择器上,所以我给select选择器绑定了change事件,通过监听change事件去修改绑定的form的值。 最初的bug代码
:
<template>
<div :class="advanced ? 'search' : null">
<a-form-model ref="queryForm" :model="form" layout="horizontal">
<div :class="advanced ? null : 'fold'">
<template v-for="(item, index) in config">
<template v-if="index == 0">
<a-row :key="index">
<template v-for="itemChild in item">
<a-col :md="8" :sm="24" :key="itemChild.name">
<a-form-model-item
:label="itemChild.label"
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 18, offset: 1 }"
>
<a-input
:placeholder="itemChild.placeholder"
v-model="form[itemChild.bindVal]"
v-if="itemChild.type == 'input'"
/>
<a-select
allowClear
v-if="itemChild.type == 'select'"
@change="selectChange($event, itemChild.bindVal)"
:placeholder="itemChild.placeholder"
>
<a-select-option
v-for="option in itemChild.option"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
</template>
</a-row>
</template>
<template v-else>
<a-row :key="index" v-if="advanced">
<template v-for="itemChild in item">
<a-col :md="8" :sm="24" :key="itemChild.name">
<a-form-model-item
:label="itemChild.label"
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 18, offset: 1 }"
>
<a-input
:placeholder="itemChild.placeholder"
v-model="form[itemChild.bindVal]"
v-if="itemChild.type == 'input'"
/>
<a-select
allowClear
v-if="itemChild.type == 'select'"
@change="selectChange($event, itemChild.bindVal)"
:placeholder="itemChild.placeholder"
>
<a-select-option
v-for="option in itemChild.option"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
</template>
</a-row>
</template>
</template>
</div>
<span style="float: right; margin-top: 3px">
<a-button type="primary" @click="query">{{ queryBtnText }}</a-button>
<a-button v-if="hasResetBtn" style="margin-left: 8px" @click="reset"
>重置</a-button
>
<a @click="toggleAdvanced" style="margin-left: 8px">
{{ advanced ? "收起" : "展开" }}
<a-icon :type="advanced ? 'up' : 'down'" />
</a>
</span>
</a-form-model>
</div>
</template>
<script>
export default {
nmae: "queryForm",
props: {
queryBtnText: {
type: String,
default: "查询",
},
hasResetBtn: {
type: Boolean,
default: true,
},
config: {
type: Array,
},
},
data() {
return {
// 控制展开收起
advanced: false,
form: {},
};
},
beforeCreate() {
this.$nextTick(() => {});
},
created() {
this.config.flat(Infinity).forEach((item) => {
this.form[item.bindVal] = '';
});
},
mounted() {},
methods: {
query() {
console.log(this.form);
},
reset() {
for (let key in this.form) {
this.form[key] = "";
}
// 强制刷新组件,因为form对象不是响应式的
this.$forceUpdate();
},
// 展开收缩
toggleAdvanced() {
this.advanced = !this.advanced;
},
// 监听选择器change事件
selectChange(val, key) {
this.form[key] = val;
},
},
};
</script>
<style lang="less" scoped>
.search {
margin-bottom: 54px;
}
.fold {
width: calc(100% - 216px);
display: inline-block;
}
@media screen and (max-width: 900px) {
.fold {
width: 100%;
}
}
</style>
找到原因后,修改正确的代码:
<template>
<div :class="advanced ? 'search' : null">
<a-form-model ref="queryForm" :model="form" layout="horizontal">
<div :class="advanced ? null : 'fold'">
<template v-for="(item, index) in config">
<template v-if="index == 0">
<a-row :key="index">
<template v-for="itemChild in item">
<a-col :md="8" :sm="24" :key="itemChild.name">
<a-form-model-item
:label="itemChild.label"
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 18, offset: 1 }"
>
<a-input
:placeholder="itemChild.placeholder"
v-model="form[itemChild.bindVal]"
v-if="itemChild.type == 'input'"
/>
<a-select
allowClear
v-if="itemChild.type == 'select'"
v-model="form[itemChild.bindVal]"
:placeholder="itemChild.placeholder"
>
<a-select-option
v-for="option in itemChild.option"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
</template>
</a-row>
</template>
<template v-else>
<a-row :key="index" v-if="advanced">
<template v-for="itemChild in item">
<a-col :md="8" :sm="24" :key="itemChild.name">
<a-form-model-item
:label="itemChild.label"
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 18, offset: 1 }"
>
<a-input
:placeholder="itemChild.placeholder"
v-model="form[itemChild.bindVal]"
v-if="itemChild.type == 'input'"
/>
<a-select
allowClear
v-if="itemChild.type == 'select'"
v-model="form[itemChild.bindVal]"
:placeholder="itemChild.placeholder"
>
<a-select-option
v-for="option in itemChild.option"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
</template>
</a-row>
</template>
</template>
</div>
<span style="float: right; margin-top: 3px">
<a-button type="primary" @click="query">{{ queryBtnText }}</a-button>
<a-button v-if="hasResetBtn" style="margin-left: 8px" @click="reset"
>重置</a-button
>
<a @click="toggleAdvanced" style="margin-left: 8px">
{{ advanced ? "收起" : "展开" }}
<a-icon :type="advanced ? 'up' : 'down'" />
</a>
</span>
</a-form-model>
</div>
</template>
<script>
export default {
nmae: "queryForm",
props: {
queryBtnText: {
type: String,
default: "查询",
},
hasResetBtn: {
type: Boolean,
default: true,
},
config: {
type: Array,
},
},
data() {
return {
// 控制展开收起
advanced: false,
form: {},
};
},
beforeCreate() {
this.$nextTick(() => {});
},
created() {
this.config.flat(Infinity).forEach((item) => {
// 给对象新增响应式属性
this.$set(this.form, item.bindVal, undefined);
});
},
mounted() {},
methods: {
query() {
console.log(this.form);
},
reset() {
for (let key in this.form) {
this.form[key] = undefined;
}
// 强制刷新组件,因为form对象不是响应式的
// this.$forceUpdate();
},
// 展开收缩
toggleAdvanced() {
this.advanced = !this.advanced;
},
// 监听选择器change事件
selectChange(val, key) {
this.form[key] = val;
},
},
};
</script>
<style lang="less" scoped>
.search {
margin-bottom: 54px;
}
.fold {
width: calc(100% - 216px);
display: inline-block;
}
@media screen and (max-width: 900px) {
.fold {
width: 100%;
}
}
</style>
在父组件中通过声明式的去使用封装好的组件:
<template>
<div class="userPage">
<a-row :gutter="24">
<!-- 左侧树 -->
<a-col :span="4">
<a-card>
<a-directory-tree
multiple
:show-line="false"
defaultExpandAll
@select="onSelect"
@expand="onExpand"
>
<a-tree-node
v-for="item in treeData"
:key="item.id"
:title="item.name"
>
<a-tree-node
v-for="child in item.subs"
:key="child.id"
:title="child.name"
is-leaf
/>
</a-tree-node>
</a-directory-tree>
</a-card>
</a-col>
<!-- 右侧表格 -->
<a-col :span="20">
<a-card>
<!-- 搜索部分 -->
<queryForm :config="queryFormConfig" />
<!-- 表格部分 -->
<queryTable />
</a-card>
</a-col>
</a-row>
</div>
</template>
<script>
import { getList } from "@/services/jurisdiction";
import queryTable from "@/components/myComponents/queryTable";
import queryForm from "@/components/myComponents/queryForm";
export default {
components: { queryTable, queryForm },
data() {
return {
treeData: [],
queryFormConfig: [
[
{
label: "部门",
type: "select",
placeholder: "请选择部门",
bindVal: "department",
option: [
{ value: "0", label: "经济部" },
{ value: "1", label: "市场部" },
{ value: "2", label: "销售部" },
],
},
{
label: "姓名",
type: "input",
placeholder: "请输入姓名",
bindVal: "name",
},
{
label: "电话",
type: "input",
placeholder: "请输入电话",
bindVal: "tel",
},
],
[
{
label: "性别",
type: "input",
placeholder: "请输入姓名",
bindVal: "sex",
},
{
label: "年龄",
type: "input",
placeholder: "请输入姓名",
bindVal: "age",
},
{
label: "爱好",
type: "input",
placeholder: "请输入姓名",
bindVal: "hobby",
},
],
],
};
},
created() {
this.getTreeList();
},
methods: {
async getTreeList() {
const { data } = await getList();
this.treeData = data;
},
onSelect(keys, event) {
console.log("Trigger Select", keys, event);
},
onExpand() {
console.log("Trigger Expand");
},
},
};
</script>
<style lang="less" scoped>
</style>
文章最后:更多精彩文章,欢迎参观我的公众号:小笑残虹。
最后
以上就是眼睛大秋天为你收集整理的封装组件引发的问题——vue给对象添加响应式属性的全部内容,希望文章能够帮你解决封装组件引发的问题——vue给对象添加响应式属性所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复