my cart items is not showing in the cart(which is in state using pinia) after adding them using an action from a button in the shop page
my code:
store.js(using pinia)
import { defineStore } from "pinia";
import Products from "../db.json";
export const useCounterStore = defineStore("counter", {
state: () => ({
cart: [],
}),
actions: {
addToCart(id) {
this.cart.push(id)
console.log("test passed!")
}
}
});
shop.vue
<template>
<div class="shop">
<h1>shop</h1>
<div class="products" v-for="item in Products" :key="item.id">
{{ item.name }} {{ item.price }}$
<button #click="storeCounter.addToCart(item.id)">Add to Cart</button>
</div>
</div>
</template>
<script setup>
import { useCounterStore } from '../stores/counter';
import Products from "../db.json"
const storeCounter = useCounterStore()
</script>
cart.vue
<template>
<div class="cart">
<h1>cart</h1>
<div class="cartitems" v-for="item in storeCounter.cart" :key="item.id">{{ item.name }} {{ item.price }}</div>
</div>
</template>
<script setup>
import { useCounterStore } from '../stores/counter';
import Products from "../db.json"
const storeCounter = useCounterStore()
</script>
why it is not working for me? i assume i did everything right...
shop.vue is only pushing the id number into the store's cart array
<button #click="storeCounter.addToCart(item.id)">
cart.vue is attempting to display the cart array as if contains full product objects and not just the ids
<div class="cartitems" v-for="item in storeCounter.cart" :key="item.id">
{{ item.name }} {{ item.price }}
</div>
This can be easily fixed by changing shop.vue to add the full item instead of just item.id
<button #click="storeCounter.addToCart(item)">
Related
I have a JobComponent.vue component where I fetch data from a VUEX Store. This component is used on two separate pages, first page Home.vue and second page AllJobs.vue.
In AllJobs.vue I used JobComponent.vue and everything is works fine, it's rendering all the jobs, but, here comes the problem...
In Home.vue I want to render only the last 5 jobs, so in store I make a getter that slice me only the latest 5 jobs.
How can I use this latestJobs from getters on the same component?
When I import the component in Home.vue page I can't use another v-for direct on the component...
here you can see my project structure and files
Home.vue
<template>
<div class="cards-container">
<JobComponent />
</div>
</template>
JobComponent.vue
<template>
<div v-for="job in allJobs" :key="job.id" class="card">
<div class="position">{{ job.position }}</div>
<div class="department">{{ job.department }}</div>
<div class="location">
<span class="material-symbols-outlined">location_on</span>
{{ job.location }}
</div>
<span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
<span #click="deleteJob(job.id)" class="material-symbols-outlined right-arrow">delete</span>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
methods: {
...mapActions(['fetchJobs', 'deleteJob']),
},
computed: mapGetters(['allJobs']),
created() {
this.fetchJobs();
}
}
</script>
store.js (vuex)
const getters = {
allJobs: (state) => state.jobs,
latestJobs: (state) => {
const response = state.jobs.slice(0, 5);
return response;
}
};
Your component should be as independent as possible from the store. It's role is to display what ever is provided so it could be reused as you want, using props :
JobComponent.vue
<template>
<div class="card">
<div class="position">{{ position }}</div>
<div class="department">{{ department }}</div>
<div class="location">
<span class="material-symbols-outlined">location_on</span>
{{ location }}
</div>
<span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
<span #click="$emit('deleteJob', id)" class="material-symbols-outlined right-arrow">delete</span>
</div>
</template>
<script>
export default {
props: {
id: string,
position: string,
department: string,
location: string
}
}
</script>
In this component you only display the provided data, and leave the responsibility of the parent component to choose how many components to display.
Home.vue
<template>
<div class="cards-container">
<JobComponent v-for="job in jobs" :key="job.id" :id="job.id" :position="job.position" :department="job.department" :location="job.location" #delete-job="deleteJob" />
</div>
</template>
<script>
export default {
created() {
this.$store.dispatch('fetchJobs')
},
computed: {
jobs() {
return this.$store.getters['latestJobs'] // Or allJobs, just make sure your getter returns an array even if no jobs are loaded yet.
}
},
methods: {
deleteJob() {
// Your logic for job delete
}
}
}
</script>
I have the following code:
<script setup>
import {ref, defineAsyncComponent, computed, reactive} from 'vue'
import {useMainStore} from '#/store/main.js'
import {useTableStore} from "#/store/tables";
import DefaultLayout from '#/layouts/DefaultLayout.vue'
import { storeToRefs } from 'pinia'
const tableStore = useTableStore()
const { userData } = useMainStore()
tableStore.getTables()
const { freeTables, usedTables } = storeToRefs(useTableStore())
const { tables } = storeToRefs(useTableStore())
const loading = ref(false)
const showOrder = ref(false)
let props = reactive({
orderID: null,
id: null,
})
const tableOrderComponent = computed(() => showOrder.value ? defineAsyncComponent(() =>
import("#/components/TableOrder.vue")) : ''
)
let tableOrder = (table) => {
props.id = parseInt(table.id, 10)
props.orderID = table.activeOrderID || null
showOrder.value = true
}
</script>
<template>
<DefaultLayout>
<template #default>
<div class="row come-in">
<div class="card col">
<div class="card-header">
<span class="text-capitalize"> {{ userData.username }}</span>
</div>
<div class="card-body subheader">
Used Tables ({{ usedTables.length }})
</div>
<ul class="list-group list-group-flush">
<li v-for="table in usedTables">
{{ table.name }}
</li>
</ul>
<div class="card-body subheader">
Free Tables ({{ freeTables.length }})
</div>
<ul class="list-group list-group-flush">
<li v-for="table in freeTables">
{{ table.name }}
<a href="#" v-tooltip="'Add order to the table'" #click="tableOrder(table)">
<font-awesome-icon icon="fas fa-list-alt" />
</a>
</li>
</ul>
</div>
<div class="col">
<table-order-component v-show="showOrder"
:orderID="props.orderID"
:id="props.id"></table-order-component>
</div>
</div>
</template>
</DefaultLayout>
</template>
When the page is loaded I am getting following error (warning):
[vite] connecting... client.ts:16:8
[vite] connected. client.ts:53:14
[Vue warn]: Invalid vnode type when creating vnode: .
at <DefaultLayout>
at <Tables onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > >
at <RouterView>
at <App> runtime-core.esm-bundler.js:38:16
[Vue warn]: Invalid vnode type when creating vnode: .
at <DefaultLayout>
at <Tables onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref<
Proxy { <target>: Proxy, <handler>: {…} }
> > at <RouterView> at <App> runtime-core.esm-bundler.js:38:16
What am I doing wrong here?
Default layout:
<script setup>
import AppHeader from '#/components/AppHeader.vue'
</script>
<template>
<AppHeader></AppHeader>
<div id="container-fluid">
<main>
<slot></slot>
</main>
</div>
</template>
App Header:
<script setup>
import { useRouter } from 'vue-router'
import {useMainStore} from "#/store/main.js";
const mainStore = useMainStore()
const router = useRouter()
const logout = () => {
mainStore.logout()
.then(() => {
router.push({
name: 'Login'
})
})
}
</script>
<template>
<nav id="nav">
<button #click="logout">Logout</button>
</nav>
</template>
<style>
</style>
I am passing array as a prop to another component, and when I want to read this on mounted in that component, I got Proxy {}. How to read data from this prop? You can see in example when I want to console log prop, result is Proxy {}. I can see all values in HTML structure, but not in the console on mounted.
<template>
<div class="custom-select-container">
<div class="selected-item" #click="openSelect">
<span class="selected-items-text">{{ selectedItem.name }}</span>
<span class="icon-arrow1_b selected-items-icon" :class="{ active: showOptions }" />
</div>
<transition name="fade">
<ul v-show="options.length && showOptions" class="custom-select-options">
<li v-for="(option, index) in options" :key="index" class="custom-select-item">{{ option.name }}</li>
</ul>
</transition>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
props: {
options: {
type: Array,
default: () => []
}
},
setup(props) {
let showOptions = ref(false);
let selectedItem = ref(props.options[0])
const openSelect = () => {
showOptions.value = !showOptions.value
}
onMounted(() => {
console.log('test', props.options)
})
return {
openSelect,
showOptions,
selectedItem
}
}
}
</script>
Parent component where I am passing data:
<template>
<div class="page-container">
<div>
<div class="items-title">
<h3>List of categories</h3>
<span>({{ allCategories.length }})</span>
</div>
<div class="items-container">
<div class="item" v-for="(category, index) in allCategories" :key="index">
<span class="item-cell size-xs">{{ index + 1 }}.</span>
<span class="item-cell size-l">{{ category.name }}</span>
</div>
</div>
</div>
<custom-select
:options="allCategories"
/>
</div>
</template>
<script>
import CustomSelect from '../components/Input/CustomSelect'
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
components: {
CustomSelect
},
computed: {
},
setup() {
const store = useStore()
const allCategories = computed(() => store.getters['categories/getAllCategories'])
return {
allCategories
}
}
}
</script>
That's how reactivity works in Vue3.
use
console.log(JSON.parse(JSON.stringify(data))
or
console.log(JSON.stringify(data, null, 2))
to show the content of proxies in console
New to vue and struggling to understand how data is passed back and forth between components. I'm aware of props and emit on the parent/child, child/parent, but I can't quite understand how they work in my case. I have two components: a parent component called "Letters" and a child called "ClaimantSearch". Claimant search return data about a person based on a call to a flask backend:
<div>
<b-form #submit="onSubmit" class="w-100">
<b-form-group id="form-title-group"
label="Claim Number:"
label-for="form-claim-number-input">
<b-form-input id="form-claim-number-input"
type="text"
v-model="claimNumberForm.claimNumber"
required
placeholder="Enter claim number">
</b-form-input>
<button
type="button"
class="btn btn-warning btn-sm"
#click="getClaimant(claimNumber)">
Get Claimant
</button>
</b-form-group>
</b-form>
<span v-if="claimant">
<p> {{ claimant.name }} </p>
<p> {{ claimant.address1 }} </p>
<p> {{ claimant.address2 }} </p>
<p> {{ claimant.city }}, {{ claimant.state }} </p>
<p> {{ claimant.zip }} </p>
</span>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
claimant: '',
claimNumberForm: {
claimNumber: '',
},
};
},
methods: {
getClaimant(number) {
const path = `http://localhost:5000/claimant/${number}`;
axios.get(path)
.then((res) => {
this.claimant = res.data.claimant;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
},
onSubmit(evt) {
evt.preventDefault();
this.getClaimant(this.claimNumberForm.claimNumber);
},
},
};
</script>
I then have a Letters parent component:
<template>
<div>
<claimant></claimant>
</div>
</template>
<script>
// import axios from 'axios';
import ClaimantSearch from './ClaimantSearch.vue';
export default {
data() {
return {
claimant: '',
claimNumberForm: {
claimNumber: '',
},
};
},
components: {
claimant: ClaimantSearch,
},
methods: {
},
};
</script>
What I'd like to be able to do is access {{claimant}} outside of the <claimant> tag, if that makes sense. So inside Letters I'd like to do something like:
<template>
<div>
<div>
<claimant></claimant>
</div>
<div>
Dear Mr. {{claimant.name}},
Please get bent. Sincerly, crappy insurance company.
</div>
</div>
</template>
I can't remember exactly where I found this link, but there's an excellent post on medium, with code samples, that discusses all the state management patterns in vue starting with props and events, eventbus, simple store and then vuex.
https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87
In my component , I am trying to execute a vuex action from a module, but I am getting an error, even if this action is defined :
console.log
DELETE USER: 14
ListUsers.vue:131
[vuex] unknown action type: deleteUser
UPDATE inserted the template
here is the full template in my ListUsers.vue
In each table row I have a trash icon, which fire a modal panel ( ref="delUserModal" ) to confirm the delete action
<b-btn class="btn" variant="danger" block
#click="onAction('delete-user', props.row)">Yes, Delete It
</b-btn>
ListUsers.vue
<template>
<div id="users">
<v-server-table name='users' url="users" ref='userTable' :columns="columns" :options="options">
<template slot="afterBody">
<div class="row">
<router-link :to="{ name: 'new_user' }" class="left btn btn-primary">New User</router-link>
</div>
</template>
<template slot="child_row" slot-scope="props">
<div id="userDetail" class="row">
<div id="userPic" class="col-md-3">
<img src="./../../assets/user_default.png">
</div>
<div class="col-md-6">
<ul>
<li><b>First Name:</b> {{props.row.firstName}}</li>
<li><b>Last Name:</b> {{props.row.lastName}}</li>
<li><b>Email:</b> {{props.row.email}}</li>
<li><b>Birth Day:</b> {{props.row.birthday}}</li>
<li><b>Role:</b> {{props.row.role}}</li>
</ul>
</div>
<div class="col-md-3"></div>
</div>
</template>
<template slot="actions" slot-scope="props">
<div class="custom-actions">
<a class="fa fa-edit"
#click="onAction('edit-user', props.row)">
</a>
<a class="fa fa-trash"
#click="onAction('show-modal', props.row)">
</a>
</div>
<b-modal ref="delUserModal" hide-footer title="Delete User">
<div class="d-block text-center">
<h3>Do you confirm that<br/> you want to delete: </h3>
<p>{{ props.row.firstName }} {{ props.row.lastName }}</p>
<p>{{ props.row.email }}</p>
</div>
<b-btn class="btn" block #click="onAction('hide-modal')">No, Return to the list</b-btn>
<b-btn class="btn" variant="danger" block #click="onAction('delete-user', props.row)">Yes, Delete It</b-btn>
</b-modal>
</template>
</v-server-table>
</div>
</template>
<script>
import Vue from 'vue'
import store from '#/vuex/store'
...
import { mapActions } from 'vuex'
import _ from 'underscore'
Vue.use(ServerTable, {}, true)
Vue.use(Event)
window.moment = require('moment')
window.axios = require('axios')
export default {
name: 'users',
data () {
return { ... }
}
},
methods: _.extend({}, mapActions(['deleteUser']), {
onAction (action, data) {
switch (action) {
case 'edit-user':
...
case 'delete-user':
this.$refs.delUserModal.hide()
console.log('DELETE USER: ', data.id)
this.deleteUser(data.id) // <- error line 131
this.$refs.userTable.refresh()
break
default:
this.$refs.userTable.refresh()
}
}
}),
store
}
</script>
vues/store.js
import Vue from 'vue'
import Vuex from 'vuex'
import login from '#/vuex/modules/login'
import shoppinglists from '#/vuex/modules/shoppinglists'
import users from '#/vuex/modules/users'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
login,
shoppinglists,
users
}
})
vuex/modules.users/actions
import * as types from './mutation_types'
import api from '#/api/users'
import getters from './getters'
import store from '#/vuex/store'
export default {
updateUser: (store, id) => {
...
},
createUser: () => {
...
},
deleteUser: (store, id) => {
...
}
}
when using vuex with vue-tables-2 , a module is registered with the name prop from the table component
useVuex is a boolean indicating whether to use vuex for state management,
or manage state on the component itself. If you set it to true you must
add a name prop to your table, which will be used to to register a module
on your store. Use vue-devtools to look under the hood and see the current state.
I added correctly my own actions into this module, ( deleteUser: (store, id) was there) ... but I unfortunatly declared this 'users' module as namespaced = true... changing it to false or calling the action as namespaced solved this issue...