Vuex change state in diferent routes - javascript

I'm trying to change the state of my Vuex Store with the mutations, and I also have routes in my application. In my default component (the first route) has a button that changes my user.name state, and I have a second component (the second route) that show to me the user.name. In my first component, when I call the mutation to change the user.name state it shows to me the new user.name, but this doesn't become different of the first state.
For example: In my first user.name state I have "John Dave" and when I click on the button that calls the mutation CHANGE_USER(), this has to change to "David Lionel", but don't work.
Store
export default new Vuex.Store({
state: {
user: {
name: 'John Dave',
},
},
mutations: {
changeUser (state, payload) {
state.user = payload;
}
},
getters: {
getName(state) {
return state.user;
}
}
});
First component
<template>
<div>
{{user}}
</div>
</template>
<script>
export default {
computed: {
user() {
const { name } = this.$store.getters.getName;
return name;
},
},
};
</script>
Second component
<template>
<div>
{{user}}
<button #click="changeUser() ">
Change user
</button>
</div>
</template>
<script>
export default {
computed: {
user() {
const { name } = this.$store.state.user;
return `My user is ${name}`
},
},
methods: {
changeUser() {
const payload = {
name: 'Name changed',
email: 'vitor#v.cm',
level: 'ADM',
};
this.$store.commit('changeUser', payload);
},
}
};
</script>

Related

Vuex global state change does not trigger re-render component in v-for loop in Nuxt

I have difficult to use vuex global state combine with re-render child-component in Vue.js.
The global state is mutated but does not re-render its data in v-for loop.
All list of data is rendered, but when the new data changes, component in /blog does not change data in it.
Here is some code:
/store/index.js
export const state = () => ({
allData: [],
})
export const getters = {
getAllData: (state) => state.allData,
}
export const mutations = {
GET_DATAS(state, payload) {
state.allData = payload
},
UPDATE_DATA(state, payload) {
const item = state.allData[payload.index]
Object.assign(item, payload)
},
}
export const actions = {
getDatas({ commit, state }, payload) {
return fetch(`URL_FETCH`)
.then((data) => data.json())
.then((data) => {
commit('GET_DATAS', data)
})
.catch((err) => console.log(err))
},
updateData({ commit, state }, payload) {
commit('UPDATE_DATA', payload)
},
}
in /layouts/default.vue
beforeCreate() {
this.$store.dispatch('getDatas').then(() => {
connectSocket()
})
},
methods: {
connectSocket() {
// connect & received message from socket
// received message from socket
this.$root.$emit('updateData', {
index: 12,
price: 34,
change: 56,
percent: 78,
})
},
},
and in /pages/blog/index.vue
<template>
<div>
<div
v-for="index in getAllData"
:key="index.name"
class="w-100 grid-wrapper"
>
<div>{{ index.price }}</div>
<div>{{ index.change }}</div>
<div>{{ index.percent }}</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data() {
return {}
},
computed: {
...mapGetters(['getAllData']),
},
mounted() {
this.$root.$on('updateData', (item) => {
this.$store.dispatch('updateData', {
index: item.index,
price: item.price,
percent: item.percent,
change: item.change,
})
})
},
}
</script>
Here is a complete example on how to use Vuex and load the data efficiently into a Nuxt app (subjective but using good practices).
/pages/index.vue
<template>
<div>
<main v-if="!$fetchState.pending">
<div v-for="user in allData" :key="user.id" style="padding: 0.5rem 0">
<span>{{ user.email }}</span>
</div>
</main>
<button #click="fakeUpdate">Update the 2nd user</button>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
data() {
return {
mockedData: {
name: 'John Doe',
username: 'jodoe',
email: 'yoloswag#gmail.com',
phone: '1-770-736-8031 x56442',
website: 'hildegard.org',
},
}
},
async fetch() {
await this.setAllData()
},
computed: {
...mapState(['allData']),
},
methods: {
...mapActions(['setAllData', 'updateData']),
fakeUpdate() {
this.updateData({ index: 1, payload: this.mockedData })
},
},
}
</script>
/store/index.js
import Vue from 'vue'
export const state = () => ({
allData: [],
})
export const mutations = {
SET_ALL_DATA(state, payload) {
state.allData = payload
},
UPDATE_SPECIFIC_DATA(state, { index, payload }) {
Vue.set(state.allData, index, payload)
},
}
export const actions = {
async setAllData({ commit }) {
try {
const httpCall = await fetch('https://jsonplaceholder.typicode.com/users')
const response = await httpCall.json()
commit('SET_ALL_DATA', response)
} catch (e) {
console.warn('error >>', e)
}
},
updateData({ commit }, { index, payload }) {
commit('UPDATE_SPECIFIC_DATA', { index, payload })
},
}

How to properly access record with dynamic keys in Vuex

I am trying to query an array in Vuex store where product_id equals a dynamic value like in the below code but it returns undefined.
const store = new Vuex.Store({
state: {
products: {},
},
getters: {
getProductById:(state) => (id) => {
return state.products.find(product => product.data.product_id == id)
},
}
}
In component.vue
<template>
<div> {{price}}</div>
</template>
<script>
export default {
name: 'someName',
//props: ['orders'],
data(){
return{
price: '',
}
},
mounted(){
this.price = this.$store.getters.getProductByProductId(1)
}
}
</script>
Use a computed property that returns a function with id as parameter which returns the getter :
<template>
<div> {{price}}</div>
</template>
<script>
export default {
name: 'someName',
//props: ['orders'],
data(){
return{
price: '',
}
},
computed:{
productById(){
return (id)=>this.$store.getters.getProductByProductId(id)
}
},
mounted(){
this.price = productById(1).price
}
}
</script>

Deleting an object in Vue, emitting from child to parent and to parent

I have created a simple to do app in VUE.
In order to delete a card (each card is an object with an id, title and description located in state in an App.vue component), I am passing and id from App as a prop to TaskList and to the button (delete) in the Task component. Then in order trigger a deleteTask function, again I am emmiting an id from Task to TaskList and then to App.
This approach works. However, is that kind of long chain of emmiting is considered as good practice? Is there a better way to do it?
App.vue
<template>
<div>
<TaskList :tasks="tasks" #id="deleteTask"/>
<Form :length="tasks.length" #addTask="addNewTask" />
</div>
</template>
<script>
import TaskList from './components/TaskList';
import Form from './components/Form';
export default {
name: 'App',
components: { TaskList, Form },
data() {
return {
tasks: [
{
id: 1,
title: 'Hello World',
description: 'this is the world'
},
{
id: 2,
title: 'Hello Mars',
description: 'this is the mars'
},
{
id: 3,
title: 'Hello Jupiter',
description: 'this is the jupiter'
}
]
}
},
methods: {
addNewTask(taskObject) {
const listOfTasks = this.tasks.concat(taskObject);
this.tasks = listOfTasks;
},
deleteTask(id) {
const filteredList = this.tasks.filter(element => {
return element.id != id;
})
this.tasks = filteredList;
}
}
}
</script>
TaskList.vue
<template>
<div class="taskList" v-bind:key="task" v-for="task in tasks">
<Task :title="task.title" :description="task.description" :id="task.id" #id="sendId"/>
</div>
</template>
<script>
import Task from './Task';
export default {
props: ['tasks'],
components: { Task },
methods: {
sendId(id) {
this.$emit('id', id);
console.log(id)
}
}
}
</script>
Task.vue
<template>
<div class="task">
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<button #click="passId">Delete</button>
</div>
</template>
<script>
export default {
props: ['title', 'description', 'id'],
methods: {
passId() {
this.$emit('id', this.id);
}
}
}
</script>
One sure way of reducing this chain of data transfer is by using Vuex, But if you don't want to use that you can also use an "EventBus"
NOTE - Still you will have to pass the id from parent to child
Creating event bus
// src > eventBus.js
import Vue from 'vue'
export default new Vue()
Emit the event when the user clicks on the delete button
// Task.vue
<template>
<div class="task">
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<button #click="passId">Delete</button>
</div>
</template>
<script>
import EventBus from 'path/to/eventBus'
export default {
props: ['title', 'description', 'id'],
methods: {
passId() {
EventBus.$emit('delete-task', this.id);
}
}
}
</script>
Listen to the event on the topmost parent
<template>
<div>
<TaskList :tasks="tasks" #id="deleteTask"/>
<Form :length="tasks.length" #addTask="addNewTask" />
</div>
</template>
<script>
import TaskList from './components/TaskList';
import Form from './components/Form';
import EventBus from 'path/to/eventBus.js'
export default {
name: 'App',
components: { TaskList, Form },
data() {
return {
tasks: [
{
id: 1,
title: 'Hello World',
description: 'this is the world'
},
{
id: 2,
title: 'Hello Mars',
description: 'this is the mars'
},
{
id: 3,
title: 'Hello Jupiter',
description: 'this is the jupiter'
}
]
}
},
mounted(){
// Listening to the delete-task event
EventBus.$on('delete-task', (id) => {
this.deleteTask(id)
})
},
methods: {
addNewTask(taskObject) {
const listOfTasks = this.tasks.concat(taskObject);
this.tasks = listOfTasks;
},
deleteTask(id) {
const filteredList = this.tasks.filter(element => {
return element.id != id;
})
this.tasks = filteredList;
}
}
}
</script>

Using Vuex state in computed property to open modal, changes are ignored, modal stays closed

I have the following code to dynamically add modal states to the Vuex store and trigger them troughout my whole application. Even though the state changes, when I press a button which dispatches the toggle action, the modal stays hidden. (using quasar framework for components)
component
<template>
<q-dialog v-model="status" persistent>
<q-card>
<q-card-actions align="right">
<q-btn flat label="Abbrechen" color="dark" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script>
import modal from '../../mixins/modal'
export default {
name: 'DiscardSession',
mixins: [modal]
}
</script>
<style scoped>
</style>
mixin
export default {
beforeCreate () {
console.log('define modal')
this.$store.dispatch('modal/define', this.$options.name)
},
computed: {
status: {
get () {
console.log('getter triggered')
return this.$store.getters['modal/status'][this.$options.name]
},
set () {
console.log('setter triggered')
this.$store.dispatch('modal/toggle', this.$options.name)
}
}
}
}
store
export default {
namespaced: true,
state: {
status: {}
},
getters: {
status (state) {
return state.status
}
},
mutations: {
DEFINE_STATUS (state, name) {
state.status[name] = false
},
TOGGLE_STATUS (state, name) {
state.status[name] = !state.status[name]
}
},
actions: {
define ({ commit, state}, name) {
if (!(name in state.status)) commit('DEFINE_STATUS', name)
},
toggle ({ commit, state }, name) {
commit('TOGGLE_STATUS', name)
}
}
}
It might be a reactivity problem. Can you try the following code?
TOGGLE_STATUS (state, name) {
state.status = {
...state.status,
[name]: !state.status[name]
}
}

Vue binding parent and child components

How to binding parent's model to child in Vue.js?
These codes below is works fine. if i fill the input manually, then child's model return it's value to the parent's model.
But the issue is, if the data set from AJAX request in a parent, the input doesn't automatically filled.
Can anyone help me on this?
Form.vue
<template>
<form-input v-model="o.name" :fieldModel="o.name" #listenChanges="o.name = $event"/>
<form-input v-model="o.address" :fieldModel="o.address" #listenChanges="o.address = $event"/>
</template>
<script>
import FormInput from '../share/FormInput.vue'
export default {
data () {
return {
o: {
name: '',
address: ''
}
}
},
components: { 'form-input': FormInput },
created: function() {
axios.get('http://api.example.com')
.then(response => {
this.o.name = response.data.name
this.o.address = response.data.address
})
.catch(e => { console.log(e) })
}
}
</script>
FormInput.vue
<template>
<input type="text" v-model='fieldModelValue' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
mounted: function() {
this.fieldModelValue = this.fieldModel;
},
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Things are more straightforward if you take advantage of v-model in components.
If you put v-model on a component, the component should take a prop named value, and should emit input events to trigger it to update.
I like to make a computed to hide the event emitting, and allow me to just v-model the computed inside my component.
new Vue({
el: '#app',
data: {
o: {
name: '',
address: ''
}
},
components: {
'form-input': {
template: '#form-input',
props: ['value'],
computed: {
fieldModelValue: {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue.toUpperCase());
}
}
}
}
},
// Simulate axios call
created: function() {
setTimeout(() => {
this.o.name = 'the name';
this.o.address = 'and address';
}, 500);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
Name ({{o.name}})
<form-input v-model="o.name"></form-input>
Address ({{o.address}})
<form-input v-model="o.address"></form-input>
</div>
<template id="form-input">
<input type="text" v-model='fieldModelValue'>
</template>
The mounted() hook is blocking subsequent updates from the parent.
Remove mounted and change v-model to 'fieldModel'
<template>
<input type="text" :value='fieldModel' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
// mounted: function() {
// this.fieldModelValue = this.fieldModel;
// },
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Demo CodeSandbox

Categories

Resources