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

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?

Related

How to not trigger watch when data is modified on specific cases

I'm having a case where I do wish to trigger the watch event on a vue project I'm having, basically I pull all the data that I need then assign it to a variable called content
content: []
its a array that can have multiple records (each record indentifies a row in the db)
Example:
content: [
{ id: 0, name: "First", data: "{jsondata}" },
{ id: 1, name: "Second", data: "{jsondata}" },
{ id: 2, name: "Third", data: "{jsondata}" },
]
then I have a variable that I set to "select" any of these records:
selectedId
and I have a computed property that gives me the current object:
selectedItem: function () {
var component = this;
if(this.content != null && this.content.length > 0 && this.selectedId!= null){
let item = this.content.find(x => x.id === this.selectedPlotBoardId);
return item;
}
}
using this returned object I'm able to render what I want on the DOM depending on the id I select,then I watch this "content":
watch: {
content: {
handler(n, o) {
if(o.length != 0){
savetodbselectedobject();
}
},
deep: true
}
}
this work excellent when I modify the really deep JSON these records have individually, the problem I have is that I have a different upload methord to for example, update the name of any root record
Example: changing "First" to "1"
this sadly triggers a change on the watcher and I'm generating a extra request that isnt updating anything, is there a way to stop that?
This Page can help you.
you need to a method for disables the watchers within its callback.

Computed Property causes array and object mutation

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

Vue: Watch array data variable doesnt work

i'm trying to watch an array declarated in data method (the 'validated' variable). I already have a watcher to an input (legal_name) and it works correctly but the array watcher doesnt give any response. Any idea?
export default {
data() {
return {
legal_name : '',
validated: [],
errors: []
}
},
watch: {
validated() {
console.log('modified')
},
legal_name(value) {
this.eventName();
this.legal_name = value;
this.checkLength(value, 3);
}
},
methods: {
checkLength(value, lengthRequired) {
if(value.length < lengthRequired) {
this.errors[name] = `Debes ingresar al menos ${lengthRequired} caracteres`;
this.validated[name] = false;
return false;
}
this.errors[name] = '';
this.validated[name] = true;
return true;
},
eventName() {
name = event.target.name;
}
}
}
You need to call Vue.set() for arrays, and NOT use indexing such as
foo[3]= 'bar'
Vue DOES recognize some operations, such as splice and push, however.
Read more about it here: https://vuejs.org/2016/02/06/common-gotchas/ and here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection
So for your code, and using the Vue handy helper method $set:
this.validated.$set(name, true);
Why...
Javascript does not offer a hook (overload) for the array index operator ([]), so Vue has no way of intercepting it. This is a limitation of Javascript, not Vue. Here's more on that: How would you overload the [] operator in javascript

How to pass an array of objects to child component in VueJS 2.x

I am trying to send an array containing arrays which in turn contains objects to one component from another, but the content from the array seems to be empty in the child component.
I have tried sending the data as a String using JSON.Stringify() and also as an array
My parent component:
data: function(){
return{
myLineItems : []
}
},
created(){
this.CreateLineItems();
},
methods:{
CreateLineItems(){
let myArrayData = [[{"title":"Title1","value":2768.88}],[{"title":"Title2","value":9}],[{"title":"Title3","value":53.61},{"title":"Title4","value":888.77},{"title":"Title5","value":1206.11},{"title":"Title6","value":162.5}]]
this.myLineItems = myArrayData;
}
}
My parent component's template:
/*
template: `<div><InvoiceChart v-bind:lineItems="myLineItems"></InvoiceChart></div>`
My child component:
const ChildComponent= {
props: {
lineItems: {
type: Array
}
},
mounted() {
console.log(this.lineItems);
}
};
The parent component is created as so (inside a method of our main component):
var ComponentClass = Vue.extend(InvoiceDetails);
var instance = new ComponentClass({
propsData: { invoiceid: invoiceId }
});
instance.$mount();
var elem = this.$refs['details-' + invoiceId];
elem[0].innerHTML = "";
elem[0].appendChild(instance.$el);
If I try to do a console.log(this) inside the childcomponent, I can see the correct array data exist on the lineItems property..but i can't seem to access it.
I have just started using VueJS so I haven't quite gotten a hang of the dataflow here yet, though I've tried reading the documentation as well as similar cases here on stackoverflow to no avail.
Expected result: using this.lineItems should be a populated array of my values sent from the parent.
Actual results: this.lineItems is an empty Array
Edit: The problem seemed to be related to how I created my parent component:
var ComponentClass = Vue.extend(InvoiceDetails);
var instance = new ComponentClass({
propsData: { invoiceid: invoiceId }
});
instance.$mount();
var elem = this.$refs['details-' + invoiceId];
elem[0].innerHTML = "";
elem[0].appendChild(instance.$el);
Changing this to a regular custom vue component fixed the issue
Code - https://codesandbox.io/s/znl2yy478p
You can print your object through function JSON.stringify() - in this case all functions will be omitted and only values will be printed.
Everything looks good in your code.
The issue is the property is not correctly getting passed down, and the default property is being used.
Update the way you instantiate the top level component.
Try as below =>
const ChildComponent= {
props: {
lineItems: {
type: Array
}
},
mounted() {
console.log(this.lineItems);
}
};

Update all object property of an array using vuex

I am trying to update a single property of an object from an array using vuex.
here is my code in store file.
export default{
namespaced: true,
state: {
customers: null,
},
mutations: {
UPDATE_MODIFIED_STATE(state, value) {
state.customers = [
...state.customers.filter(item => item.Id !== value.Id),
value,
];
},
},
And below code is from my .vue file.
export default {
computed: {
customerArray() {
return this.$store.state.CustomerStore.customers;
},
},
methods: {
...mapMutations('CustomerStore', ['UPDATE_MODIFIED_STATE']),
updateCustomers() {
if(someCondition) {
this.customerArray.forEach((element) => {
element.IsModified = true;
this.UPDATE_MODIFIED_STATE(element);
});
}
/// Some other code here
},
},
};
As you can see I want to update IsModified property of object.
It is working perfectly fine. it is updating the each customer object.
Just want to make sure, is it correct way to update array object or I should use Vue.set.
If yes, I should use Vue.set, then How can I use it here.
You are actually not mutating your array, what you do is replacing the original array with a new array generated by the filter function and the passed value. So in your example there is no need to use Vue.set.
You can find more information about replacing an array in the vue documentation.
The caveats begin however when you directly set an item with the index or when you modify the length of the array. When doing this the data will no longer be reactive, you can read more about this here.
For example, consider the following inside a mutation:
// If you update an array item like this it will no longer be reactive.
state.customers[0] = { Id: 0, IsModified: true }
// If you update an array item like this it will remain reactive.
Vue.set(state.customers, 0, { Id: 0, IsModified: true })