Vuejs: call created() hook twice - javascript

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.

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

Store data and component data isn't the same (vuex + persisted state)

Here is my post.js in store:
import axios from 'axios'
import createPersistedState from "vuex-persistedstate"
export default {
namespaced: true,
state: {
sample_data: 'Welcome!!',
logged_in: false,
},
getters: {
getSampleData: state => state.sample_data
},
mutations: {
},
actions: {
},
}
And in my component:
<template>
<div class="home">
<p>{{ sample_data }}</p>
<p>{{ logged_in }}</p>
</div>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex'
export default {
name: 'Home',
components: {
},
data() {
return {
msg: 'Welcome'
}
},
methods: {
},
computed: {
...mapState('post', ['sample_data', 'logged_in'])
}
}
</script>
I have installed the Vue extension in Chrome.
Now when I change the sample_data in DevTools > Vue > Vuex from "welcome" to "welcome 2222", it will exactly say "welcome 2222" on the page. Now when I hit refresh, the page goes back in saying "welcome" but in the Vuex panel it says the sample_data: welcome 2222. I tried refreshing the Vue panel but it will still say sample_data: welcome 2222 so the persisted state must be working. Why is my page though displays the old "welcome" and not the "welcome 2222"?
Thanks!
UPDATE
I tried putting this:
mutations: {
setSampleData(state, payload) {
state.sample_data = payload
}
},
actions: {
setSampleData({commit}, payload) {
commit('setSampleData', payload)
}
},
And in my component:
<button #click="setSampleData('Hello Hero')">Get Data</button>
Now it changed the state to "Hello Hero" even if I refresh the page. It seems to be working. However again, in the Vuex panel, it will still say something like "welcome 2222".
If I change the data in the Vuex panel to "hi there", the page will say "hi there". But when I refresh, it will display "Hello Hero" again and in the Vuex panel it says "hi there". Seems there's a problem between the two?
UPDATE 2
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import post from './post'
import createPersistedState from "vuex-persistedstate"
import * as Cookies from 'js-cookie'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
post
},
plugins: [createPersistedState()],
})
It seems you didn't meet this requirement from the package documentation:
New plugin instances can be created in separate files, but must be imported and added to plugins object in the main Vuex file

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>

Vue control and update state from another component with vuex

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

Eventbus in Vue.js isn't updating my component

I have two components and I want to display what the user enters in one on the other component. I don't really want to use a state manager like vuex because it's probably a bit overkill as it's a small application
this is my main.js:
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router';
import { routes }from './routes';
export const EventBus = new Vue();
Vue.use(VueRouter);
const router = new VueRouter({
routes,
mode: 'history'
});
new Vue({
el: '#app',
router,
render: h => h(App)
})
Component that emits the event called addHtml.vue
<template>
<div>
<h1>Add HTML</h1>
<hr>
<button #click="navigateToHome" class="btn btn-primary">Go to Library</button>
<hr>
Title <input type="text" v-model="title">
<button #click="emitGlobalClickEvent()">Press me</button>
</div>
</template>
<script>
import { EventBus } from '../../main.js'
export default {
data: function () {
return {
title: ''
}
},
methods: {
navigateToHome() {
this.$router.push('/');
},
emitGlobalClickEvent() {
console.log(this.title);
EventBus.$emit('titleChanged', this.title);
}
}
}
</script>
the file that listens for the event thats emitted and to display what was entered on the other component:
<template>
<div>
<h1>Existing Items</h1>
<hr>
<p>{{ test }}</p>
</div>
</template>
<script>
import { EventBus } from '../main.js';
export default {
data: function () {
return {
test: ''
}
},
created() {
EventBus.$on('titleChanged', (data) => {
console.log('in here!',data);
this.test = data;
});
}
}
</script>
the console.log('in here!',data); inside the listener gets printed out to the console so I know it's picking it up however {{ test }} doesn't get updated to what the user enters when I click back onto the component to view if it was updated, it just remains blank? Any Ideas?
If you are using vue-router to display the secound component. The reason might be that you just see a new instance of that component everytime and the value of test will be reseted when the component is destroyed. You can bind it to a (global) variable to persist test: window.yourApp.test. Maybe webpack will mourn but it is possible. Even eslint has an ignore comment for such cases.
It is like Reiner said, because the component gets destroyed once you switch pages. Try wrapping your router-view inside a keep-alive:
<keep-alive>
<router-view><router-view>
</keep-alive>
Also. If you want to keep the state of just one specific component / page you can use the include tag:
<keep-alive include='name of component'>
<router-view></router-view>
</keep-alive>

Categories

Resources