实现一个异步带Loading的控件组件

问题来源
例子
如图所示,在一个列表中,有一个开关,点击之后设置某条数据的状态,但由于开启和关闭某个状态是需要发请求的,是一个一个异步的问题,因此在点击开关之后,应如何先去请求等待成功/失败之后让开关展示相应的状态呢?(已知el-switch没有相关配置项)
分析
1、首先在组件里需要需要加一个loading的效果
2、关于loading的状态设置应该在组件内部还是组件外部?因为在列表中引入此组件,如果要在组件内部维护状态,那么则需要传入数据的某个属性作为控制的判断条件,因此最好的还是在外部维护。
3、如何去在外部维护状态,内部生效?方法1:传入函数,内部调用 方法2:通过自定义事件实现
实现过程
1、关于实现loading效果
实现的方法有很多,这里使用vuetify的loading组件v-progress-circular,实现思路为:在开关上罩一层div,点击之后先执行div的click事件,阻止开关的变化,实现代码如下:实现一个异步带Loading的控件组件_第1张图片
实现效果如下:loading实现效果图
2、关于实现loading状态的控制
(1)传入函数
父组件:父组件实现代码

<ZdySwitch v-model="item.activeStatus" class="mr-3" :async-func="changeOpenStatus" :item="item"></ZdySwitch>
async changeOpenStatus(item) {
            const changeStatus = item.activeStatus ? '0' : '1';
            const flag = await this.editWarnScenes([item.id], changeStatus);
            if (flag) {
                item.activeStatus = !item.activeStatus;
            }
        },

:此处的editWarnScenes函数即是需要进行异步处理的函数,并返回异步处理是否成功
子组件接受一个函数asyncFunc,并在click事件中执行该函数

async change() {
            this.loading = true;
            await this.asyncFunc(this.item);
            this.loading = false;
}

经过试验,这样处理是有问题的,问题在哪里呢?注意我们传入子组件的是个函数,但是这样处理之后,我们传入的是什么呢?答案就是promise,因此就会报错。。。(此方案暂未找到怎么解决,欢迎大佬给与建议)
(2)现在介绍第二种已经实现了的方案,采用自定义事件方式。
父组件实现:在这里插入图片描述

<ZdySwitch v-model="item.activeStatus" class="mr-3" @async-func="changeOpenStatus(item,$event)"></ZdySwitch>
async changeOpenStatus(item, { callback }) {
            const changeStatus = item.activeStatus ? '0' : '1';
            await this.editWarnScenes([item.id], changeStatus);
            callback();
        },

子组件实现:

async change() {
            this.loading = true;
            this.$emit('async-func', {
                callback: () => {
                    this.loading = false;
                }
            });
        }

我们在点击的时候,向外面emit出去,并且传出一个回调函数,在回调函数中关闭loading。在组件外部,我们等到异步请求的结果之后,执行接收到的回调,此时就能正确的实现效果了。
:在父组件中我们接收到子组件的emit执行对应的事件时,由于需要将当前项数据传入函数中取值处理,因此我们将第二个参数传入了$event,这里作为一个占位的参数。

你可能感兴趣的:(javascript,vue,前端)