How can I delete an object from getter if PersistedState is used - vuejs2

I am creating a small website and faced with such a problem. On the site, I use PersistedState to save the products that I added to favorites. But the problem is that I can't delete this product since splice doesn't work. He only visually removes it, but it can be seen from VueJS DevTools that it is still there.
store.js
import { createStore } from 'vuex'
import axios from "axios"
import createPersistedState from "vuex-persistedstate";
export default createStore({
state: {
products: [],
favourites: []
},
getters: {
PRODUCTS(state){
return state.products
},
PRODUCT_BY_ID(state){
return ProductId => {
return state.products.find(Product => Product.id === ProductId)
}
},
FAVOURITES(state){
return state.favourites
}
},
mutations: {
SET_PRODUCTS_TO_STATE: (state, products) =>{
state.products = products
},
SET_TO_FAVOURITES: ( state, favouritesItem) =>{
if (state.favourites.length){
let ProductExist = false
state.favourites.map(function(item){
if (item.id === favouritesItem.id){
ProductExist = true
}
})
if (!ProductExist){
state.favourites.push(favouritesItem)
}
} else{
state.favourites.push(favouritesItem)
}
}
},
actions: {
async GET_PRODCUTS_FROM_DB({commit}){
try {
const products = await axios("http://localhost:3000/products", {
method: "GET"
})
commit("SET_PRODUCTS_TO_STATE", products.data)
return products.data
} catch (error) {
return error
}
},
ADD_TO_FAVOURITES({commit}, favouritesItem){
commit('SET_TO_FAVOURITES', favouritesItem)
}
},
modules: {
},
plugins: [
createPersistedState()
]
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
products-item.js
<script>
import { mapGetters } from 'vuex'
export default {
name: "ProductsCart",
data(){
return {
IsProductExist: false
}
},
computed: {
...mapGetters([
'FAVOURITES'
]),
},
props: {
Productsitem: {
type: Object,
default() {
return {}
}
}
},
methods: {
addToCarts(){
if(this.$route.name === 'Favourites'){
this.FAVOURITES.splice(this.FAVOURITES.indexOf(this.Productsitem), 1)
} else {
this.$emit('addToCarts', this.Productsitem)
this.IsProductExist = true
}
}
},
mounted(){
this.$nextTick(function () {
if(this.FAVOURITES.length > 0){
for(let i = 0; i < this.FAVOURITES.length; i++){
if(this.FAVOURITES[i].id === this.Productsitem.id){
this.IsProductExist = true
}
}
}
})
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

I have found a solution to my problem. In mutation, as #yoduh said above, it was necessary to correctly refer to getter in mutations. Now the mutation looks like this:
DELTE_PRODUCTS(state, {data, getters}){
getters.FAVOURITES.splice(0,1)
//console.log(data);
//console.log(state);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Related

Vuex mapActions: mapper parameter must be either an Array or an Object

When I'm trying to use the "getProducts" method through mapActions i am getting the "this.getProducts" is not a function.
Basically i get the actions from my product.js. So i don't know really why the error is existing.
I hope u can help me to handle with it. :)
My component ProductList.vue:
<script>
import ProductCard from "./ProductCard.vue";
import { mapState, mapActions } from "vuex";
export default {
components: {
ProductCard
},
computed: {
//state.modulname.state.js(products state)
...mapState("product" ["products"])
},
mounted() {
this.getProducts();
},
methods: {
...mapActions("product"["getProducts"])
}
};
</script>
<style>
</style>
My product.js File:
import store from "../../store/store";
import Product from "../../apis/Product";
const state = {
product: null,
products: [],
};
const mutations = {
SET_PRODUCT(state, product) {
state.product = product;
},
SET_PRODUCTS(state, products) {
state.products = products;
},
};
const actions = {
getProduct({ commit }, productId) {
Product.show(productId).then((response) => {
commit("SET_PRODUCT", response.data);
});
},
getProducts({ commit }) {
Product.all().then((response) => {
commit("SET_PRODUCTS", response.data);
});
},
};
const getters = {
getProductID() {
return (id) => state.products.filter((product) => product.id === id);
},
};
export default {
namespaced: true,
store,
state,
mutations,
actions,
getters,
};
Try this:
...mapActions([
'product/getProducts'
])

Vuex getter always return null

When i use vuex getter in my vue.js component it return null for me.
Here is my code
MainLayout.vue
<script>
import NavBar from '#/components/NavBar.vue'
import ToolBar from "#/components/ToolBar"
import { mapActions, mapGetters } from 'vuex'
export default {
name: "MainLayout",
components : {
ToolBar, NavBar
},
data: () => ({
drawer: null,
}),
computed: {
...mapGetters([
'error',
]),
},
methods: {
close() {
this.$store.commit('SET_ERROR', null)
},
}
}
</script>
<template>
<div id="main">
<v-navigation-drawer clipped v-model="drawer" app>
<nav-bar></nav-bar>
</v-navigation-drawer>
<tool-bar #toggleDrawer="drawer = !drawer"/>
<v-content>
<v-container class="fill-height" fluid>
<router-view></router-view>
</v-container>
</v-content>
<v-snackbar :timeout="0" :value="error">
{{ error }}
<v-btn color="red" text #click="close">
Close
</v-btn>
</v-snackbar>
</div>
</template>
<style scoped>
</style>
Here is NavBar.vue
<script>
import { mapGetters } from 'vuex'
export default {
data: () => ({
}),
computed: {
...mapGetters([
'authUser'
]),
isAdmin() {
return this.authUser.role.name == 'admin'
},
}
}
</script>
Vuex module auth.js
import api from '#/api'
import {clearAccessToken, setAccessToken} from '#/auth'
import router from '#/router'
const state = {
loading: null,
user: null
}
const mutations = {
SET_LOADING: (state, loading) => {
state.loading = loading
},
SET_USER: (state, user) => {
state.user = user
}
}
const getters = {
loading: state => {
return state.loading
},
loggedIn: (state) => {
return !!state.user
},
authUser: (state) => {
return state.user
},
}
const actions = {
async login({commit, dispatch }, user) {
commit('SET_LOADING', true)
try {
const data = await api.post('/api/auth/login', { user })
setAccessToken(data.token)
await dispatch('getUser')
commit('SET_LOADING', false)
router.push('/')
} catch (e) {
commit('SET_LOADING', false)
dispatch('handleError', e)
}
},
async getUser({commit, dispatch}) {
try {
const user = await api.get('/api/auth/user')
commit('SET_USER', user.data)
return user
} catch (e) {
clearAccessToken()
dispatch('handleError', e)
}
},
async logout({commit, dispatch}) {
try {
await api.post('/api/auth/logout')
clearAccessToken()
router.push('/login')
} catch(e) {
dispatch('handleError', e)
}
}
}
export default {
namespaced: false,
state,
getters,
actions,
mutations,
}
When i run this code i have next error
[Vue warn]: Error in render: "TypeError: Cannot read property 'role' of null"
But if i add code
isAdmin() {
return this.authUser.role.name == 'admin'
},
in ToolBaar component (and remove from NavBar)
<script>
import { mapGetters } from 'vuex'
export default {
methods: {
toggleDrawer () {
this.$emit('toggleDrawer')
},
logout() {
this.$store.dispatch('logout')
}
},
computed: {
...mapGetters([
'loggedIn',
'authUser'
]),
fullName() {
return this.authUser.first_name + ' ' + this.authUser.last_name
},
isAdmin() {
return this.authUser.role.name == 'admin'
}
},
}
</script>
Then it work good, without any error, so i dont know what is the issue here, in one component code work good, and in another it doesnt, also if i add it in MainLayout component and pass isAdmin as props then it also work. Help me pls fix this.
Also, i dispatch user in router hook
router.beforeEach(async(to, from, next) => {
const needAuth = to.matched.some(record => record.meta.auth)
function redirectToLogin() {
next({
path: '/login',
query: { redirect: to.fullPath },
})
}
if (!hasToken() && needAuth) {
return redirectToLogin()
}
if (hasToken() && !store.getters.loggedIn) {
try {
const user = await store.dispatch('getUser')
if (!user) {
return redirectToLogin()
}
} catch(e) {}
}
next()
})
You should guard your access of authUser with loggedIn. For example
isAdmin() {
return this.loggedIn && this.authUser.role.name == 'admin'
}

Variable not updated after vuex mutation

I am creating a settings page, where I fetch some data from the API and I am using Vuex to handle mutations.
I can see that the Vuex completes properly, but value for my dailyCount variable doesn't update in frontend.
This is my Settings component:
<template>
<div>
<div class="row col">
<h1>Settings</h1>
</div>
<div class="row col">
<div class="well">
<form class="form-inline">
<input type="number" v-model="dailyCount" />
{{ dailyCount }}
</form>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'settings',
data () {
return {
dailyCount: 500
};
},
created () {
this.$store.dispatch('settings/fetchSetting');
},
computed: {
isLoading() {
return this.$store.getters['user/isLoading'];
},
hasError() {
return this.$store.getters['user/hasError'];
},
error() {
return this.$store.getters['user/error'];
},
},
}
</script>
I do mutations here:
import SettingsAPI from '../api/settings';
export default {
namespaced: true,
state: {
isLoading: false,
error: null,
settings: null,
},
getters: {
isLoading (state) {
return state.isLoading;
},
hasError (state) {
return state.error !== null;
},
error (state) {
return state.error;
},
user (state) {
return state.user;
},
},
mutations: {
['FETCHING_SETTINGS'](state) {
state.isLoading = true;
state.error = null;
state.settings = null;
},
['FETCHING_SETTINGS_SUCCESS'](state, settings) {
state.isLoading = false;
state.error = null;
state.settings = settings;
},
['FETCHING_SETTINGS_ERROR'](state, error) {
state.isLoading = false;
state.error = error;
state.settings = null;
},
},
actions: {
fetchSetting ({commit}) {
commit('FETCHING_SETTINGS');
return SettingsAPI.get()
.then(res => {commit('FETCHING_SETTINGS_SUCCESS', res.data);})
.catch(err => commit('FETCHING_SETTINGS_ERROR', err));
},
},
}
And call to a server is done here (api/settings.js - it is imported in mutation file):
import axios from 'axios';
export default {
get() {
return axios.get('/user');
},
}
Can you see what am I doing wrong? I am trying to debug it using Vuejs debug toolbar, but all seems to work fine.
You need to get store state from vuex and inject to Vue component, either by this.$store.state or this.$store.getters.
For example:
<script>
export default {
name: 'settings',
data () {
return {
dailyCount: 500
};
},
created () {
this.$store.dispatch('settings/fetchSetting');
},
computed: {
isLoading() {
return this.$store.getters['user/isLoading'];
},
hasError() {
return this.$store.getters['user/hasError'];
},
error() {
return this.$store.getters['user/error'];
},
settings() {
return this.$store.state.settings
}
},
watch: {
settings () {
this.dailyCount = this.settings.dailyCount
}
}
}
</script>

Rerender component after state change vue.js

I am working with NuxtJS and VueJS. I'm having a problem with a component not re-rendering after the state changed.
index.js file
Vue.use(Vuex)
const state = {
productsHome: [],
accessToken: {},
collections: {},
product: {},
cart: {},
}
const getters = {
productForHomepage (state) {
return state.productsHome
},
productForPdp (state) {
return state.product
},
cart (state){
return state.cart
}
}
const actions = {
nuxtServerInit (context) {
//good place to set language
},
GET_HOME(){
api.getHomepageProducts().then(response => {
this.commit('setHomeProducts', response.data)
})
},
GET_PDP(sth){
api.findBySlug(this.app.router.history.current.params.slug).then(response => {
this.commit('setPDPData', response.data)
})
},
ADD_TO_CART(store, id){
api.addToCart(id).then(res => {
store.commit('updateCart', res.data)
})
}
}
const mutations = {
setHomeProducts(state, data){
state.productsHome = data
},
setPDPData(state, data){
state.product = data[0]
},
updateCart(state, data){
for (var optbox of data) {
state.cart[optbox.id] = optbox;
}
// state.cart.set('iteams', 'count', 1)
}
}
const createStore = () => {
return new Vuex.Store({
state,
getters,
mutations,
actions
});
}
export default createStore;
and this is the component
<template>
<div>
<div class="content">
<p>
This is cart
</p>
{{ cart }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
cart: this.$store.state.cart
}
},
watch: {
cart: function(val){
cart = this.$store.state.cart
}
},
methods: {
updateCart: function(){
console.log(this)
}
}
}
</script>
When you do this:
data() {
return {
cart: this.$store.state.cart
}
}
You initilise the data with the value of the cart state, but it won't keep changing when the cart state changes, it's a one time deal, as you can see in this JSFiddle
What you actually want to do is use a computed:
computed: {
cart(){
return this.$store.state.cart
}
}
Now whenever cart state changes in your store, so too will the value of cart in your component.
And here's the JSFiddle for that: https://jsfiddle.net/craig_h_411/zrbk5x6q/

Pre-fetch data using vuex and vue-resource

I'm building an app following this structure: http://vuex.vuejs.org/en/structure.html
My components/App.vue like this:
<template>
<div id="app">
<course :courses="courses"></course>
</div>
</template>
<script>
import Course from './course.vue'
import { addCourses } from '../vuex/actions'
export default {
vuex: {
getters: {
courses: state => state.courses,
},
actions: {
addCourses,
}
},
ready() {
this.addCourses(this.fetchCourses())
},
components: { Course },
methods: {
fetchCourses() {
// what do I have to do here
}
}
}
</script>
How can I fetch the data and set it to the state.courses ?
Thanks
I've just figured it out:
in /components/App.vue ready function, I just call:
ready() {
this.addCourses()
},
in vuex/actions.js:
import Vue from 'vue'
export const addCourses = ({ dispatch }) => {
Vue.http.get('/api/v1/courses')
.then(response => {
let courses = response.json()
courses.map(course => {
course.checked = false
return course
})
dispatch('ADD_COURSES', courses)
})
}
and in vuex/store.js:
const mutations = {
ADD_COURSES (state, courses) {
state.courses = courses
}
}