getActivePinia was called with no active Pinia. Vue - vue.js

i get this error: Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
at useStore (pinia.mjs:1691:19)
at PokemonDetails.vue:3:22
what's wrong with my code?
pokemonDetails:
<script>
import { usePokemonStore } from '../stores/PokemonStore';
const pokemonStore = usePokemonStore();
export default {
name: 'PokemonDetails',
methods: {
evolutions() {
return [
pokemonStore.evolutionOne,
pokemonStore.evolutionTwo,
pokemonStore.evolutionThree,
];
},
resetApp() {
this.$router.push('/');
pokemonStore.$reset();
},
},
};
</script>
<template>
<template v-if="pokemonStore.findPokemon == false">
<div class="firstDiv">
<div class="secondDiv">
<div>
<strong>{{ pokemonStore.pokemonName }}</strong
><img :src="pokemonStore.selfie" alt="foto de pokemon" />
<p>Elemento Principal: {{ pokemonStore.type }}</p>
</div>
<div>
<strong>Habilidades:</strong>
<ul>
<li v-for="stat in pokemonStore.stats[0]">
{{ stat.stat.name }}: +{{ stat.base_stat }}
</li>
</ul>
</div>
</div>
<div class="divEvolutions">
<strong>Evolução</strong>
<ul class="evolutions">
<li v-for="evolution in evolutions">
<img :src="evolution.selfie" />
{{ evolution.name }}
</li>
</ul>
</div>
<button v-on:click="resetApp" class="newSearch">Nova pesquisa</button>
</div>
</template>
</template>
<style lang="scss" scoped>
.firstDiv {
text-align: center;
}
.secondDiv {
display: grid;
justify-items: center;
align-items: center;
grid-template-columns: repeat(2, 1fr);
max-height: 600px;
width: 400px;
padding: 20px;
border-radius: 1.5rem;
background-color: $gray-200;
div {
display: flex;
flex-direction: column;
}
}
.divEvolutions {
background-color: $gray-200;
border-radius: 1.5rem;
margin-top: 10px;
}
.evolutions {
display: flex;
justify-content: center;
align-items: center;
li {
display: flex;
flex-direction: column;
}
}
.newSearch {
margin-top: 10px;
padding: 5px;
border-radius: 1rem;
background-color: $gray-200;
transition-duration: 500ms;
&:hover {
background-color: black;
color: $gray-200;
}
}
</style>
pokemonStore.js:
import { defineStore } from 'pinia';
export const usePokemonStore = defineStore('pokemon', {
state: () => ({
findPokemon: true,
pokemonName: '',
speciesLink: '',
selfie: '',
type: '',
stats: [],
evolutionOne: {},
evolutionTwo: {},
evolutionThree: {},
}),
getters: {},
actions: {
addPokemon(
name,
species,
selfie,
type,
stats,
evolutionOne,
evolutionTwo,
evolutionThree
) {
this.pokemonName = name;
this.speciesLink = species;
this.selfie = selfie;
this.type = type;
this.stats.push(stats);
this.findPokemon = false;
this.evolutionOne = evolutionOne;
this.evolutionTwo = evolutionTwo;
this.evolutionThree = evolutionThree;
},
},
});
main.js:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './assets/main.css';
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount('#app');
i tried call my store in computed:
computed: {
pokemonStore() {
return usePokemonStore();
},
evolutions() {
return [
this.pokemonStore.evolutionOne,
this.pokemonStore.evolutionTwo,
this.pokemonStore.evolutionThree,
];
},
},
it works, but i believe is don't the best practices

Stores aren't supposed to be used before Pinia is installed to Vue application.
The reason why use... composables are created by Pinia defineStore instead of store objects is that this allows to avoid race conditions.
Here usePokemonStore is called on pokemonDetails import before Pinia install. pokemonStore is referred in the template while it's not a part of component instance. For a component with options API it should be:
name: 'PokemonDetails',
data() {
return { pokemonStore: usePokemonStore() }
},

Related

How can I add multiple product in cart using vue js and django rest framework

I am trying to add multiple product in the cart. I am using vuejs and django rest framework. My problem is: When I add a product into the cart it added successfully But when I add another product it doesnt add. It adds the same product again and again.
For example:
I have three products named "A" and "B" and "C". I added "A". Then i try to add "C" in the cart. But it stills adds "A" in the cart. I cleared the session and tried again still add the "A" product if I try to add "C" product first. It always add "A" product.
Here is my store/index.js:
import { createStore } from 'vuex'
export default createStore({
state: {
cart: {
items: []
},
isAuthenticated: false,
token: '',
isLoading: false,
},
mutations: {
initializeStore(state){
if(localStorage.getItem('cart')){
state.cart = JSON.parse(localStorage.getItem('cart'))
}
else{
localStorage.setItem('cart', JSON.stringify(state.cart))
}
},
addToCart(state, item) {
let exists = state.cart.items.filter(i => i.product.id === item.product.id)
if (exists.length){
exists[0].quantity = parseInt(exists[0].quantity) + parseInt(item.quantity)
}
else{
state.cart.items.push(item)
}
localStorage.setItem('cart', JSON.stringify(state.cart))
}
},
actions: {
},
modules: {
}
})
Here is my add to cart page and code:
<template>
<br />
<div class="col">
<div class="col1">
<img v-bind:src="product.get_image" alt="">
</div>
<div class="col2">
<div class="product__title">
<p>PRODUCT TITLE</p>
<h2>{{ product.name }}</h2>
<small>{{ product.short_description }}</small>
</div>
<div class="product__price">
<p>Price: $ {{ product.price }}</p>
</div>
<div class="product__button">
<input type="hidden" v-model="quantity" min="1">
<button type="submit" class="button__primary" #click="addToCart">Add To Cart</button>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import {toast} from 'bulma-toast'
export default {
name: "ProductDetail",
data () {
return {
product: {},
quantity: 1
}
},
mounted() {
this.getProduct()
},
methods: {
getProduct() {
const categorySlug = this.$route.params.category_slug
const productSlug = this.$route.params.product_slug
axios.get(`/api/product-details/${categorySlug}/${productSlug}/`)
.then(response => {
this.product = response.data
})
.catch(error => {
console.log(error)
})
},
addToCart() {
if(isNaN(this.quantity) || this.quantity < 1){
this.quantity = 1
}
const item = {
product: this.product,
quantity: this.quantity
}
this.$store.commit("addToCart", item)
toast({
message: "Product has been added to cart" + item.product.name,
type: "is-success",
pauseOnHover: true,
duration: 2000,
position: "bottom-right",
dismissible: true,
})
}
}
}
</script>
<style scoped>
.col {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 5rem;
height: 50vh;
padding: 60px;
}
.col1 {
height: 520px;
width: 100%;
}
.col1 img{
object-fit: fill;
height: 520px;
width: 100%;
}
.col2 {
display: grid;
grid-auto-rows: 1fr 1fr 1fr 1fr;
background: #fff;
padding: 12px;
}
.product__button button{
height: 40px;
width: 100%;
border: none;
background: #007bc4;
color: #fff;
border-radius: 3px;
}
.product__price p {
color: #007bc4;
font-size: 18px;
}
</style>
Here is the cartitem component:
<template>
<tr class="is-fullwidth">
<td>{{ item.product.name }}</td>
<td>$ {{ item.product.price }}</td>
<td>
<button #click="increment(item)" class="plusButton">+</button>
{{ item.quantity }}
<button #click="decrement(item)" class="minusButton">-</button>
</td>
<td>{{ getTotal(item).toFixed(2) }}</td>
<td><button class="delete"></button></td>
</tr>
</template>
<script>
export default {
name: "Cartitem",
props: {
initialItem: Object
},
data() {
return {
item: this.initialItem
}
},
methods: {
getTotal(item) {
return item.quantity * item.product.price
},
increment(item){
item.quantity += 1
this.updateCart()
},
decrement(item) {
item.quantity -= 1
if(item.quantity === 0){
this.$emit('removeFromCart', item)
}
this.updateCart()
},
updateCart() {
localStorage.setItem('cart', JSON.stringify(this.$store.state.cart))
},
removeFromCart(item) {
this.$emit('removeFromCart', item)
this.updateCart()
}
}
}
</script>
<style scoped>
.plusButton{
border: none;
background: #fff;
font-size: 19px;
}
.minusButton{
border: none;
background: #fff;
font-size: 19px;
}
</style>
I am new in vue js. I am trying to build this projetc so that i can learn. But this issue is eating my brain. I tried to use find function in sotre/index.js. It solved my problem though but if i clear the cookies and try to add product it gives me error.
is there any solution for me?
Thanks in advance.

Getting the computed method to another components (Vuejs)

i wanted to get the computed data and display in another component. However i put the computed in my app.vue and try to call this computed using :style="inputStyles" in my ChangeBackground.vue . But when i try to do this it showing error that " Property or method "inputStyles" is not defined on the instance but referenced during render" Can someone help me? Thank you
You can access the code here:
https://codesandbox.io/s/hardcore-morning-5ch1u?file=/src/components
Here is the code:
App.vue
<template>
<div id="app">
<ChangeBackground msg="Hello Vue in CodeSandbox!" />
</div>
</template>
<script>
import ChangeBackground from "./components/ChangeBackground";
export default {
name: "App",
components: {
ChangeBackground,
},
data() {
return {
bgColor: "red",
};
},
created() {
this.bgColor = "#F6780D";
},
computed: {
inputStyles() {
return {
background: this.bgColor,
};
},
},
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
body {
background-color: blue;
}
</style>
ChangeBackground.vue
<template>
<div class="hello" :style="inputStyles">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
msg: "Getting the computed area here to change the background",
};
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
h1,
h2 {
font-weight: bold;
}
ul {
list-style-type: none;
padding: 1rem;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
You should pass it as prop as you did with msg :
App.vue
<ChangeBackground msg="Hello Vue in CodeSandbox!" :input-styles="inputStyles" />
ChangeBackground.vue
<template>
<div class="hello" :style="inputStyles">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props:["inputStyles"],//⬅
data() {
return {
msg: "Getting the computed area here to change the background",
};
},
};
</script>

Error in cypress: Object(...) is not a function

This is my test component:
<template>
<div class="container">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String,
},
data () {
},
}
</script>
<style>
.container {
max-width: 500px;
margin: 30px auto;
overflow: auto;
min-height: 300px;
border: 1px solid steelblue;
padding: 30px;
border-radius: 5px;
}
</style>
And this is my test:
import { mount } from '#cypress/vue'
import HelloWorld from '../../src/HelloWorld.vue'
describe('HelloWorld', () => {
it('renders a message', () => {
const msg = 'Hello Cypress Component Testing!'
mount(HelloWorld, {
propsData: {
msg,
},
})
cy.get('h1').should('have.text', msg)
})
})
Below is the output in Cypress when I run yarn cypress open-ct:
Maybe it's too late but the solution for me was to change import { mount } from 'cypress' to import { mount } from 'cypress/vue2 in the test file. That solved my problem.

Delete item for todo app in with $emit 2 level up or 1 level up? [duplicate]

This question already has answers here:
Vue 2 - Mutating props vue-warn
(28 answers)
Closed 2 years ago.
I have 3 .vue here: App.vue (default), Todos.vue and Todoitem.vue. I am following the tutorial from https://www.youtube.com/watch?v=Wy9q22isx3U&t=2458. May I know why the author in TodoItem.vue emit id two level up to App.vue to perform the method to delete? Is it best practice or better coding style? Is it easier to just go up one level for Todos.vue to do the same? Below is my one level up approach for any comment.
Below is my TodoItem.vue code
<template>
<div class="todo-item" v-bind:class="{'is-complete':todoObj.completed}">
<p>
<input type="checkbox" v-on:change="markComplete" />
{{todoObj.title}}
<button #click="$emit('del-todo',todoObj.id)" class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todoObj"], // todoObj is defined in the parent.
methods: {
markComplete() {
this.todoObj.completed = !this.todoObj.completed;
}
}
};
</script>
<style scoped>
.todo-item {
background: #f4f4f4;
padding: 10px;
border-bottom: 1px #ccc dotted;
}
.is-complete {
text-decoration: line-through;
}
.del {
background: #ff0000;
color: #fff;
border: none;
padding: 5px 9px;
border-radius: 50%;
cursor: pointer;
float: right;
}
</style>
Below is my Todo.vue code
<template>
<div>
<h1>Todo List2</h1>
<!-- :key= and v-bind:key= are exactly the same. -->
<!-- v-bind. Shorthand: : -->
<div v-for="todo in ptodos" :key="todo.id">
<!-- Define todoObj here which to be used in the child component, TodoItem -->
<MyTodoItem v-bind:todoObj="todo" v-on:del-todo="deleteTodo" />
<!-- del-todo is from the child. child goes up to parent and then to grandparent (App.vue) -->
</div>
</div>
</template>
<script>
import MyTodoItem from "./TodoItem.vue";
export default {
name: "Todos",
components: {
MyTodoItem
},
props: ["ptodos"],
methods: {
deleteTodo(id) {
this.ptodos = this.ptodos.filter(todo => todo.id !== id);
}
}
};
</script>
<style scoped>
</style>
Below is my App.vue code
<template>
<MyToDos v-bind:ptodos="todos" />
</template>
<script>
import MyToDos from "./components/Todos";
export default {
name: "App",
components: { MyToDos },
data() {
return {
todos: [
{
id: 1,
title: "Todo One",
completed: false
},
{
id: 2,
title: "Todo Two",
completed: true
},
{
id: 3,
title: "Todo Three",
completed: false
}
]
};
}
};
</script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
line-height: 1.4;
}
</style>
If you can do it with one level up it's better. To have multiple props on each child can be a bad practice called prop drilling.
Vuex is a good alternative to avoid to get props nested.

Could Vue.js router-view name be changed?

I am new to Vue.js and I know there is more than one component in a route with different names.
In App.vue file, could <router-view name="default"> be changed to other name? Thank you for your help.
HelloWorld.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
App.vue
<template>
<div id="app">
<!-- <img src="./assets/logo.png"> -->
<router-view name="default"></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
You need dynamic components for this,
Html code
<template>
<div id="app">
<router-view :is="currentComponent"></router-view>
</div>
</template>
Js code: here depending on isTrue value set whichever component you need.
<script>
export default {
name: 'App' ,
components: {
LoginComponent,
HelloComponent
},
computed: {
currentComponent () {
return isTrue ? 'HelloComponent' : 'LoginComponent'
}
}
}
</script>