我是靠谱客的博主 眼睛大秋天,这篇文章主要介绍封装组件引发的问题——vue给对象添加响应式属性,现在分享给大家,希望可以做个参考。

在开发中发现发现有好多关于搜索的表单的业务,所以想封装一个搜索的组件,只用通过声明式的配置就可以自动生成我想要的搜索表单。样子就是下图这个样子:
基于ant-vue组件库封装
以上组件封装基于Ant Design of Vue组件库,原理都一样,可根据自己需求更改。因为在给form对象动态添加属性值不是响应式的,导致表单无法双向绑定form对象的值,form对象的值能改变,但是视图不会改变。这个bug尤其体现在select选择器上,所以我给select选择器绑定了change事件,通过监听change事件去修改绑定的form的值。 最初的bug代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<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>

找到原因后,修改正确的代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<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>

在父组件中通过声明式的去使用封装好的组件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<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给对象添加响应式属性的全部内容,更多相关封装组件引发内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部