Vue control and update state from another component with vuex - javascript

For example, I have a file named App.vue and there I have navigation drawer component. And I have a file named Home.vue with the toolbar component. The thing is that I need to toggle navigation drawer state(true or false) from the Home.vue's toolbar component(also, the navigation drawer component is rendered in Home.vue). The code below doesn't return any error and doesn't change the navigation drawer visibility. Also, if set state manually in store.js, navigation drawer follows it. Can anyone please help?
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
drawer: false
},
mutations: {
toggleDrawer: function(state) {
return state.drawer = !state.drawer;
}
},
actions: {
toggleDrawer({ commit }) {
commit('toggleDrawer');
}
},
getters: {
active: (state) => {
return state.drawer;
}
}
})
App.vue
<v-navigation-drawer v-model="drawer"> ... </v-navigation-drawer>
<script>
import store from './store'
export default {
data: function() {
return {
drawer: this.$store.state.drawer
}
}
}
</script>
Home.vue
<v-toolbar-side-icon #click="$store.commit('toggleDrawer')"> ... </v-toolbar-side-icon>
<script>
import store from '../store'
export default {
data: function() {
return {
drawer: this.$store.state.drawer // Seems like I don't need it here
}
}
}
</script>

This is an older post, but in case anyone come looking for the answer, this seems to work.
from the Vuex guide, Form Handling section, Two-way Computed Property
I modified the codesandbox provided by Sovalina (thanks!) link
The other way is to use v-model
<v-navigation-drawer v-model="drawer" app>
with the two way computed property, instead of mapGetters
<script>
export default {
computed: {
drawer: {
get () {
return this.$store.state.drawer
},
set (value) {
this.$store.commit('toggleDrawer', value)
}
}
}
}
</script>

You can use the navigation drawer's property permanent instead of v-model (to avoid mutate your store outside vuex) and use the getter active you defined.
App.vue:
<template>
<v-app >
<v-navigation-drawer :permanent="active">
...
</v-navigation-drawer>
</v-app>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'active'
])
},
}
</script>
Home.vue:
<template>
<v-toolbar-side-icon #click="toggle"> ... </v-toolbar-side-icon>
</template>
<script>
export default {
methods: {
toggle() {
this.$store.dispatch('toggleDrawer')
}
}
}
</script>
Note: as you defined an action toggleDrawer in your store you can use dispatch instead of commit.
Live example here

Related

Reactive property is not propagating to component in Vue 3 app

I have a Vue 3 app. I am trying to setup a store for state management. In this app, I have the following files:
app.vue
component.vue
main.js
store.js
These files include the following:
store.js
import { reactive } from 'vue';
const myStore = reactive({
selectedItem: null
});
export default myStore;
main.js
import { createApp } from 'vue';
import App from './app.vue';
import myStore from './store';
const myApp = createApp(App);
myApp.config.globalProperties.$store = myStore;
myApp.mount('#app');
component.vue
<template>
<div>
<div v-if="item">You have selected an item</div>
<div v-else>Please select an item</div>
<button class="btn btn-primary" #click="generateItem">Generate Item</button>
</div>
</template>
<script>
export default {
props: {
item: Object
},
watch: {
item: function(newValue, oldValue) {
alert('The item was updated.');
}
},
methods: {
generateItem() {
const item = {
id:0,
name: 'Some random name'
};
this.$emit('itemSelected', item);
}
}
}
</script>
app.vue
<template>
<component :item="selectedItem" #item-selected="onItemSelected" />
</template>
<script>
import Component form './component.vue';
export default {
components: {
'component': Component
},
data() {
return {
...this.$store
}
},
methods: {
onItemSelected(item) {
console.log('onItemSelected: ');
console.log(item);
this.$store.selectedItem = item;
}
}
}
</script>
The idea is that the app manages state via a reactive object. The object is passed into the component via a property. The component can then update the value of the object when a user clicks the "Generate Item" button.
I can see that the selectedValue is successfully passed down as a property. I have confirmed this by manually setting selectedValue to a dummy value to test. I can also see that the onItemSelected event handler works as expected. This means that events are successfully flowing up. However, when the selectedItem is updated in the event handler, the updated value is not getting passed back down to the component.
What am I doing wrong?
$store.selectedItem stops being reactive here, because it's read once in data:
data() {
return {
...this.$store
}
}
In order for it to stay reactive, it should be either converted to a ref:
data() {
return {
selectedItem: toRef(this.$store, 'selectedItem')
}
}
Or be a computed:
computed: {
selectedItem() {
return this.$store.selectedItem
}
}

Add a class to a nabar modal in a component Vue.js

I wanted to know how can I add a class to a modal in a navbar components? My navbar is in App.vue and I wanted to create a message that would add the class "is-active" to a modal in my navbar when I click on it. But I can't find the way to do that..
Thank you
Usually when you have a parent -> child relationship you can use events. In this case since you have two components that are not linked (directly) then you have two alternatives.
Using store (it is usually used in cases where your application is of a considerate size)
You can use vuex to have a central place where you will have your global state. A simple example would be:
store/main.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
isModalOpen: false
},
getters: {
isModalOpen => (state) => state.isModalOpen,
},
mutations: {
setIsModalOpen (state, isOpen) {
state.isModalOpen = isOpen;
}
}
})
then you can access the store in your component as such:
<template>
<navbar :class="[isNavBarOpen ? "is-active" : ""]" />
</template>
export default {
computed: {
isNavBarOpen () {
this.$store.getters['isModalOpen']
}
}
}
Event bus (it is usually used in cases where you have a small app and do not need a global state manager)
Read more about EventBus here.
You can create a simple EventBus
services/eventBus.js
import Vue from 'vue';
const export EventBus = new Vue();
then on your component when the modal is open you can do:
// # -> is an alias to your root folder. Most projects scafolded by Vue CLI has this by default
import {EventBus} from "#/services/eventBus"
export default {
methods: {
openStore: () => {
// your logic to open modal
EventBus.$emit('modal-open');
}
}
}
then on your App.vue you just listen to this event
App.vue
<template>
<navbar :class="[isModalOpen ? "is-active" : ""]" />
</template>
// # -> is an alias to your root folder. Most projects scafolded by Vue CLI has this by default
import {EventBus} from "#/services/eventBus"
export default {
data() {
return {
isModalOpen: false,
}
},
created() {
EventBus.$on('modal-open', this.onModalOpen);
},
methods: {
onModalOpen() {
this.isModalOpen = true;
}
}
}
The one you will pick depends on our application structure and if you think it is complex enough to use a central state management (vuex).
There might contain some errors in the code but the main idea is there.

List isn't dynamically displayed in vue.js

I'm new to vue.js and learning on my own with the vue doc, youtube videos and such. I've been searching for a while and looking at youtube tutorials and haven't found an answer so far, hope you guys will be able to help me.
So here's my issue, I'm building a web app and I need to display a list of objects dynamically but it doesn't show the first time I'm loading that page. I have to go to some other route and come back to see it, so I guess I'm misunderstanding some life cycle or something from that field of expertise...
I'm using the vuex to store and retrieve my data as seen below :
import Vue from 'vue';
const state = {
journees: {},
};
const getters = {
getJourneeList(state) {
return state.journees;
}
};
const mutations = {
GET_LIST(state, journees) {
state.journees = journees;
}
};
const actions = {
getJourneesUser({commit}) {
Vue.axios.get('/journee/')
.then( res => {
commit('GET_LIST', res.data)
})
.catch((err) => console.log(err))
}
};
export default {
state,
getters,
mutations,
actions
};
And then I'm getting it in my vue like this:
<template>
<v-container>
<v-card v-for="heure in heures" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
export default {
name: "TimeList",
data() {
return {
heures: this.$store.getters.getJourneeList,
}
},
created() {
this.$store.dispatch('getJourneesUser');
}
}
</script>
You need to use mapState and use it inside computed value because then computed value will response to change in state. You do not need getter but if you want here is the version with getter. It should be like this if your store module called journees:
without getter
<template>
<v-container>
<v-card v-for="heure in journees" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "TimeList",
computed: {
...mapState(["journees"])
},
created() {
this.$store.dispatch("getJourneesUser");
},
};
</script>
with getter
<template>
<v-container>
<v-card v-for="heure in getJourneeList" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "TimeList",
computed: {
...mapGetters(["getJourneeList"])
},
created() {
this.$store.dispatch("getJourneesUser");
},
};
</script>

How to change vue-cookie-law default Props

I am trying to change buttonText default value of the component vue-cookie-law with Props.
I can change the default value directly from the node_modules plugin source code, but I would like to change it from a Vue Single File Component.
vue-cookie-law - https://www.npmjs.com/package/vue-cookie-law
prop default type
buttonText: 'Got It!'
Since I haven't used Props before, I've been trying few things, below is my CookieLaw.vue component
<template>
<footer>
<cookie-law theme="base">
<div slot="message">
We use cookies to enhance your experience. By continuing to visit our site you agree to our use of cookies.
<router-link to="terms_and_conditions">View Policy</router-link>
</div>
</cookie-law>
</footer>
</template>
<script>
import CookieLaw from "vue-cookie-law";
export default {
props: {
buttonText: {
default: "Agree"
}
},
components: { CookieLaw }
};
</script>
The props are not changing the default of buttonText.
buttonText is one of the default props for the vue-cookie-law component as you know ... not the parent component (the one that you import it on ) so you have to bind them to the component it self :
<cookie-law theme="base" buttonText="Agree">
...
</cookie-law>
Or bind a dynamic value :
<script>
import CookieLaw from "vue-cookie-law";
export default {
data() {
return {
text: 'Agree'
}
}
components: {
CookieLaw
}
}; <
</script>
<cookie-law theme="base" :buttonText="text">
...
</cookie-law>

Vuejs: call created() hook twice

I created a hook to reload my data from the database on a button click:
<template>
<base-projects :projects="projects" />
</template>
<script>
import { mapGetters } from 'vuex';
import Projects from './Projects';
import projectService from '#/services/projectService';
export default {
components: { Projects },
computed: {
...mapGetters([
'projects'
])
},
created() {
projectService.getAllCompanyProjects();
},
};
</script>
So that works fine, but only if I click the first time. If I click a second time, it doesn't reload the data a second time. Does anyone know how to fix that issue?
Thank you in advance!
I assume that your data are reloaded from your database using projectService.getAllCompanyProjects(); function. Since you want to reload you data on "click" I suggest you to bind the "click" event to one of your component method.
<template>
<base-projects :projects="projects" #click.native="reloadData" />
</template>
<script>
import { mapGetters } from 'vuex';
import Projects from './Projects';
import projectService from '#/services/projectService';
export default {
components: { Projects },
computed: {
...mapGetters([
'projects'
])
},
methods: {
reloadData() {
projectService.getAllCompanyProjects();
}
}
};
</script>
The reloadData method will be triggered by a "click" on the DOM of your base-projects component.

Categories

Resources