【千字长文,熬夜更新,原创不易,多多支持,感谢大家】
前言
hello,小伙伴们大家好。昨天我们对最初的投票功能做了一个简单的扩展:实现了一个简单的问卷调查功能。在实现该问卷功能时,我们分别封装了几个不同类型的组件:投票组件,单选组件,多选组件,输入组件和评分组件。不知有没有小伙伴发现,虽然问卷的大部分功能都实现了,问卷主题可自由配置,用户也可以进行点选输入填写问卷,但最后问卷填写完成后发现点击提交按钮并没有反应,那岂不是白操作了一顿?没错该问卷还少了个提交功能,接下来我们就把问卷的提交功能补齐,同时对App.vue组件进行一个简单的优化。
给自定义问卷组件添加change事件
在我们封装的问卷组件中不管是投票组件,多选组件,单选组件还是输入组件,最终选择的结果都是保存在一个响应式属性selectValue上,这个属性只能在组件内部使用,外部是无法访问到的,而我们的问卷最终是在父组件中通过提交按钮来提交问卷结果的,也就是说我们需要在父组件中来获取各个子组件已选择的结果,那么就需要用到子组件向父组件传值了,在本次分享中我们选择通过自定义事件的形式向父组件传值。除了投票组件,其它几个组件的代码基本上都是一样的,所以就在这里进行统一说明了,具体实现步骤如下:
- 在setup函数的第二个参数context中解构出emit方法
- 定义一个方法valChange,接收一个参数val,并在这个方法中调用emit方法,同时传递两个参数:【自定义事件“select”】和【已选结果值“val”】。注意这里我们给所有的自定义组件(包括投票组件)统一指定自定义事件select
- 通过return暴露valChange方法给模板
- 最后修改模板给组件添加change事件
下面以单选组件myradio为例,展示一下修改后的代码,其它几个组件(投票组件除外)与之相同,不再赘述
<template>
<el-radio-group v-model="selectedValue" @change="valChange">
</el-radio-group>
</template>
setup(props, context){
const {emit} = context;
const valChange = () => {
emit('select', val);
}
}
投票组件vote.vue改造
之所以把投票组件单独改造是因为在投票组件相对来说有点复杂,并且投票组件中的的元素除了按钮el-button和卡片el-card都是原生的html元素,这些元素都没有change事件,所以我们应该在按钮的点击事件中调用emit来自定义事件select,同时将投票结果(支持人数,反对人数和支持率)返回给主问卷页面
- 在setup方法的第二个参数context中解构出emit方法
- 定义一个result变量用于接收投票结果
- 定义一个方法calcResult,在该方法中以固定字符串模板将支持人数,反对人数和支持率拼接成字符串并赋值给变量result
- 在calcResult 中调用emit方法并传递“select”和result作为参数
- 最后在原有的support和oppose中调用calcResult方法
setup(props, context){
const { emit } = context;
let result = "";
const calcResult = () => {
result = `支持人数:${supCount.value}/${supCount.value+oppCount.value};反对人数:${oppCount.value}/${supCount.value + oppCount.value};支持率:${supRate.value}%`
emit('select', result);
}
const support = () => {
supCount.value++;
calcResult();
};
const oppose = () => {
oppCount.value++;
calcResult();
};
}
封装Home组件
在前面的案例中,我们把所有的业务逻辑都写在了App.vue里,但是作为入口组件我们应该尽可能少的把业务逻辑都写在App.vue中,因此从这次的分享开始,我们就把业务逻辑相关的代码从App.vue中提取出来单独放在Home.vue的组件中。同时由于这个组件中的响应式变量越来越多,因此我们也把ref换成reactive进行响应式变量的定义。
- 新增一个Home.vue的组件
- 将原来App.vue中的代码剪切到Home.vue中(注意代码切换后,导入自定义组件路径的变化)
- 将原来用ref声明的响应式变量统一改成用reactive来声明(注意在return的时候需要先调用toRefs方法然后再进行结构,以免破坏响应式)
- 声明一个普通变量result默认值为空对象,用于保存已选中的问卷结果
- 定义一个submit方法,用于点击提交按钮时调用,在该方法中遍历result对象循环输出问卷主题及结果
- 定义一个getValue方法,接收3个参数,并将问卷主题和结果以字符串的形式添加到对象result中,3个参数:
- val:已选结果,用于接收子组件传回来的已选结果值
- title:问卷主题,用于展示当前所选结果是属于哪个问卷主题的
- flag:问卷标识,由于每类问卷主题的个数都是不固定的,我们自定义的组件又都是通过v-for指令循环生成的,并且每个组件都会调用该方法,因此我们需要方法被调用时对每个问卷主题做一个唯一标识便于区分
- 将上面的响应式变量及两个方法通过return暴露出去
- 最后在为模板中的每个自定义组件添加一个select事件(注:该事件由子组件定义返回),在该事件中调用上面的getValue方法并传递3个实参
改造后的代码如下:
<template>
<div class="main">
<vote @select="getValue($event, item.title, `vote${item.id}`)"></vote>
<myradio @select="getValue($event, radio.title, `radio${radio.id}`)"></myradio >
<mycheck @select="getValue($event, check.title, `check${check.id}`)"></mycheck>
<myinput @select="getValue($event, inp.title, `input${inp.id}`)"></myinput>
<mystar @select="getValue($event, star.title, `star${star.id}`)"></mystar>
</div>
</template>
import { reactive, toRefs } from 'vue'
export default {
setup(){
const state = reactive({
data:[],
radios:[],
checks:[],
inputs:[],
stars:[]
})
http.get('/data.json').then(res=>{
state.data = res.data;
state.radios = res.radios;
state.checks = res.checks;
state.inputs = res.inputs;
state.stars = res.stars;
});
const result = {}
const submit = () => {
for(const key in result){
console.log(result[key])
}
}
const getValue = (val, title, flag) => {
result[flag] = `${title} >>> ${val}`
}
return {
...toRefs(state),
submit,
getValue
}
}
}
效果展示
总结
本次分享我们基于前面的案例继续扩展,实现了问卷的提交功能,其中涉及到了子组件向父组件传值,子组件中自定义事件以及基于reactive去定义响应式属性等,最终实现点击提交按钮后将问卷结果输出到控制台中。 本次分享就到这里了,喜欢的小伙伴欢迎点赞评论加关注哦!
|