Vue / Vuex: How to properly make a mutation for adding a property nested in object - vue.js

I have a mutation that sets my state of products from a backend API when the page loads. Below is how the data is structured when the mutation runs and populates the products.
state:
products: {
title: "Television",
desc: "Here is a tv",
order_products: [
{
inventory_id: 44,
color: "red"
},
{
inventory_id: 45,
color: "blue"
},
{
inventory_id: 46,
color: green,
}
]
}
I need to periodically find a nested product by the inventory_id and a add a property scanned (eg. scanned: true) under the nested color field for each inventory item. I am reading on the proper way to use a mutation to add a property to an Object using Vuex such as this method: state.obj = { ...state.obj, newProp: 123 } (https://vuex.vuejs.org/guide/mutations.html)
but I don't know how that would work because I am adding a nested property , not just a property to the root of the Object.

There are multiple ways to add a reactive property.
Here's one way, with Vue.set
const id = '...'
const product = products.order_products.find(p => p.inventory_id === id)
Vue.set(product, 'scanned', true)

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.

VueJS/vuex application design question - how to initialize local data with getters

Context:
I have a reports application that contains a report editor. This Report Editor is used to edit the contents of the report, such as the title, the criteria for filtering the results, the time range of results, etc..
The Problem:
There is something wrong with the way I have used Vuex/Vuejs in my components I believe. My store contains getters for each aspect of this report editor. Like this:
const getters = {
activeReportTitle: state => {
return state.activeReport.title;
},
activeReportID: state => {
return state.activeReport.id;
},
timeframe: state => {
return state.activeReport.timeframe;
},
includePreviousData: state => {
return state.activeReport.includePreviousData;
},
reportCriteria: state => {
return state.activeReport.reportCriteria;
},
emailableList: state => {
return state.activeReport.emailableList;
},
dataPoints: state => {
return state.activeReport.configuration?.dataPoints;
},
...
Each getter is used in a separate component. This component uses the getter only to initialize the local data, and uses actions to modify the state. The way I have done this is by adding a local data property and a watcher on the getter that changes the local data property. The component is using the local data property and that data property is sent to the action and the getter is updated.
ReportSearchCriteria.vue
...
data() {
return {
localReportCriteria: [],
currentCriteria: "",
};
},
watch: {
reportCriteria: {
immediate: true,
handler(val) {
this.localReportCriteria = [...val];
}
}
},
computed:{
...reportStore.mapGetters(['reportCriteria'])
},
methods: {
...reportStore.mapActions(["updateReportCriteria"]),
addSearchCriteria() {
if (this.currentCriteria) {
this.localReportCriteria.push(this.currentCriteria);
this.updateReportCqriteria(this.localReportCriteria);
}
this.currentCriteria = "";
this.$refs['reportCriteriaField'].reset();
},
...
The hierarchy of the components is set up like this
Reports.Vue
GraphEditor.vue
ReportSearchCriteria.vue
Could you clarify what the problem is? Does the 'reportCriteria' not get updated when it's supposed to? How does the function 'updatedReportCriteria' look like? You use mutations to update a state in the store. Also, you have a typo when you're calling the action.

How does an aframe entity defaults to the schema ones if no data is passed on

I'm having some difficulties in understanding how in aFrame an entity works with schema and data together. Forgive me, I'm a bit new in the library and maybe I'm missing something. Thanks.
Consider the following:
// register the aframe component (Button.js):
import { registerComponent, THREE } from 'aframe'
const button = registerComponent('button', {
schema: {
width: { type: 'number', default: 0.6 },
height: { type: 'number', default: 0.40 },
color: { type: 'string', default: 'orange' },
},
init: function() {
const schema = this.schema // Schema property values.
const data = this.data // Component property values.
const el = this.el // Reference to the component's entity.
// Create geometry.
this.geometry = new THREE.BoxBufferGeometry(data.width || schema.width.default, data.height || schema.height.default, data.depth || schema.depth.default)
// Create material.
this.material = new THREE.MeshStandardMaterial({ color: data.color || schema.color.default })
// Create mesh.
this.mesh = new THREE.Mesh(this.geometry, this.material)
// Set mesh on entity.
el.setObject3D('mesh', this.mesh)
}
})
export default button
// add the entity in the app (App.js)
import button from './Button'
<a-entity button="width: 0.4; height: 0.4: color: 'green'" position="0 0 0"></a-entity>
Now with the example above I would expect that my component will use data instead of schema defaults. And if I would instead just do:
<a-entity button position="0 0 0"></a-entity>
It would use the schema default ones.
Is this how it should be done?
I've been seeing a lot of examples where people just use data.
Thanks
The schema isn't intended to be used directly. From the docs:
The schema defines the properties of its component. (...)
The component’s property type values are available through this.data
The idea goes like this:
You define the properties (with defaults) in the schema
schema {
prop: {default: "default value"}
},
You access the values with this.data[prop_name]:
init: function() {
console.log("prop value at init:", this.data.prop)
},
You can monitor the property updates (like via setAttribute("propname", "newValue") within the update handler:
update: function(oldData) {
console.log("prop changed from", oldData.prop, "to", this.data.prop)
},
In a setup like this:
<!-- this.data.prop will contain the default values -->
<a-entity foo></a-entity>
<!-- this.data.prop will contain the new value -->
<a-entity foo="prop: value></a-entity>

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

Accessing Vuex state object property from component

I'm trying to change value of a state object property from a component but I can't find the proper way to do it
Here is my state:
state: {
articles: [ {
title: "Lorem ipsum",
position: 7
}, {
title: "Lorem ipsum",
position: 8
}
]
}
In the computed property of the component :
return this.$store.state.articles.filter((article) => {
return (article.title).match(this.search); /// just a search field, don't mind it
});
I would like to change every article.position under 10 to article.position = +'0'article.position.
I tried :
let articlesList = this.$store.state.articles
if(articlesList.position < 10) {
articlesList.position = +'0'articlesList.position
}
I know this is not the good way to do it but this is all I have. Any tips? :)
You should put all Vuex state change code into the Vuex store itself, via a mutation or an action. Mutations are for synchronous data changes that will update all reactive components. Actions are for asynchronous events, such as persisting state changes to the server.
So, when the change occurs, it should call a method on your component, which should in turn call the appropriate mutation or action on the Vuex store.
You don't give your full code, but here is a hypothetical example:
(Edited to reflect question about accessing data from within the mutation method.)
Template:
<input type="text" name="position" :value="article.position" #input="updatePosition">
Method on Component:
methods: {
updatePosition(e) {
this.$store.commit('changeArticleOrder', article.position, e.target.value)
}
}
Mutation on Vuex Store:
mutations: {
changeArticleOrder(state, oldPosition, newPosition) {
for (var i = 0; i < 10; i++) {
state.articles[i].position = i + 1;
/* Or do whatever it is you want to do with your positioning. */
}
}