数据变动与渲染问题

1.需求

  1. 实现一个动态表单,要求传入一个数组,自动生成表单内容
  2. 为了简单,用多组按钮来演示
  3. 一个按钮一个数据单元,包括按钮名称,按钮值,和按钮样式

2.实现效果

数据变动与渲染问题

实现代码

son.vue 文件

<template>
    <view>
        <block v-for="(v,i) in selects" :key="i">
            <view class="cu-form-group flex-left">
                <view class="title">{{v.label}}</view>
                <view class="grid col-3 padding-sm">
                    <button v-for="(radio,i2) in v.options" :key="i2" class="cu-btn margin-right orange  block" @click="onRadioChange" :class="radio.value == propMap[v.name].selected?'bg-orange':'line-orange'" :data-name="v.name" :data-value="radio.value">
                        {{radio.label}}
                    </button>
                </view>
            </view>
        </block>
    </view>
</template>
<script lang="ts" src="./son.ts"></script>

son.ts文件

import Vue from 'vue';
import { Component } from 'vue-property-decorator';

@Component({
    props: ['selects']
})
export default class SonComponent extends Vue {

    //如果没有这个map而是直接修改props的值,即使后续数据变动,页面将不会渲染。
    propMap: any = {}

    //要在挂载前将map的设置出来,如果页面拿到的某个值是undefined,即使后续数据变动,页面也不会渲染
    created() {
        //先将父组件传入的数组变成属性
        this.$props.selects.forEach((v: any) => {
            this.$set(this.propMap, v.name, v)
        })
    }

    //当radio值改变的时候触发
    onRadioChange(e: any) {
        //获取当前点击的值
        let value = e.currentTarget.dataset.value
        //获取当前点击的名称
        let name = e.currentTarget.dataset.name

        console.log("当前点击的", name, value)

        this.$props.selects.forEach((v: any, k: any) => {
            if (v.name == name) {
                v.selected = value

                //提交给vue,让他去渲染
                this.$set(this.propMap, v.name, v)

                return
            }
        })
    }
}

如代码中所示

  1. 如果改变父组件传入的props对象的值是不会体现到页面上的
  2. 要页面能监听到数据的变动,要给组件设置新的变量,并且这个变量在组件渲染前已经生成,绝对不能是undefined
  3. 改变数据的时候要用this.$set,而不是直接修改数据对象本身,否则页面不会重新渲染

详细介绍请看vue文档

文档指出,由于 JavaScript 的限制,Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength
  3. Vue 不能检测对象属性的添加或删除:

这个时候需要

Vue.set(vm.items, indexOfItem, newValue) //现在是响应式的

//如果userProfile上没有age字段
vm.userProfile.age = 27 //不响应
Vue.set(vm.userProfile, 'age', 27)  //现在是响应式的

4.父组件代码

example.vue文件

<template>
    <view>
        <view class="cu-modal bottom-modal show">
            <view class="cu-dialog">
                <view class="my-search-button-group text-size-main cu-bar bg-white">
                    <view class="action text-blue">取消</view>
                    <view class="action text-green">确定</view>
                </view>
                <!-- 筛选条件 s-->
                <son-component :selects="selects"></son-component>
                <!-- 筛选条件 e -->
                <view class="bg-white" style="height:30px;"></view>
            </view>
        </view>
    </view>
</template>
<script lang="ts" src="./example.ts"></script>

example.ts文件

import Vue from 'vue';
import { Component } from 'vue-property-decorator';
import SonComponent from '../components/son';


@Component({
    components: {
        SonComponent
    },
    data() {

        let selects = [
            {
                label: "订单状态1",
                name: "status",
                selected: "",
                options: [
                    {
                        label: "全部",
                        value: ""
                    },
                    {
                        label: "已到账",
                        value: "account"
                    },
                    {
                        label: "冻结中",
                        value: "freeze"
                    }
                ]
            },
            {
                label: "订单状态2",
                name: "status2",
                selected: "",
                options: [
                    {
                        label: "全部",
                        value: ""
                    },
                    {
                        label: "已到账",
                        value: "account"
                    },
                    {
                        label: "冻结中",
                        value: "freeze"
                    }
                ]
            }
        ]

        return { selects }
    }
})
export default class Example extends Vue {

}
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!