This Is My Global state and shared component I set and defined getters here
export default {
getters: {
loading(state) {
return state.loading;
},
statusMessage(state) {
return state.statusMessage;
},
},
}
The store export shared modules here
import Vue from 'vue'
import Vuex from 'vuex'
import shared from './shared/index.js'
Vue.use(Vuex)
export const store = new Vuex.Store({
modules:{
shared: shared,
}
});
The registration template component here i want to access loading and error message but actually the problem of mine is when the page rendering I found this errors in console
in render: "TypeError: Cannot read property 'getters' of undefined"
found in
---> <Registrations> at src/views/Registrations.vue
<VApp>
<App> at src/App.vue
<Root>
computed: {
loading (){
return this.$store.getters.loading
},
error(){
return this.$store.getters.statusMessage
}
}
getters are defined above as I Shown and export as well but I not able to find the reason what's the reason and problem there!!! help me If You can
try this.$store.shared.getters.loading - because you are using a module getter
Related
I have a vue2 project that uses ClassComponents and Chart.js (via vue-chart-3). I now have a simple component that wraps a DoughnutChart to manage data and stuff.
DBOverviewDoughnut.vue
<template>
<div>
<p>test</p>
<DoughnutChart ref="doughnutRef" :chartData="sharesData"></DoughnutChart>
</div>
</template>
<script lang="ts">
import Component from 'vue-class-component';
import Vue from 'vue';
import { DoughnutChart, ExtractComponentData } from 'vue-chart-3';
import { Prop, Ref } from 'vue-property-decorator';
import { ChartData } from 'chart.js';
#Component({ components: { DoughnutChart } })
export default class DBOverviewDoughnut extends Vue {
#Prop()
private sharesData!: ChartData<'doughnut'>;
#Ref()
private readonly doughnutRef!: ExtractComponentData<typeof DoughnutChart>;
created(): void {
this.assignColors();
}
mounted(): void {
if (this.doughnutRef.chartInstance) {
console.log(this.doughnutRef.chartInstance.data);
}
}
assignColors(): void {
this.sharesData.datasets[0].backgroundColor = [
'#77CEFF',
'#0079AF',
'#123E6B',
'#97B0C4',
'#A5C8ED',
];
}
}
</script>
Starting the program it will work fine and I can access the chartInstance inside the mounted hook.
But now I want to unit test my component. I thought on setting the propsData which will be the input data for the chart.
DBOverviewDoughnut.spec.ts
import DBOverviewDoughnut from '#/components/dashboard/DBOverviewDoughnut.vue';
import { mount, Wrapper } from '#vue/test-utils';
import { Share } from '#/Share';
describe('DBOverviewDoughnut', () => {
let cut: Wrapper<DBOverviewDoughnut>;
it('should render the correct amount of sections', () => {
cut = mount(DBOverviewDoughnut, {
propsData: {
sharesData: {
labels: ['TestShare1', 'TestShare2', 'TestShare3'],
datasets: [{ data: [11, 22, 33] }]
}
}
});
const chart = cut.findComponent({ ref: 'doughnutRef' });
console.log(chart);
});
});
Using shallowMount() doesn't seem to work, because I only get this from logging (no chartInstance and its properties as in the production code):
VueWrapper {
isFunctionalComponent: undefined,
_emitted: [Object: null prototype] {},
_emittedByOrder: [],
selector: { ref: 'doughnutRef' }
}
So I thought maybe I have to mount the component because the DoughnutChart is also a wrapper around the Chart.js charts. But when using mount() I get the following error:
console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
[Vue warn]: `createElement()` has been called outside of render function.
console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
[Vue warn]: Error in render: "Error: [vue-composition-api] must call Vue.use(VueCompositionAPI) before using any function."
found in
---> <DoughnutChart>
<DBOverviewDoughnut>
<Root>
I don't really know what I'm doing wrong. I registered the VueCompostionAPI in my main.ts:
import Vue from 'vue';
import { Chart, registerables } from 'chart.js';
import App from './App.vue';
import router from './router';
import store from './store';
import VueCompositionAPI from '#vue/composition-api';
Chart.register(...registerables);
Vue.use(VueCompositionAPI);
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
Following this post doesn't solve the problem either.
Anyone got an idea what's going wrong? I'm a bit confused if the error has to do with my test setup or with the installation of chart.js or compositionApi.
You need to use VueCompositionAPI inside your spec as well when you mount the component. You can do this by creating a local Vue instance inside your spec, adding VueCompositionAPI as a plugin to the instance and using the instance when you mount the component. https://vue-test-utils.vuejs.org/api/options.html#localvue
Using localVue is really what I should have thought about. This and installing the canvas-package works, that I get additional information about my Ref-Element. However I still have to figure out what to do with it.
#AdriHM I want to test if the rendered chat gets the correct data I guess. Or if it displays it correctly (e.g. display the correct amount of sections) But the longer I think about it the less I'm sure it's the right thing to test. I don't want to test the Chart.js API though.
I want to build an application with vue3 and vuex. I have got an error when I use $store (Uncaught (in promise) TypeError: Cannot read property '$store' of undefined). But I did not find anything about that. Could you help me?
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import { store } from "./store";
createApp(App)
.use(store)
.use(router)
.mount("#app");
main.js
<template>
{{ posts }}
<button #click="clickOnButton">Tıkla</button>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
},
computed: {
posts: () => this.$store.state.post
},
methods: {
clickOnButton: () => {
this.$store.commit("getPost");
}
}
};
</script>
component
The problem has nothing to do with your store. You are using an arrow function when you should be using the standard method syntax:
// ...
methods: {
clickOnButton() {
// this will now point to your component.
this.$store.commit('getPost');
}
}
// ...
The foremost limitation of arrow functions is that they are totally agnostic of context: this is not bound. See the vue documentation
and the arrow functions documentation:
An arrow function expression is a compact alternative to a traditional function expression, but is limited and can't be used in all situations.
Differences & Limitations:
Does not have its own bindings to this or super, and should not be used as methods.
I'm new to Vue, so it's likely I misunderstand something. I want to call a vuex action inside a local function in App.vue like so:
<template>
<div id="app">
<button #click="runFunction(1)">Test</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default{
data() { return { } },
methods: {
...mapActions(['doAction']),
buttonClicked: (input) => { runFunction(input) }
}
}
function runFunction(input){
doAction({ ID: input });
}
</script>
The action calls a mutation in store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
IDs: []
},
mutations: {
doAction: (state, id) => { state.IDs.push(id) }
},
actions: {
doAction: ({ commit }, id) => { commit('doAction', id) }
}
})
I also have a main.js that sets up the vue:
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
el: '#app',
store,
render: h => h(App)
})
The error I'm getting is:
ReferenceError: doAction is not defined
at runFunction
How can I call the mapped action inside a function? Version is Vue 2.6.10
There are several problems with defining runFunction as a 'local function':
function runFunction(input){
doAction({ ID: input });
}
Firstly, this is just a normal JavaScript function and the usual scoping rules apply. doAction would need to be defined somewhere that this function can see it. There is no magic link between this function and the component defined in App.vue. The function will be accessible to code in the component, such as in buttonClicked, but not the other way around.
The next problem is that it won't be available within your template. When you write runTemplate(1) in your template that's going to be looking for this.runTemplate(1), trying to resolve it on the current instance. Your function isn't on the current instance. Given your template includes #click="runFunction(1)" I'm a little surprised you aren't seeing a console error warning that the click handler is undefined.
mapActions accesses the store by using the reference held in this.$store. That reference is created when you add the store to your new Vue({store}). The store may appear to be available by magic but it's really just this.$store, where this is the current component.
It isn't really clear why you're trying to write this function outside of the component. The simplest solution is to add it to the methods. It'll then be available to the template and you can access doAction as this.doAction.
To keep it as a separate function you'd need to give it some sort of access to the store. Without knowing why you want it to be separate in the first place it's unclear how best to achieve that.
Of course it is not defined outside your instance .... you have to import the exported store from store.js on your function component :
<script>
import { mapActions } from 'vuex'
import store from 'store.js'
export default{
data() { return { } },
methods: {
...mapActions(['doAction']),
buttonClicked: (input) => { runFunction(input) }
}
}
function runFunction(input){
store.commit({ ID: input });
}
</script>
I'm a little confused over vuex modules.
I have a Vue component, which I show on a search page:
<template>
<div v-if="filtersPanelActive">
Filters panel
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
...mapState(["filtersPanelActive"])
},
methods: {}
};
</script>
I then have a store.js
import Vue from "vue";
import Vuex from "vuex";
import searchPage from "./modules/searchPage.js";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
artPage,
legalsPage,
searchPage
}
});
and a search page store:
const searchPage = {
state: {
filtersPanelActive: false
},
actions: {},
mutations: {
toggleFilters(state) {
state.filtersPanelActive = !state.filtersPanelActive;
}
},
getters: {}
};
export default searchPage;
But nothing is quite working... In my vuex dev tools I have the following:
{
"artPage":{
"startDate":false,
"endDate":false,
"confirmBooking":false
},
"legalsPage":{
"filesUploaded":[
]
},
"searchPage":{
"filtersPanelActive":false
}
}
But when I inspect the Vue component it shows the following:
Why do I get undefined? I'm slightly overwhelmed by vuex, namespaces, modules etc, so feel free to over explain...
In another component I use
this.$store.commit("toggleFilters");
And its worth noting that that 'works' – as in the vuex state changes from true to false, however my mapState still isn't pulling through anything. So the module seems to be loaded correctly.
2018-06-05 MINOR UPDATE: Updated the mapState code to something slightly simpler.
Two things to add to your code to enable namespaces and to make sure you are referencing the right module.
In your searchPage module, add the namespaced: true property to the export as such:
const searchPage = {
namespaces: true,
state: { ... },
...
}
This enables namespacing for the module and makes it self contained and reusable.
Then in your search page component change your mapState to this:
computed: {
...mapState('searchPage', [ 'filtersPanelActive' ])
}
This will map the component variable filtersPanelActive to your searchPage module state variable filtersPanelActive. Your v-if="filtersPanelActive" line in your template should work now.
Let me know if this fixes things for you and if it doesn't please let me know and I will update my answer.
I'm using a project with Vue, Vuetify, Vue-Router, and Vuex. The intent was to create a basic layout with a sidebar in a more module approach to dabble in scalability with Vue. So I created a folder called Store, which has a modules folder. So my index file within the store folder is as follows:
import Vue from 'vue';
import Vuex from 'vuex';
import global from './Modules/Global';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
site: global
}
});
The module is broken down into a single file with actions, getters, mutations, and state.
const actions = {
sidebarState: ({ commit }, status) => {
commit('openOrCloseSidebar', status);
}
}
const mutations = {
openOrCloseMenu: (status) => {
if (status !== true)
return state.sidebar = true;
return state.sidebar = false;
}
};
const getters = {
};
const state = {
sidebar: true
};
export default {
namespaced: true,
actions,
mutations,
getters,
state
};
I invoke the Vue instance as follows.
import Vue from 'vue/dist/vue';
import Vuetify from 'vuetify';
import Axios from 'axios';
import application from './Template/Application.vue';
import router from './Router';
import store from './Store';
import { sync } from 'vuex-router-sync';
Vue.use(Vuetify);
Vue.use(router);
Vue.use(store);
sync(store, router);
var vue = new Vue({
el: '#application',
template: '<application></application>',
components: {
application
},
router: router,
store: store
});
However, when I call this.$store.global.state.sidebar or this.$store.state.sidebar Vue is unable to find my property. I receive the error:
Cannot read property global of undefined.
The error also references state, but I believe since I'm using namespace the syntax should mirror above. Where I attempt to call that is here.
<template>
<v-container>
<application_sidebar :my-prop="menu"></application_sidebar>
<application_navigation :my-prop="menu"></application_navigation>
</v-container>
</template>
<script type="text/javascript">
import application_navigation from './Navigation.vue'
import application_sidebar from './Sidebar.vue';
import { mapState } from 'vuex';
export default ({
components: {
application_navigation,
application_sidebar
},
data: {
menu: this.$store.global.state.sidebar
}
});
</script>
I'm trying to access my state and learn how to correctly emit, so in the navigation component I can emit upward so the value is reflected to move the sidebar open or close.
Any help would be terrific, I'm quite new to Vue.
I think the main problem is your path to your module state is meant to be this.$store.state.site.
The recommended method is to use computed properties. For example
computed: {
menu() {
return this.$store.state.site.sidebar
}
}
You can also use the mapState helper
import { mapState } from 'vuex'
export default {
computed: mapState({ menu: state => state.site.sidebar })
}
The this variable does not reference the Vue instance when you are trying to access the store via this.$store.
The data object needs to be a method that returns an object.
data() {
return { menu: this.$store.state.site.sidebar };
}
However, by retrieving the value from the store's state object the data method like this, you are only setting the value of the menu data property when the Vue instance initializes. The value of menu will not update in response to changes to the value in the store's state.
If you need the menu value to be reflective of the state object throughout the life of the Vue instance, then you'd need to use a computed property or mapState, as suggested in #Phil's answer.