Reputation: 29987
I have a Vue page which gathers information from external JSON sources, and then uses it to compute a property.
Problem: this property is not reactive (= it is not recomputed when underlying data changes)
<div id="app">
<div v-for="tag in filteredTags">{{tag}}</div>
</div>
<script>
new Vue({
el: "#app",
data: {
tags: {},
allTags: {},
},
computed: {
// provides an Array of tags which are selected
filteredTags() {
let t = Object.keys(this.allTags).filter(x => this.allTags[x])
console.log(t)
return t
}
},
mounted() {
// get source tags
fetch("tags.json")
.then(r => r.json())
.then(r => {
this.tags = r
console.log(JSON.stringify(this.tags))
// bootstrap a tags reference where all the tags are selected
Object.keys(this.tags).forEach(t => {
this.allTags[t] = true
});
console.log(JSON.stringify(this.allTags))
})
}
})
</script>
The file which is fetched ({"tag1":["/posts/premier/"],"post":["/posts/premier/","/posts/second/"]}
) is correctly processed in mounted()
, the output on the console is
{"tag1":["/posts/premier/"],"post":["/posts/premier/","/posts/second/"]}
{"tag1":true,"post":true}
filteredTags
is however empty. On the console, I see it displayed (as []
) right at the start of the processing of the page, which is initially fine (first compute, when allTags
is empty), but is then not computed anymore when allTags
changes (after tags.json
is fetched, processed and allTags
correctly updated).
Why isn't this reactive?
Upvotes: 1
Views: 499
Reputation: 10334
Vue isn't reactive to properties that didn't exist when the object was added to data
Since your tag
and allTags
are empty objects with no properties (yet), any properties added after aren't reactive automatically.
To solve this you have to use the Vue.Set
or this.$set
functions provided by Vue.
the Set
function accepts the values this.$set(object, key, value)
new Vue({
el: "#app",
data: {
tags: {},
allTags: {},
},
computed: {
// provides an Array of tags which are selected
filteredTags() {
let t = Object.keys(this.allTags).filter(x => this.allTags[x])
return t
}
},
mounted() {
const r = '{"tag1":["/posts/premier/"],"post":["/posts/premier/","/posts/second/"]}';
const rJson = JSON.parse(r);
// You shouldn't need to use $set here as you replace the entire object, instead of adding properties
this.tags = rJson;
Object.keys(this.tags).forEach(t => {
// Change this
//this.allTags[t] = true;
// To this
this.$set(this.allTags, t, true);
});
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<div id="app">
<div v-for="tag in filteredTags">{{tag}}</div>
</div>
Upvotes: 3