How to pass store values in components - vuejs2

I want to pass values in the store.state in components and got an error:
[Vue warn]: Error in render: "TypeError: Cannot read property 'state' of undefined"
So I called the value in the component directly and it doesn't work.
const store = new Vuex.Store({
state: {
filter: {
selected: false,
value: 'test'
}
},
});
new Vue({
el: '#app',
template: `<div id="app"><div :selected="this.$store.state.filter.selected">Option</div></div>`
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex#3.0.1/dist/vuex.js"></script>
<div id="app"></div>
Vue
const store = new Vuex.Store({
state: {
filter: {
value: 'test',
selected: true
}
},
mutations: {},
actions: {},
getters: {}
});
HTML
<option :selected="store.state.filter.selected">{{store.state.filter.value}}</option>
Can I use it this way, or I need to think differently and how? Thanks.
My current solution is store the value in computed variables, but would there be a way to pass Vuex store values into a component directly?
computed: {
filter () {
return storeLogs.state.filter;
}
}
<option :selected="filter.selected">{{filter.value}}</option>

Two things:
You must pass your store object to the Vue constructor
There's no need to reference "this" in your template
const store = new Vuex.Store({
state: {
filter: {
selected: false,
value: 'test'
}
},
});
new Vue({
store, // add store object to Vue
el: '#app',
template: `<div id="app"><div selected="$store.state.filter.selected">Option</div></div>`
});
You can also use mapState like this in your .vue file:
<!-- MyComponent.vue -->
<template>
<option :selected="filter.selected">{{filter.value}}</option>
</template>
<script>
import { mapState } from 'vuex';
export default {
name : 'MyComponent',
computed() {
...mapState(['filter'])
}
}
</script>

Related

Vuex uncaught TypeError: Cannot read property 'commit' of undefined in .vue and .js file

This is my first time learning to use vuex and I'm building a simple authorization with hardcoded value. I want to redirect user to Products.vue if the entered password in Confirm.vue matches.
Products.vue will contain the secured content that the user cannot access before entering the correct password.
The result keeps on giving error of
uncaught TypeError: Cannot read property 'commit' of undefined in my Confirm.vue file
Confirm.vue
<template>
<div>
<p>Please enter password</p>
<input
type="password"
name="password"
v-model="input.password"
placeholder="Password"
/>
<button type="button" v-on:click="login()">Go to admin</button>
</div>
</template>
<script>
export default {
name: "Confirm",
data() {
return {
input: {
password: "",
},
};
},
methods: {
login() {
if (this.input.password == "123") {
this.$store.commit("setAuthentication, true");
this.$router.replace({ name: "products" });
} else {
console.log("incorect password");
}
},
},
};
</script>
So my assumption is that $store is not defined, but I already set the constant for it in my Main.js and also created a new Vue.
Main.js
import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
import VueRouter from "vue-router";
import Confirm from "./pages/admin/Confirm.vue";
import Products from "./pages/admin/Products.vue";
Vue.config.productionTip = false;
Vue.use(VueRouter);
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
authenticated: false,
},
mutations: {
setAuthentication(state, status) {
state.authenticated = status;
},
},
});
const router = new VueRouter({
routes: [
{
path: "/",
redirect: {
name: "main",
},
},
{
path: "/admin/confirm",
name: "confirm",
component: Confirm,
},
{
path: "/admin/products",
name: "products",
component: Products,
},
],
});
new Vue({
render: (h) => h(App),
router: router,
store: store,
}).$mount("#app");
Lastly, this is the content of App.vue
App.vue
<template>
<router-view />
</template>
<script>
export default {
name: "app",
};
</script>
You are calling the mutation wrong.
You are using this syntax -
this.$store.commit("setAuthentication, true");
Instead, you should use this
this.$store.commit("setAuthentication", true);

Passing data-attribute from Vue instance html tag

Is it possible to declare and pass a data-attribute value from a html tag of the Vue instance, and then have it available in the data object?
index.html:
<div id="app" data-title="My app title"></div>
App.vue:
data () {
return {
appTitle: // whatever is declared in data-title
}
}
This code works for me:
index.html:
<div id="app" data-id="123"></div>
index.js:
(function (el) {
new Vue({
el,
render: h => h(Module),
data: () => Object.assign({}, el.dataset) ,
});
})(document.getElementById('app'));
Module.vue:
export default {
name: 'Module',
data() {
return {
id: this.$parent.id,
};
},
};
Yes it is:
data () {
return {
appTitle: document.getElementById('app').dataset.title
}
}
However, it is possible that the DOM is not available on component initialization. So you should probably put that code into the mounted hook of your component:
<script>
export default {
data () {
return {
appTitle: null
}
},
mounted () {
this.appTitle = document.getElementById('app').dataset.title
}
}
</script>
Here's a different approach that doesn't rely on the DOM API, but cannot be used to get data-attributes from the root (#app) element:
{
el: '#app',
template: `
<div ref="mydiv" data-attribute="data attribute">
Hello from template
<div>
Hello from {{attribute}}
</div>
</div>`,
data(){
return {
attribute: ''
}
},
mounted(){
this.$data.attribute = this.$refs.mydiv.dataset.attribute;
}
});
Here's a pen with a working example

Access module from Vuex Store

I have the following module:
export const ProfileData = {
state: {
ajaxData: null;
},
getters: {/*getters here*/},
mutations: {/*mutations here*/},
actions: {/*actions here*/}
}
and this module is registered in my global store:
import {ProfileData} from './store/modules/ProfileData.es6'
const store = new Vuex.Store({
modules: {
ProfileData: ProfileData
}
});
I have also used the Vue.use(Vuex) and set the store in new Vue({ store: store}) properly. However, when I try to access the ajaxData belonging to the ProfileData module, in one of my components via this.$store.ProfileData.ajaxData, the console shows an undefined error. The same goes for reading the this.$store.ProfileData or this.$store.ajaxData, while this.$store is defined and I am already able to read it. I also see the ProfileData object added to the _modules property of the store in browser's console.
What is that I am doing wrong to access the modules registered to the Vuex? How can I access those?
Directly accessing state of Vuex module
The format to access a Module's local state is $store.state.moduleName.propertyFromState.
So you would use:
this.$store.state.ProfileData.ajaxData
Demo:
const ProfileData = {
state: {ajaxData: "foo"}
}
const store = new Vuex.Store({
strict: true,
modules: {
ProfileData
}
});
new Vue({
store,
el: '#app',
mounted: function() {
console.log(this.$store.state.ProfileData.ajaxData)
}
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>ajaxData: {{ $store.state.ProfileData.ajaxData }}</p>
</div>
Getters, Actions and Mutators of modules, how to directly access them?
It depends if they are namespaced or not. See demo (explanation in comments):
const ProfileDataWithoutNamespace = {
state: {ajaxData1: "foo1"},
getters: {getterFromProfileDataWithoutNamespace: (state) => state.ajaxData1}
}
const ProfileDataWithNamespace = {
namespaced: true,
state: {ajaxData2: "foo2"},
getters: {getterFromProfileDataWithNamespace: (state) => state.ajaxData2}
}
const store = new Vuex.Store({
strict: true,
modules: {
ProfileDataWithoutNamespace,
ProfileDataWithNamespace
}
});
new Vue({
store,
el: '#app',
mounted: function() {
// state is always per module
console.log(this.$store.state.ProfileDataWithoutNamespace.ajaxData1)
console.log(this.$store.state.ProfileDataWithNamespace.ajaxData2)
// getters, actions and mutations depends if namespace is true or not
// if namespace is absent or false, they are added with their original name
console.log(this.$store.getters['getterFromProfileDataWithoutNamespace'])
// if namespace is true, they are added with Namespace/ prefix
console.log(this.$store.getters['ProfileDataWithNamespace/getterFromProfileDataWithNamespace'])
}
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>Check the console.</p>
</div>
I see you added the module using key:value, and the key to access your module is Profile. Try to call your module using it, or define the module setting directly, without Profile key:
modules: {
ProfileData
}

VueJs Nested props coming through undefined

I am trying to access an array which is part of a prop (event) passed into a component, but when in created() or mounted() the array part of the event prop (the rest is fine) comes through as undefined.
As can be seen below, when I inspect the props in the vue chrome plugin, the registration_fields are there.
I can add a watcher to the event prop and can access the registration_fields that way, but this seems very awkward to have to do this to access already passed in data.
This is from the Chrome vue inspector:
event:Object
address1_field:"Some Address 1"
address2_field:"Some Address 2"
approved:true
registration_fields:Array[1]
This is what part of my vue file looks like:
export default {
props: ['event'],
data() {
return {
regFields: []
}
},
created() {
this.regFields = this.event.registration_fields // Undefined here!
},
watch: {
event() {
this.regFields = this.event.registration_fields //Can access it here
});
}
}
}
I am using Vue 2.4.4
This is how the component is called:
<template>
<tickets v-if="event" :event="event"></tickets>
</template>
<script>
import tickets from './main_booking/tickets.vue'
export default {
created() {
var self = this;
this.$http.get('events/123').then(response => {
self.event = response.data
}).catch(e => {
alert('Error here!');
})
},
data: function () {
return {event: {}}
},
components: {
tickets: tickets
}
}
</script>
Thank you
It actually works fine without the watcher.
new Vue({
el: '#app',
data: {
event: undefined
},
components: {
subC: {
props: ['event'],
data() {
return {
regFields: []
}
},
created() {
this.regFields = this.event.registration_fields // Undefined here!
}
}
},
mounted() {
setTimeout(() => {
this.event = {
registration_fields: [1, 3]
};
}, 800);
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<sub-c v-if="event" :event="event" inline-template>
<div>
{{regFields}}
</div>
</sub-c>
</div>
If, as Belmin Bedak suggests in the comment below, event is populated asynchronously, it comes in as undefined because it's undefined. In that case, you need a watcher, or, somewhat more elegantly, use a computed:
new Vue({
el: '#app',
data: {
event: {}
},
components: {
subC: {
props: ['event'],
computed: {
regFields() {
return this.event.registration_fields;
}
}
}
},
// delay proper population
mounted() {
setTimeout(() => { this.event = {registration_fields: [1,2,3]}; }, 800);
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<sub-c :event="event" inline-template>
<div>
{{regFields}}
</div>
</sub-c>
</div>

How to pass property to component used in main.js from other components in vuejs

Basically I want to a loadingbar component globally (included in app template)
Here is my loadingbar component
<template>
<div class="loadingbar" v-if="isLoading">
Loading ...
</div>
</template>
<script>
export default {
name: 'loadingbar',
props: ['isLoading'],
data () {
return {
}
}
}
</script>
<style scoped>
</style>
and in main.js, I have included this component as
import LoadingBar from './components/LoadingBar.vue';
new Vue({
router,
data () {
return {
isLoading: true
};
},
methods: {
},
created: function () {
},
components: {
LoadingBar
},
template: `
<div id="app">
<LoadingBar :isLoading="isLoading"/>
<router-view></router-view>
</div>
`
}).$mount('#app');
My aim is to show loading component based upon the value of variable isLoading. The above code working fine. But I want to use set isLoading variable from other component (so that to decide whether to show loading component). Eg. In post components
<template>
<div class="post container">
</div>
</template>
<script>
export default {
name: 'post',
data () {
return {
posts: []
}
},
methods: {
fetchPosts: function() {
// to show loading bar
this.isLoading = true;
this.$http.get(APIURL+'listpost')
.then(function(response) {
// to hide loading bar
this.isLoading = false;
console.log("content loaded");
});
}
},
created: function() {
this.fetchPosts();
}
}
</script>
<style scoped>
</style>
Of coarse we can't access isLoading directly from main.js so i decided to use Mixin so i put following code in main.js
Vue.mixin({
data: function () {
return {
isLoading: false
};
}
});
This however allow me to access isLoading from any other component but I can't modify this variable. Can any help me to achieve this?
Note: I know i can achieve this by including loadingbar in individual component (I tried that and it was working fine, But i do not want to do that as loadingbar is needed in every component so i was including in main template/component)
You could use Vuex like so:
// main.js
import Vuex from 'vuex'
let store = new Vuex.Store({
state: {
isLoading: false,
},
mutations: {
SET_IS_LOADING(state, value) {
state.isLoading = value;
}
},
getters: {
isLoading(state) {
return state.isLoading;
}
}
})
import LoadingBar from './components/LoadingBar.vue';
new Vue({
router,
store, // notice you need to add the `store` var here
components: {
LoadingBar
},
template: `
<div id="app">
<LoadingBar :isLoading="$store.getters.isLoading"/>
<router-view></router-view>
</div>
`
}).$mount('#app');
// script of any child component
methods: {
fetchPosts: function() {
// to show loading bar
this.$store.commit('SET_IS_LOADING', true);
this.$http.get(APIURL+'listpost')
.then(function(response) {
// to hide loading bar
this.$store.commit('SET_IS_LOADING', false);
console.log("content loaded");
});
}
},