資料變動與渲染問題

1711293058發表於2020-03-22

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 協議》,轉載必須註明作者和本文連結

相關文章