I have an empty object data property
data() {
return {
chosen: {},
}
}
Then I have a computed property the results of which are related with chosen property.
myComputed() {
let result = 0
Object.entries(this.chosen).forEach(([key, value]) => {
result = result + value
})
return result
}
Then I have a method that actually changes the chosen property
setChosen(someValue) {
this.chosen['key'] = someValue
}
So when I call setChosen and adding a new value the myComputed is not executed and not updating my view value.
I believe it's caused by your computed method not detecting the change, read more here: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
Try the folowing:
setChosen(someValue) {
// IF no key is parsed
this.$set(this.chosen, 'key', someValue)
// IF key is parsed
// this.$set(this.chosen, key, someValue)
}
Obviously if someValue is only the value, you need to wrap the 'key' in quotes to directly name it: key. If your function is parsed a second value as a string, just use that directly.
Reactively set the new value
Related
I have an array:
const arr = []
I then have a computed property:
const filtered_arr = computed(() => {
//...
}
I then have another computed property: filtered_arr2 where I want to call some() method on the first filtered_arr.
But getting errors since filtered_arr is not an actual array.
const filtered_arr2 = computed(() => {
filtered_arr.some((e)=>{}) // <- error
}
UPDATED:
Getting several errors :
some is not a function
Container is not set or can not be properly recognized. Use container() method to set it
I think my whole approach is buggy...
From the API documentation...
computed()#
Takes a getter function and returns a readonly reactive ref object
and
ref()#
Takes an inner value and returns a reactive and mutable ref object, which has a single property .value that points to the inner value
So with that in mind, you'll need to use the .value property within your setup
const filtered_arr = computed(() => {
// return some array
});
const filtered_arr2 = computed(() => {
const isPresent = filtered_arr.value.some(...);
// return some other value
});
I have this object of arrays that I'm tryin to watch every update of.
myData = {
"299":[527],
"376":[630,629]
}
I read this documentation on watching an object which instructed to use either this.$set(object, propertyName, value) or Object.assign({}, this.object, dataToBeAppended) to watch an object. I used this.$set.
export default {
...
data() {
return {
myData: {},
};
},
watch: {
myData(newVal) {
console.log(`🔴localStorage`);
},
},
methods: {
onFoldChange(propertyName) {
const newArr = [...]
this.$set(this.myData, propertyName, newArr);
},
}
}
Unlike what I expected, vue captures changes on property only. Changes in value to an existing property are not being watched. For example, if a property "299" was newly added, it will print 🔴localStorage. When the value of a property "299" is updated from [527] to something else, nothing is fired. When I print myData, I see every value updated correctly. It is just that watch isn't capturing the changes.
The documentation also described we can watch an array using this.$set(this.myData, indexOfItem, newValue) so I also tried array version of the above code, like this.
this.$set(this.myData[propertyName], index, newValueToAdd);
This time it doesn't listen at all. Not even the first entry.
Is there any better way to solve this issue? How do others watch an object? Is the complication coming from the type of values (array) ?
Currently, myData watcher observes only an object. Object contains pointers to arrays as in JS Objects & Arrays are passed by reference not by copy. That's why it can detect only changes in keys and with simple values. If you want to observe it deeper - I mean also those subarrays (or subobjects) - just use deep watch.
watch: {
myData: {
deep: true,
handler (newVal) {
console.log(`🔴localStorage`);
}
}
}
Another possible solution could be to use some Array.prototype operation to modify an array if it already exists. E.g:
methods: {
onFoldChange(propertyName) {
if (propertyName in this.myData && Array.isArray(this.myData[propertyName])) {
this.myData[properyName].push(162) // Some random value
} else {
const newArr = [...]
this.$set(this.myData, propertyName, newArr);
}
},
}
I have a situation where I need to update data when it detects changes to a state. The user needs to be able to make further changes this info within a textarea. Using computed properties pulls in the data exactly how I want, but any changes made by the user after this are overridden because the computed property keeps changing this data back to it's initial values. What would be the best way to pull in data initially upon a state change but then allow for editing after that point?
Thanks!
Edit: Updated to what i've tried for #Libby.
<textarea v-model="exampleData"></textarea>
computed: {
...mapGetters({
item: 'item'
})
methods: {
exampleFunction() {
this.exampleData = this.item;
}
mounted() {
this.exampleFunction();
}
Update exampleData in a watcher for item:
watch: {
item(value) {
this.exampleData = value;
}
}
This way you can bind your exampleData to the textfield, but changes to the item will still affect it.
And if you want exampleData to be initially set to the value of item, do that in the component's mounted hook:
mounted() {
this.exampleData = this.item;
}
Here's a fiddle.
If you set your property indata, you can initialize it in mounted which only runs once when the page is loaded:
data:
text: null
mounted: ->
text = "This text is initialized"
And then set v-model on your textarea
<textarea v-model="text"></textarea>
So the value of the textarea will start out as "This text is initialized", but the user will be able to change it, and those changes will be saved in text
Vue already has a built-in solution to handle this if you use the getter/setter syntax for computed properties
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
As a result, when your state changes you can update the computer property by assigning it a value:
// state has changed in text area handler
this.fullName = 'new value'
I had an API call to the backend and based on the returned data, I set the reactive data dynamically:
let data = {
quantity: [],
tickets: []
}
api.default.fetch()
.then(function (tickets) {
data.tickets = tickets
tickets.forEach(ticket => {
data.quantity[ticket.id] = 0
})
})
Based on this flow, how can I set watcher for all reactive elements in quantity array dynamically as well?
You can create a computed property, where you can stringify the quantity array, and then set a watcher on this computed property. Code will look something like following:
computed: {
quantityString: function () {
return JSON.stringify(this.quantity)
}
}
watch: {
// whenever question changes, this function will run
quantityString: function (newQuantity) {
var newQuantity = JSON.parse(newQuantity)
//Your relevant code
}
}
Using the [] operator to change a value in an array won't let vue detect the change, use splice instead.
I need to check variable rasters_previews_list for changing. Here is my code:
var userContent = Vue.extend({
template: '<p>Some template</p>',
data: function () {
return {
rasters_previews_list: []
}
},
watch: {
'rasters_previews_list': function(value, mutation) {
console.log("Value changed");
}
}
});
But In console I do not see Value changed when it got new data.
Data changing function:
map.on('draw:created', function (e) {
//...
Vue.http.post('/dbdata', DataBody).then((response) => {
userContent.rasters_previews_list = response; // putting JSON answer to Component data in userContent
console.log(response);
}, (response) => {
console.log("Can't get list rasters metadata from DB. Server error: ", response.status)
});
I change value in map.on('draw:created', function (e) (Leaflet JS). I see console.log output, so seems data is changing.
If you want to change the value of an array you will have to use the special Array extension methods Vue.set and Vue.delete.
Due to limitations of JavaScript, Vue cannot detect the following changes to an Array:
When you directly set an item with the index, e.g. vm.items[0] = {};
When you modify the length of the Array, e.g. vm.items.length = 0.
https://vuejs.org/api/#Vue-set
This problem is also mentioned in the common gotchas
When you modify an Array by directly setting an index (e.g. arr[0] = val) or modifying its length property. Similarly, Vue.js cannot pickup these changes. Always modify arrays by using an Array instance method, or replacing it entirely. Vue provides a convenience method arr.$set(index, value) which is just syntax sugar for arr.splice(index, 1, value).