How to commit received data to Vue store? - vue.js

I'm trying to:
get element's data #click using getDetails method and put it into fileProperties: []
and then send that data to store using fileDetails computed property
This worked for my other components which have v-model and simple true/false state, but I'm not sure how to send the created by the method array of data to the store properly.
In other words, how do I make this computed property to get the data from fileProperties: [] and commit it to store? The fileDetails computed property below is not committing anything.
Code:
[...]
<div #click="getDetails(file)"></div>
[...]
<script>
export default {
name: 'files',
data () {
return {
fileProperties: []
}
},
props: {
file: Object
},
methods: {
getDetails (value) {
this.fileProperties = [{"extension": path.extname(value.path)},
{"size": this.$options.filters.prettySize(value.stat.size)}]
}
},
computed: {
isFile () {
return this.file.stat.isFile()
},
fileDetails: {
get () {
return this.$store.state.Settings.fileDetails
},
set (value) {
this.$store.commit('loadFileDetails', this.fileProperties)
}
}
}
}
</script>
store module:
const state = {
fileDetails: []
}
const mutations = {
loadFileDetails (state, fileDetails) {
state.fileDetails = fileDetails
}
}
Example on codepen:
https://codepen.io/anon/pen/qxjdNo?editors=1011
In this codepen example, how can I send over the dummy data [ { "1": 1 }, { "2": 2 } ] to the store on button click?

You are never setting the value for fileDetails, so the set method of the computed property is never getting called. Here's the documentation on computed setters.
If the fileProperties data is really just the same as the fileDetails data, then get rid of it and set fileDetails directly in your getDetails method.
Here's a working example:
const store = new Vuex.Store({
state: {
fileDetails: null
},
mutations: {
loadFileDetails (state, fileDetails) {
state.fileDetails = fileDetails
}
}
})
new Vue({
el: '#app',
store,
data() {
return {
fileProperties: null
}
},
methods: {
getDetails (value) {
this.fileDetails = [{"1": 1}, {"2": 2}]
}
},
computed: {
fileDetails: {
get () {
return this.$store.state.fileDetails
},
set (value) {
this.$store.commit('loadFileDetails', value)
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
<h1>element data:</h1>
{{fileDetails}}
<hr>
<h1>store data:</h1>
<p>(should display the same data on button click)</p>
{{fileDetails}}
<hr>
<button #click="getDetails">btn</button>
</div>

Related

Vue computed methods can't change data using setter in template

I need change data using computed:
<template>
<div>{{ userDataTest }}</div>
</template>
props: {
exampleData: {
type: Object,
required: true,
},
},
computed: {
userDataTest: {
get: function() {
return this.exampleData;
},
set: function(newValue) {
console.log(newValue);
return newValue;
},
},
}
mounted () {
setTimeout(() => {
console.log('Change now to null!');
this.userDataTest = null;
}, 5000);
},
I get data using props, next I create computed methods with getter and setter. I added userDataTest in <template>. And the I change (using mounted) data in this.userDataTest to null using setter.
In console.log(newValue); in setter I see newValue is null, but in <template> nothing change still I have data from getter.
Why setter not change data in <template> to null ?
It seems you're trying to set the computed property's value by returning a new value, but Vue doesn't actually check the setter's return value. Perhaps you were trying to proxy a data variable through a computed property. If so, the setter should set that data variable in the setter body.
For instance, your component could declare a data variable, named userData, which always has the latest value of the exampleData prop through a watcher:
export default {
props: {
exampleData: Object
},
data() {
return {
userData: {}
}
},
watch: {
exampleData(exampleData) {
this.userData = exampleData
}
},
}
Then, your template and computed prop would use userData instead:
<template>
<div>{{ userData }}</div>
</template>
<script>
export default {
//...
computed: {
userDataTest: {
get() {
return this.userData
},
set(newValue) {
this.userData = newValue
}
}
}
}
</script>
Mutating a prop locally is considered an anti-pattern
However, you can use the .sync modifier as shown below, but you can't set the prop to null because you are specifying that it has to be an Object type.
Vue.component('my-component', {
template: `<div>{{ userDataTest }}</div>`,
props: {
exampleData: {
type: Object,
required: true
}
},
computed: {
userDataTest: {
get: function() {
return this.exampleData
},
set: function(newValue) {
this.$emit('update:exampleData', newValue)
}
}
},
mounted() {
setTimeout(() => {
console.log('Change now!')
this.userDataTest = {}
}, 2500)
}
})
new Vue({
el: '#app',
data() {
return {
exampleData: {
foo: 'bar'
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<my-component :example-data.sync="exampleData"></my-component>
</div>

vuex: how to save data from multiple components?

If I follow the vuex state docu I get a simple counter. Now I can use this counter as a component like this: https://jsfiddle.net/zr86xtqg/. Its easy to note that all counters use the same data (like a component without data: function(){...}).
What do I need to do to get a real component with unique data? Should I add an array to the store and push each counter into it?
Sorry for this simple question but I am totally new to this state managment / data handling.
Maybe it's nice to know why I need it: There is a place in my app with a variable mix of components and another place where I have to display some of the component data. I googled a bit and found many recommendation to use vuex.
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment: state => state.count++,
decrement: state => state.count--
}
})
Vue.component('test-counter', {
computed: {
count () {
return store.state.count
}
},
template: '<div><p>{{ count }}</p><button #click="increment">+</button><button #click="decrement">-</button><p></div>',
methods: {
increment () {
store.commit('increment')
},
decrement () {
store.commit('decrement')
}
}
})
new Vue({
el: '#app'
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js"></script>
<div id="app">
<test-counter></test-counter>
<test-counter></test-counter>
</div>
Yeah, you can make multiple store for various modules. Below I'll show you an example that how I make the store for many modules.
IMPORTANT I recommend you to use namespace=true, It allow you differenciate from each other
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const storeTercero = {
namespaced:true,
state: {
razonSocial: null
},
mutations:{
setRazon(pState, pRazon){
pState.razonSocial = pRazon
}
}
}
const storeFactura = {
namespaced:true,
state:{
numero: null
},
mutations:{
setNumero(pState, pNumero){
pState.numero = pNumero
}
}
}
const storeProducto = {
namespaced: true,
state:{
arr_pestanas: []
},
mutations:{
setPestanas(pState,pArrPestanas){
pState.arr_pestanas = pArrPestanas
},
setFolia1(pState,pObjFolia1){
pState.arr_pestanas.folia1 = pObjFolia1
},
setCombinaciones(pState,pObjCombinaciones){
pState.arr_pestanas.push(pObjCombinaciones)
},
limpiarPestanas(pState){
pState.arr_pestanas = []
},
actualizarFolia(pState,payload){
switch (payload.fila) {
case 1:
pState.arr_pestanas[payload.indice].folia1= {
valor:payload.objeto.value,
texto:payload.objeto.display
}
break;
case 2:
pState.arr_pestanas[payload.indice].folia2= {
valor:payload.objeto.value,
texto:payload.objeto.display
}
break;
case 3:
pState.arr_pestanas[payload.indice].folia3= {
valor:payload.objeto.value,
texto:payload.objeto.display
}
break;
default:
pState.arr_pestanas[payload.indice].folia1= null
pState.arr_pestanas[payload.indice].folia2= null
pState.arr_pestanas[payload.indice].folia3= null
break;
}
},
eliminarIndice(pState,payload){
pState.arr_pestanas.splice(payload.indice,1)
}
},
actions:{
actualizarFolia({commit},payload){
commit('actualizarFolia',payload)
},
cambiarFoliaTodos({state,commit},payload){
state.arr_pestanas.forEach((valor,indice,array) => {
commit('actualizarFolia',{
indice,
fila:payload.fila,
objeto:payload.objeto
})
});
},
eliminarCombinacion({commit},payload){
if (payload.indice > -1) {
commit('eliminarIndice',{
indice:payload.indice
})
}
}
},
getters:{
pestanasCount: state => {
return state.arr_pestanas.length
}
}
}
const storeCartera = {
namespaced: true,
state:{
arrCarteraPagos: []
},
mutations:{
actualizarVlrPago(pState,payload){
pState.arrCarteraPagos[payload.fila] = payload.valorPago;
},
setVlrCarteraPagos(pState,payload){
pState.arrCarteraPagos.filter(function(item){
if(item.id === payload.id){
item.valorPagado = payload.vlr;
}
})
},
setArrCarteraPagos(pState,payload){
pState.arrCarteraPagos = payload
}
}
}
const store = new Vuex.Store({
strict: true,
modules:{
terceros: storeTercero,
facturas: storeFactura,
productos: storeProducto,
cartera: storeCartera
}
})
export default store
Is this what you are trying to achieve?
HTML
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
JS
// Define a new component called button-counter
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
new Vue({ el: '#components-demo' })
DEMO
https://jsfiddle.net/emanuell_minciu/L4xot38s/
SOURCE:
https://v2.vuejs.org/v2/guide/components.html
DEMO UPDATE (Vuex)
https://jsfiddle.net/emanuell_minciu/Ljno612k/3/
DEMO UPDATE (based on new requirements)
https://jsfiddle.net/emanuell_minciu/cr5w87g4/53/

Iterating over a Vuex store object

I'm new to Vue.js and Vuex and trying out a sample app.
This is the scenario-
I have a store module for notifications which stores the notifications in an object with a given name as its key.
{
'message1': {
type: 'info',
message: 'This is an info message.',
isShown: true,
},
'message2': {
type: 'success',
message: 'This is a success message.',
isShown: true,
},
'message3': {
type: 'error',
message: 'This is an error message.',
isShown: true,
}
}
And this is my Vuex module that handles notification-
const state = {
notifications: {},
};
const mutations = {
setNotification(state, { message, type, name }) {
state.notifications[name] = {
message,
type,
isShown: true,
}
},
removeNotification(state, name) {
delete state.notifications[name];
}
};
const actions = {
async showNotification(context, options) {
await context.commit('setNotification', options);
},
async removeNotification(context, name) {
await context.commit('removeNotification', name);
}
}
const getters = {
isNotificationShown: (state, getters) => {
return getters.getNotificationMessageList.length > 0;
},
getNotificationMessageList: state => {
return state.notifications;
},
}
export default {
state,
actions,
mutations,
getters,
}
And this is my component-
<template>
<div v-if="isShown">
<div v-for="(notice, name, index) in notificationMessageList" :key="name">
{{ index }} - {{ notice.type }} - {{ notice.message}}
</div>
</div>
</template>
<script>
export default {
computed: {
isShown() {
return this.$store.getters.isNotificationShown;
},
notificationMessageList() {
return this.$store.getters.getNotificationMessageList;
},
},
};
</script>
I checked with the Vue Development tool and found that the store does get updated and so does the component with the notification messages that I'm passing to the store. But the component is not being rendered. But if I use the same data by hardcoding it in the component, it works.
I'm not sure if this is the right way to connect the Vuex store to a component.
It's Vue reactivity problem. You need to update the reference to make Vue reactive. You can use JSON.parse(JSON.stringify()) or use ES6 syntax:
const mutations = {
setNotification(state, { message, type, name }) {
state.notifications = {
...state.notifications,
[name]: {
message,
type,
isShown: true
}
}
},
removeNotification(state, name) {
const newNotifications = {...state.notifications}
delete newNotifications[name]
state.notifications = newNotifications
}
};

vue computed cannot get the the attributes in the object

Account to the defind of vuex
// inside mutations
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
// html
<input v-model="message">
// ...
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
and I code this
<input type="text" v-model="data.reference">
data () {
return {
data:{
...
reference: '',
}
}
},
computed: {
'data.reference':{
get () {
return this.$store.state.currentKbdata.reference
},
set (value) {
console.log(222)
this.$store.commit('updateReference', value)
}
}
}
And when i enter the input the 222 is not show up
.........................................................................
You cannot define computed getters with dot notation like you can watchers. Here's a fiddle of that not working, where you can see the error in the console reads:
Cannot read property 'reference' of undefined.
Also, it appears that you are attempting to define a computed property that already exists as a property defined in the data method. In this fiddle, you can see that that also won't work. The value defined in the data method takes precedence over the computed property definition.
Anyways, from your example, I don't see why you need to create a computed property on a nested object property at all.
Just use a normal definition for a computed property:
const store = new Vuex.Store({
state: {
reference: '',
},
mutations: {
updateReference(state, reference) {
state.reference = reference;
}
}
});
new Vue({
el: '#app',
store,
computed: {
reference: {
get() {
return this.$store.state.reference;
},
set(val) {
this.$store.commit('updateReference', val);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.min.js"></script>
<div id="app">
<input type="text" v-model="reference">
{{ $store.state.reference }}
</div>
If you really need to set the value of data.reference, then there are many ways you could make that happen. One would be to turn it into a computed property as well:
const store = new Vuex.Store({
state: {
reference: '',
},
mutations: {
updateReference(state, reference) {
state.reference = reference;
}
}
});
new Vue({
el: '#app',
store,
computed: {
reference: {
get() {
return this.$store.state.reference;
},
set(val) {
this.$store.commit('updateReference', val);
}
},
data() {
return { reference: this.reference };
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.min.js"></script>
<div id="app">
<input type="text" v-model="reference">
<div>data.reference: {{ data.reference }}</div>
<div>$store.state.reference: {{ $store.state.reference }}</div>
</div>

V-bind not updating class on store state change

I am pretty new to Vue and Nuxt. I am trying to get my head around $stores.
I created a state object and gave it a property which is a simple boolean. I'd like to add a class to an element depending on whether or not that property is true. Here's how I created the store:
const store = () => {
return new Vuex.Store({
state: {
foo: "You got the global state!",
userSidebarVisible: true
},
})
}
In my vue file I have the following:
<template>
<div>
<div>Hello!</div>
<button v-on:click="showSidebar">Click</button>
<div v-bind:class="{active: userSidebarVisible}">the sidebar</div>
<div>{{$store.state.userSidebarVisible}}</div>
</div>
</template>
<script>
export default {
data: function() {
return {
userSidebarVisible: this.$store.state.userSidebarVisible,
}
},
methods: {
showSidebar: function() {
if (this.$store.state.userSidebarVisible === true) {
this.$store.state.userSidebarVisible = false;
} else {
this.$store.state.userSidebarVisible = true;
}
}
}
}
</script>
When I click the button, the active class doesn't toggle, but the text within the last <div> does get updated. I am wondering what I am doing wrong here. Doing the same thing with local data property seems to work as intended.
First of all, you should not change the $store state outside of a mutation.
You need to add a mutation method to your store for updating userSidebarVisible:
state: {
userSidebarVisible: true
},
mutations: {
SET_USER_SIDEBAR_VISIBLE(state, value) {
state.userSidebarVisible = value;
}
}
Secondly, if you want your Vue instance's data to reflect the state data, you can make userSidebarVisible a computed property with getter and setter functions:
computed: {
userSidebarVisible: {
get() {
return this.$store.state.userSidebarVisible;
},
set(value) {
this.$store.commit('SET_USER_SIDEBAR_VISIBLE', value);
}
}
}
Here's an example:
const store = new Vuex.Store({
state: {
userSidebarVisible: true
},
mutations: {
SET_USER_SIDEBAR_VISIBLE(state, value) {
state.userSidebarVisible = value;
}
}
})
new Vue({
el: '#app',
store,
computed: {
userSidebarVisible: {
get() {
return this.$store.state.userSidebarVisible;
},
set(value) {
this.$store.commit('SET_USER_SIDEBAR_VISIBLE', value);
}
}
},
methods: {
toggleSidebar() {
this.userSidebarVisible = !this.userSidebarVisible;
}
}
})
.active {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.4.0/vuex.min.js"></script>
<div id="app">
<button v-on:click="toggleSidebar">Click</button>
<div v-bind:class="{active: userSidebarVisible}">the sidebar</div>
<div>Global state: {{$store.state.userSidebarVisible}}</div>
<div>Vue instance state: {{userSidebarVisible}}</div>
</div>