MrRed
MrRed

Reputation: 697

How to structure nested reactive data in VUE

I think this is a fairly common problem. You fetch a list from an api, and then you display that list via

<div v-for="item in items">
    <checkbox v-model="item.checked">
</div>

Now My question is about the checked property. when iterating over a list of undefined length, of unknown keys, it seems the checked property has to be created and attached to the item object like so

computed: {
    items () {
        var newList = Object.assign([], this.rootList) // shallow clone the api list
        for (var i of newList) {
            i.checked = false
            // or
            Vue.set(i, 'checked', false)
        }
        return newList
    }

However this is not making my checkbox reactive. But more importantly, this way of adding new properties to the rootList object clone, is this the best practice? and if so, why is this not reactive? Even when using Vue.set

Upvotes: 2

Views: 2096

Answers (1)

Filip Sobol
Filip Sobol

Reputation: 5491

Computed properties are by default getter-only [...]

https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive.

https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats

This might help: https://jsfiddle.net/eywraw8t/187063/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <div v-for="item in items">
            <input type="checkbox" v-model="item.checked"> {{ item.name }}
        </div>
        
        <button @click="fetch">Fetch more items</button>
    </div>              
    
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script>
        new Vue({
            el: "#app",
            
            data: {
                items: []
            },
            
            methods: {
                fetch() {
                    let itemsFromApiResponse = [
                        { name: "Test 1" },
                        { name: "Test 2" },
                        { name: "Test 3" },
                    ];
                
                itemsFromApiResponse.forEach(item => this.items.push(Object.assign({ checked: false }, item)));
                }
            }
        })
    </script>
</body>
</html>

Upvotes: 1

Related Questions