Computed Property causes array and object mutation - vue.js

I am trying to pass data to a v-select dropdown.
This of course works:
computed: {
itemDropdown() {
const menuItems = {
id: "1",
name: "Joe"
}
return menuItems;
}
}
But when I try:
computed: {
itemDropdown() {
const newArray = [...this.data.originalItems];
newArray.map(item => {
item.name = "myCoolNewName";
});
return newArray;
}
}
It mutates the original array.
I have also tried copying the object:
computed: {
itemDropdown() {
const newObj = { ...this.data };
newObj.items.map(item => {
item.name = "myCoolNewName";
});
return newObj;
}
}
Not sure what I’m missing, but wondering if there is a work around. Thanks for any help :slight_smile:

You are using the map array method wrong.
The first thing you need to know, is that the map method returns a new array, so you have to either return the result of your map function or save it in a variable, otherwise you will just be looping through your array without ever saving it anywhere.
Another thing is about how you use the map method.
Here I have made an example of how it should work with your code:
computed: {
itemDropdown() {
return this.data.originalItems.map(item => {
return {
name: "myCoolNewName"
}
});
}
}
The big difference you should notice, is that inside the map function, we have to return what we want each object to look like, after we have gone through it. We want it to give us the object back, but make some changes to it, so we have to actually return an object and change what we want in that.
What you were doing before, was refering to the item in the old array, and assigning it a new value, instead of returning a new object with your changes.
You can read about the array.map method here
Hope that makes sense :)

Related

All values getting upadted when tried to update single element in an associative array in Vue 3

I have the below code in Vue3:
data: function() {
return {
testData:[],
}
},
mounted() {
var testObj = {
name: 'aniket',
lastname: 'mahadik'
}
for (let index = 0; index < 3; index++) {
this.testData.push(testObj);
}
},
methods: {
updateLastName: function(key) {
this.testData[key].lastname = 'kirve';
}
}
When I call updateLastName(1) to update the lastname of only the second element, it's updating the lastname of all the elements.
I tried several ways but found no desired result.
Can someone point out to me what is going wrong here?
It is because you are pushing the reference to the same object in the array so when you update any item in the array you are instead updating every item since it reference the same object.
Either push by cloning the object :
testData.value.push({...testObj})
Or put the definition in the push
testData.value.push({ name: 'aniket', lastname: 'mahadik' })
Is JavaScript a pass-by-reference or pass-by-value language?

Vue: Add items to computed property results which do not exist my Array of Objects

I have a computed property that filters the results on the date:
resultfilteredResults() {
const filteredResults = this.results.filter((result) => {
return Date.now() < new Date(result.metaData.E);
});
return filteredResults;
},
That works fine.
Now I have realized that my filteredResults need to contain data that does not necessarily exist in the specific Object.
For example. One bit of data within the object in the Array looks like this:
"C": "Pakistan, Vietnam, Wales, Western Sahara, Yemen, Zambia"
Sometimes "C" will not exist (when this is the case it means it should bring back all available data in all "C" objects within the whole Array. This is because it is not only for specific counties but all countries. I hope that makes sense.
I tried this but it does not work.
resultfilteredResults() {
const undefinedResults = result.metaData;
{
const filteredResults = this.results.filter((result) => {
return Date.now() < new Date(result.metaData.E);
});
if (undefinedResults == "undefined") {
return undefinedResults;
} else {
return filteredResults;
}
}
},
Can anyone help?
There is nothing wrong with the code.
What is not clear is if (undefinedResults == "undefined") {
undefinedResult is a string "undefined"? or just undefined?
Can you try this?
if (undefinedResults == undefined) {

How do I watch changes to an object? this.$set seems to capture only property update

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);
}
},
}

Declaring variable in "data" section as an alias to $root is not reactive

I declare variable in main.js:
data: {
globalData: {}
}
I want to avoid using this.$root.globalData all the time — so I use local variable in a component as an alias to "global variable":
data() {
return {
localAlias: this.$root.globalData,
}
}
Then I fetch global variable from a server in main.js (simulate by setTimeout):
create() {
window.setTimeout(() => {
this.globalData = {a:1, b:2};
}, 1500);
}
And localAlias remains equal to initial value.
How to make it work? I don't need Vuex yet, I just grab data from server and use it read-only.
Example
Instead of using data you can use computed. It will solve your problem.
computed: {
localAlias: function() {
return this.$root.globalData;
}
}
I have updated the example
The reason localAlias doesn't change is because it still points to the same object, while you re-point this.$root.globalData to a new object. One way to do it is of course to use computed as the other answer suggested. Another way to solve it it to just change the properties instead of re-binding the entire object:
create() {
window.setTimeout(() => {
this.globalData.a = 1;
this.globalData.b = 2;
}, 1500);
}
This is less versatile though and will scale worse if the object becomes bigger.

Watch for variable change do not work

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).