Uncaught Error: [vuex] getters should be function but "getters.products" in module "prods" is [] - vue.js

I've reduced this code to the bare minimal, but I'm still not sure where this problem is coming from, but this is the first sentence of the error:
Uncaught Error: [vuex] getters should be function but "getters.products" in module "prods" is []
This is my main.js:
import { createApp } from 'vue';
import router from './router.js';
import storage from './store.js';
import App from './App.vue';
const app = createApp(App);
app.use(router);
app.use(storage);
app.mount('#app');
This is my router.js:
import { createRouter, createWebHistory } from 'vue-router';
import ProductsList from './ProductsList.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', redirect: '/products' },
{ path: '/products', component: ProductsList },
]
});
export default router;
This is my store.js:
import { createStore } from 'vuex';
// import productsModule from './products.js';
const products = createStore({
namespaced: true,
state() {
return {
allProducts: [
{
id: 'p1',
image: "",
title: 'Books',
description: 'Books collection.',
price: 20
},
]
};
},
getters: {
products(state) {
return state.allProducts;
}
}
})
const store = createStore({
modules: {
prods: products,
},
});
export default store;
This is my App.vue minus the style:
<template>
<the-header></the-header>
<router-view></router-view>
</template>
<script>
import TheHeader from './TheHeader.vue';
export default {
components: {
TheHeader
},
}
</script>
This is my ProductsList.vue minus the style:
<template>
<section>
<ul>
<product-item
v-for="prod in products"
:key="prod.id"
:id="prod.id"
:title="prod.title"
:image="prod.image"
:description="prod.description"
:price="prod.price"
></product-item>
</ul>
</section>
</template>
<script>
import ProductItem from './ProductItem.vue';
export default {
// inject: ['products'],
components: {
ProductItem,
},
computed: {
products() {
return this.$store.getters['prods/products'];
}
}
};
</script>
And this is my ProductItem.vue minus the style:
<template>
<li class="product">
<div class="product__data">
<div class="product__image">
<img :src="image" :alt="title" />
</div>
<div class="product__text">
<h3>{{ title }}</h3>
<h4>${{ price }}</h4>
<p>{{ description }}</p>
</div>
</div>
<div class="product__actions">
<button>Add to Cart</button>
</div>
</li>
</template>
<script>
export default {
props: ['id', 'image', 'title', 'price', 'description'],
};
</script>
My code including the styles and the workable products.js can be found at:
https://github.com/maxloosmu/vue-complete/tree/main/15/vuex-0012/src
Could someone help point me to why this way of using Vuex getters is wrong? Or is it due to another problem with Vuex?

I've discovered an answer to my question. In my store.js, I do not use createStore twice, but just once. That will resolve the problem:
import { createStore } from 'vuex';
const products = {
namespaced: true,
state() {
return {
allProducts: [
{
id: 'p1',
image: "",
title: 'Books',
description: 'Books collection.',
price: 20
},
]
};
},
getters: {
products(state) {
return state.allProducts;
}
}
}
const store = createStore({
modules: {
prods: products,
},
});
export default store;

Related

Attribute patch in pinia store is working from one component, but not from another (Nuxt.js)

I've those two components using Nuxt 3.
The setActive method in component 1 changes the state of activeColor, but the cancelEdit method in component 2 does not.
Any idea why this is the case?
Component 1
Here the setActive method changes activeColor:
<template>
<div class="f-button" #click="addColor">+ Add Color</div>
{{ activeColor }}
<div class="f-colors">
<Color v-for="color in colors" :color="color" :edit="activeColor === color" #click="setActive(color)"/>
</div>
</template>
<script>
import {useProjectStore} from "~/stores/projects";
import {storeToRefs} from "pinia";
import Color from "~/components/Color.vue";
export default defineComponent({
name: "ColorManagement",
components: {Color},
setup() {
const projectStore = useProjectStore()
const { getColors, getActiveColor } = storeToRefs(projectStore);
return {
projectStore,
colors: getColors,
activeColor: getActiveColor
};
},
methods: {
addColor() {
...
},
setActive(color) {
this.projectStore.$patch({ activeColor: color })
}
}
});
</script>
Component 2
Here the cancelEdit method doesn't change activeColor:
<div class="f-color">
<div class="f-color__actions" v-if="edit">
<div class="f-color__action" #click="cancelEdit">
<Cancel /><span>Cancel</span>
</div>
</div>
</div>
</template>
<script>
import Cancel from "~/components/icons/Cancel.vue";
import {useProjectStore} from "~/stores/projects";
import {storeToRefs} from "pinia";
export default defineComponent({
name: "Color",
components: {Cancel},
props: ["color","edit"],
setup() {
const projectStore = useProjectStore()
const { activeColor } = storeToRefs(projectStore);
return {
projectStore,
activeColor
};
},
methods: {
cancelEdit() {
this.projectStore.$patch({ activeColor: false })
}
}
});
</script>
nuxt.config.ts
export default defineNuxtConfig({
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '#use "#/assets/styles/_styles.scss" as *;'
}
}
}
},
modules: ['#pinia/nuxt']
})
Store
import { defineStore } from "pinia";
export const useProjectStore = defineStore({
id: 'project',
state: () => {
return {
project: {
colors: [{ color: false, name: '' }]
},
activeColor: null
}
},
getters: {
getColors(state){
return state.project.colors || [];
},
getActiveColor(state){
return state.activeColor;
}
}
});
Ok, if I got this correctly, the deal is this:
Your so called Component 2 is the <Color ... component being used in Component 1, right?
When you trigger cancelEdit inside Component 2 (aka Color) you are also triggering the logic from setActive due to this <Color ...#click="setActive(color)"...so your activeColor is set to false (from the cancelEdit method) but right after it is set to active again, got it?
To fix this (if you don't want to change your HTML structure) you can use events stopPropagation method inside the cancelEdit:
cancelEdit(e) {
e.stopPropagation()
this.projectStore.$patch({ activeColor: false })
}
Event.stopPropagation() reference

Property or method "navigationLinks" is not defined on the instance but referenced during render. Make sure that this property is reactive

Am stack in calling data from vuex module to component.
Am calling Navigation links where I have created a module navigation.js. I have imported the module in the main store.
Am using the getters to call the data in the component.
My navigation component looks like this
<template>
<nav>
<ul>
<li v-for="link in navigationLinks" :key="link.label">
<router-link :to="link.route">{{ link.label }}</router-link>
</li>
</ul>
</nav>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import store from '#/store';
import navigation from '#/store/navigation';
export default {
name: 'Navbar',
data: {
},
computed: {
...mapGetters(['navigationLinks'])
}
}
In my main store.js
import Vue from 'vue'
import Vuex from 'vuex'
import auth from './auth'
import navigation from './navigation'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
auth,
navigation
}
})
In my navigation.js I have
const state = {
links: [
{ label: 'Home', route: '/', icon: 'home' },
{ label: 'About', route: '/about', icon: 'info' },
{ label: 'Contact', route: '/contact', icon: 'envelope' },
]
}
const getters = {
navigationLinks: state => state.links
}
export default {
state,
getters
}

Vue3 with vuetify alpha

I am trying to install the latest version of Vuetify 3 with Vue 3 but I am getting an error at run time. I am upgrading from Vue2 to Vue3. It doesn't appear I am integrating Vuetify correctly. I have spent all labor day trying to figure this one out.
Uncaught TypeError: Cannot read properties of undefined (reading 'lgAndUp')
It's coming from my Vue component:
<template>
<div
:class="{
'mx-5 px-5 pt-5': $vuetify.breakpoint.lgAndUp,
'py-5 my-3 px-0': $vuetify.breakpoint.mdAndDown
}"
id="login_router_wrapper"
>
<router-view id="login_router"></router-view>
</div>
</template>
<script>
import { mapState, mapActions, mapMutations } from 'vuex';
export default {
props: ['errors'],
data() {
return {};
},
async created() {},
destroyed() {},
computed: {},
methods: {}
};
</script>
<style></style>
My main login_app.js
import 'material-design-icons-iconfont/dist/material-design-icons.css';
import 'vuetify/styles' // Global CSS has to be imported
import { createApp } from 'vue';
import store_login from './store/index-login.js';
import router from './router/index-login.js';
import my_vuetify from './vuetify.js';
import AuthRouter from './components/auth/AuthRouter.vue';
import AuthEntrypoint from './components/auth/AuthEntrypoint.vue';
const login_app = createApp({
data() {
return {
message: 'Hello root Component 1'
};
},
components: {
'auth-router': AuthRouter,
'auth-entrypoint': AuthEntrypoint,
},
});
login_app.use(store_login)
.use(router)
.use(my_vuetify)
.mount('#app-login');
My store
import {createStore} from 'vuex';
import auth from './auth/auth.js';
import loader from './util/loader.js';
import error from './util/error.js';
const store_login = createStore({
modules: {
auth,
loader,
error
},
state: {
errors: []
},
mutations: {},
getters: {},
actions: {}
});
export default store_login;
My router
import { createRouter, createWebHistory } from 'vue-router';
import auth from './auth.js';
import AuthRouter from '../components/auth/AuthRouter.vue';
import authResetPassword from './authResetPassword.js';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/login',
props: true,
component: AuthRouter,
children: [...auth]
},
{
path: '/password',
props: true,
component: AuthRouter,
children: [...authResetPassword]
}
]
});
export default router;
Then Vuetify
import {createVuetify} from 'vuetify';
import 'vuetify/styles' // Global CSS has to be imported
const my_vuetify = createVuetify({
iconfont: 'mdi', // 'md' || 'mdi' || 'fa' || 'fa4'
theme: {
options: {
customProperties: true
},
light: true,
themes: {
light: {
primary: '#9E9E9E',
secondary: '#b0bec5',
accent: '#8c9eff',
error: '#b71c1c',
background: '#fafafa'
}
}
}
});
export default my_vuetify;

Missing required prop: "slug" - Vue 3

Im build project on forntend vue and backend django. I stuck with problem what i can't resolv.
I trying to send data from ProjectsView.vue to ProjectDetailView.vue. Im trying to use props but i get error:
[Vue warn]: Missing required prop: "slug"
ProjectsView.vue don't send data to ProjectDetailView.vue and axios throw a error
GET http://127.0.0.1:8000/api/v1/projectdetail/undefined/ 500 (Internal Server Error)
I can't find problem in this code.
this is my ProjectsView:
<template>
<div class="container d-flex d-xl-flex justify-content-xl-center">
<div class="d-flex d-sm-flex d-md-flex d-lg-flex d-xl-flex justify-content-center flex-wrap justify-content-sm-center justify-content-md-center justify-content-lg-center justify-content-xl-center">
<div v-for="prof in projects" v-bind:key="prof.id">
<div class="card" style="width: 285px;height: 400px;margin: 5px;border-radius: 15px;">
<div class="card-body text-center">
<img class="img-fluid" :src="prof.avatar" style="width: 150px;border-width: 1px;border-radius: 100px;" />
<h4 class="card-title">
<router-link
:to="{ name: 'projectdetail', params: { slug: prof.slug } }"
>{{ prof.title }}
</router-link>
<fa v-if="prof.is_online" icon="circle" data-bs-toggle="tooltip" title="Online" style="color: rgb(0,197,67);font-size: 12px;padding: 0px;margin-top: 0px;" /></h4>
<h6 class="text-muted card-subtitle mb-2">{{ prof.about }}</h6>
<h6 class="text-muted card-subtitle mb-2">{{ prof.last_online_at }}</h6>
<div v-if="prof.tahs">
<div class="d-inline-block" v-for="tag in prof.tahs.split(',')" v-bind:key="tag">
<span class="badge rounded-pill bg-secondary" style="margin: 1px;">{{tag}}</span>
</div>
</div>
<p class="card-text"></p><a class="card-link" href="#">Link</a><a class="card-link" href="#">Link</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import { useToast } from "vue-toastification";
import Project from '#/views/DetailProjectView.vue'
export default {
name: 'Projects',
setup() {
// Get toast interface
const toast = useToast();
return { toast }
},
data() {
return {
projects: [],
errors: [],
}
},
mounted() {
this.getItemsProjects()
},
components: {
Project,
},
methods: {
async getItemsProjects() {
this.$store.commit('setIsLoading', true)
axios
.get('/api/v1/projects/')
.then(response => {
this.projects = response.data
console.log(this.projects)
})
.catch(error => {
console.log(error)
})
this.$store.commit('setIsLoading', false)
},
}
}
</script>
and this is my ProjectDetailView.vue
<template>
<div>working? {{slug}}
</div>
</template>
<script>
import axios from 'axios'
import { useToast } from "vue-toastification";
export default {
name: 'Project',
setup() {
// Get toast interface
const toast = useToast();
return { toast }
},
data() {
return {
project: [],
errors: [],
}
},
mounted() {
this.getItemsProjects()
},
props: {
slug: {
type: String,
required: true,
},
},
methods: {
async getItemsProjects() {
this.$store.commit('setIsLoading', true)
axios
.get(`/api/v1/projectdetail/${this.slug}`)
.then(response => {
this.project = response.data
console.log(this.project)
})
.catch(error => {
console.log(error)
})
this.$store.commit('setIsLoading', false)
},
}
}
</script>
and my router:
import { createRouter, createWebHistory } from 'vue-router'
import store from '../store'
import HomeView from '../views/HomeView.vue'
import Signup from '../views/SignupView.vue'
import Login from '../views/LoginView.vue'
import Dash from '../views/DashView.vue'
import Myacc from '../views/MyAccView.vue'
import Profile from '../views/ProfileView.vue'
import Projects from '../views/ProjectsView.vue'
import ProjectDetail from '../views/DetailProjectView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/signup',
name: 'signup',
component: Signup
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/dash',
name: 'dash',
component: Dash,
meta: {
requiredLogin: true
}
},
{
path: '/myacc',
name: 'myacc',
component: Myacc,
meta: {
requiredLogin: true
}
},
{
path: '/profile',
name: 'profile',
component: Profile,
meta: {
requiredLogin: true
}
},
{
path: '/projects',
name: 'projects',
component: Projects,
meta: {
requiredLogin: true
}
},
{
path: '/project/:slug',
name: 'projectdetail',
component: ProjectDetail,
meta: {
requiredLogin: true,
}
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiredLogin) && !store.state.isAuthenticated) {
next('/login')
} else {
next()
}
})
export default router
how to fix this bug? thank you for advice.. im new in vue
edit

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);