hello everyone im trying to add product into cart array so i can render it after , i used vuex but i got this error : Console was cleared
[vuex] unknown action type: addToCart
hopefully u can give me some hint so i can fix this issue
thank you so much !!
here is the sandbox : https://codesandbox.io/s/suspicious-minsky-9cig7?file=/src/components/Products.vue
<template>
<main>
<div class="margin"></div>
<div class="shopTitle">
<h1>Shop</h1>
</div>
<section class="categoriesSection">
<ul class="categories">
<li>
See All
</li>
<li>
Shoes
</li>
<li>
Suits
</li>
</ul>
</section>
<section>
<div class="products">
<div class="product" v-for="product in filteredProducts" :key="product.productId">
<div class="imgproduct"></div>
<div class="productDetails">
<div>
<h1>{{product.productTitle}}</h1>
</div>
<div>
<i onClickButton class="fa fa-heart"></i>
</div>
<div>
<p>{{product.productPrice}}</p>
</div>
<div>
<i onClickButton #click="addToCart(product)" class="fa fa-shopping-cart"></i>
</div>
</div>
</div>
</div>
</section>
</main>
</template>
<script>
export default {
data() {
return {};
},
computed: {
selectedCategory: {
// getter
get: function() {
return this.$store.getters.selectedCategory;
},
// setter
set: function(newValue) {
return this.$store.commit("updateCategory", newValue);
}
},
filteredProducts() {
return this.$store.getters.filteredProducts;
}
},
methods: {
addToCart: function(product) {
this.$store.dispatch("addToCart", product);
}
}
};
</script>
You have a mutation called addToCart but no action either create an action called addToCart or just call the mutation directly :
this.$store.commit("addToCart", product);
Related
HERE is a SANDBOX with the issue on it
I have a hoverable dropdown inside a Navbar
When I move to a different page, the dropdown is still open
I have tried this in plain Bulma, the issue still remains
I am on Nuxt.js using SSR
I am using nuxt-link / equivalent of Vue router-link to navigate to a different page.
Here is the my default.vue file
<template>
<div class="ch-container">
<header class="ch-header">
<nav
class="navbar is-fixed-top"
role="navigation"
aria-label="main navigation"
>
<div class="navbar-brand">
<nuxt-link class="navbar-item" to="/">
<img
alt="CH Logo"
src="https://i.imgur.com/v35Kfc9.png"
width="28"
height="28"
/>
</nuxt-link>
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<nuxt-link class="navbar-item" to="/news">
News
</nuxt-link>
<nuxt-link class="navbar-item" to="/resources">
Resources
</nuxt-link>
<nuxt-link class="navbar-item" to="/tickers">
Tickers
</nuxt-link>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
More
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
FAQ
</a>
<nuxt-link class="navbar-item" to="/contact">
Contact
</nuxt-link>
<hr class="navbar-divider" />
<a class="navbar-item">
Feature Request
</a>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<a href="#">
<fa :icon="faMoon" />
</a>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-arrowless">
<fa :icon="faExclamationCircle" />
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
No new notifications
</a>
</div>
</div>
<div class="navbar-item">
<div class="buttons">
<nuxt-link class="button is-primary" to="/signup">
<strong>Sign up</strong>
</nuxt-link>
<nuxt-link id="login" class="button is-light" to="/login">
Log in
</nuxt-link>
</div>
</div>
</div>
</div>
</nav>
</header>
<main class="ch-main">
<nuxt />
</main>
<footer class="ch-footer is-hidden-mobile">
<div class="level">
<div class="level-left">
<div class="level-item">
<a href="#">
<span class="icon">
<fa :icon="faFacebookSquare" />
</span>
</a>
<a href="#">
<span class="icon">
<fa :icon="faTwitterSquare" />
</span>
</a>
<a href="#">
<span class="icon">
<fa :icon="faRedditSquare" />
</span>
</a>
</div>
</div>
<div class="level-right">
<div class="level-item">
©ch, All Rights Reserved
</div>
<div class="level-item">
<nuxt-link to="/contact">Contact</nuxt-link>
</div>
<div class="level-item">
<nuxt-link to="/terms-of-service">Terms</nuxt-link>
</div>
<div class="level-item">
<nuxt-link to="/privacy-policy">Privacy</nuxt-link>
</div>
</div>
</div>
</footer>
</div>
</template>
<script>
import {
faFacebookSquare,
faTwitterSquare,
faRedditSquare,
} from '#fortawesome/free-brands-svg-icons'
import { faMoon, faExclamationCircle } from '#fortawesome/free-solid-svg-icons'
export default {
computed: {
faFacebookSquare() {
return faFacebookSquare
},
faTwitterSquare() {
return faTwitterSquare
},
faRedditSquare() {
return faRedditSquare
},
faMoon() {
return faMoon
},
faExclamationCircle() {
return faExclamationCircle
},
},
mounted() {
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(
document.querySelectorAll('.navbar-burger'),
0
)
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) {
// Add a click event on each of them
$navbarBurgers.forEach((el) => {
el.addEventListener('click', () => {
// Get the target from the "data-target" attribute
const target = el.dataset.target
const $target = document.getElementById(target)
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active')
$target.classList.toggle('is-active')
})
})
}
},
}
</script>
<style></style>
Here is a GIF illustrating the problem
How to close the dropdown after you move to a different page?
Ok, the whole thing makes it complicated, because the hover is triggered by css and therefore the dropdown can always be seen when the mouse is over it. You have to overwrite this state and solve it with vue events. We also have to put a watcher on the route to reset the state.
CodeSandbox - Example
<template>
<div class="container">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<!-- <a class="navbar-item" href="https://bulma.io"> -->
<img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item">Home</a>
<a class="navbar-item">Documentation</a>
<div
#mouseover="toggleDropdown(true)"
#mouseleave="toggleDropdown(false)"
class="navbar-item has-dropdown is-hoverable"
>
<a class="navbar-link">More</a>
<div class="navbar-dropdown" :style="{display: showDropdown ? 'block' : 'none' }">
<nuxt-link class="navbar-item" to="/about">About</nuxt-link>
<nuxt-link class="navbar-item" to="/jobs">Jobs</nuxt-link>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<a class="button is-primary">
<strong>Sign up</strong>
</a>
<a class="button is-light">Log in</a>
</div>
</div>
</div>
</div>
</nav>
<Nuxt/>
</div>
</template>
<script>
export default {
data() {
return {
routeChange: false,
showDropdown: false
};
},
watch: {
$route() {
this.routeChange = true;
this.showDropdown = false;
}
},
methods: {
toggleDropdown(payload) {
if (this.showDropdown !== payload) this.routeChange = false;
if (!this.routeChange) {
this.showDropdown = payload;
}
}
}
};
</script>
This worked form me Bootstrap 5, in touch devices it uses click, on devices with mouse it uses hover
<template>
<span
v-if="item"
class="primary-navigation-list-dropdown"
#mouseover="isTouchscreenDevice ? null : openDropdownMenu()"
#mouseleave="isTouchscreenDevice ? null : closeDropdownMenu()"
>
<nuxt-link
to="#"
#click.prevent.native="openDropdownMenu"
v-click-outside="closeDropdownMenu"
:title="item.title"
:class="[
item.cssClasses,
{ show: isDropdownMenuVisible }
]"
:id="`navbarDropdownMenuLink-${item.id}`"
:aria-expanded="[isDropdownMenuVisible ? true : false]"
class="
primary-navigation-list-dropdown__toggle
nav-link
dropdown-toggle"
aria-current="page"
role="button"
data-toggle="dropdown"
>
{{ item.label }}
</nuxt-link>
<ul
:class="{ show: isDropdownMenuVisible }"
:aria-labelledby="`navbarDropdownMenuLink-${item.id}`"
class="
primary-navigation-list-dropdown__menu
dropdown-menu-list
dropdown-menu"
>
<li
v-for="item in item.children" :key="item.id"
class="dropdown-menu-list__item"
>
<NavLink
:attributes="item"
class="dropdown-menu-list__link dropdown-item"
/>
</li>
</ul>
</span>
</template>
<script>
import NavLink from '#/components/Navigation/NavLink';
export default {
name: "DropdownMenu",
props: {
item: {
type: Object,
required: true,
},
},
data() {
return {
isDropdownMenuVisible: false,
isTouchscreenDevice: false
};
},
mounted() {
this.detectTouchscreenDevice();
},
methods: {
openDropdownMenu() {
if (this.isTouchscreenDevice) {
this.isDropdownMenuVisible = !this.isDropdownMenuVisible;
} else {
this.isDropdownMenuVisible = true;
}
},
closeDropdownMenu() {
if (this.isTouchscreenDevice) {
this.isDropdownMenuVisible = false;
} else {
this.isDropdownMenuVisible = false;
}
},
detectTouchscreenDevice() {
if (window.PointerEvent && ('maxTouchPoints' in navigator)) {
if (navigator.maxTouchPoints > 0) {
this.isTouchscreenDevice = true;
}
} else {
if (window.matchMedia && window.matchMedia("(any-pointer:coarse)").matches) {
this.isTouchscreenDevice = true;
} else if (window.TouchEvent || ('ontouchstart' in window)) {
this.isTouchscreenDevice = true;
}
}
return this.isTouchscreenDevice;
}
},
components: {
NavLink
}
};
</script>
<style scoped lang="scss">
.primary-navigation-list-dropdown {
&__toggle {
color: $white;
&:hover {
color: $blue;
}
}
&__menu {
margin-top: 0;
}
&__dropdown {
}
}
.dropdown-menu-list {
&__item {
}
&__link {
&.active,
&.nuxt-link-exact-active {
border-bottom: 1px solid $blue;
}
}
}
</style>
NavLink.vue
<template>
<component
:is="attributes"
v-bind="linkAttributes(attributes.path)"
:title="attributes.title"
:class="[ attributes.cssClasses ]"
class="nav-link active_"
aria-current="page"
prefetch
>
{{ attributes.label }}
</component>
</template>
<script>
export default {
name: 'NavLink',
props: {
attributes: {
type: Object,
required: true
}
},
methods: {
linkAttributes(path) {
if (path.match(/^(http(s)?|ftp):\/\//) || path.target === '_blank') {
return {
is: 'a',
href: path,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'nuxt-link',
to: path
}
}
}
}
</script>
click-outside.js
import Vue from 'vue'
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
if (!(el == event.target || el.contains(event.target))) {
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent)
},
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent)
},
});
I’m not using Vue, but using Meteor (where similar route-calling trips up the Bulma drop-down removal).
Slightly hacky way to fix it, but the dropdown is made visible because the .has-dropdown element has the .is-hoverable class. So to fix, on any click on the dropdown’s items, I run this:
// Remove the hover effect = dropdown disappears
$(".has-dropdown").removeClass("is-hoverable");
// Tiny time later, put it back, so hover-drop works anew
setTimeout(function() {
$(".has-dropdown").addClass("is-hoverable");
}, 100);
Doesn’t really matter that it would hit all the dropdowns if you have more than one, because resetting them all is harmless when routing to a new page. But if you’re fussy you could target just the closest dropdown.
As Bulma’s mobile view doesn’t activate an on-hover effect anyway, this doesn’t break in the mobile “burger menu” version of my navbar.
Works OK for my project on Chrome, Safari, Firefox.
I would like to set a class to one of my elements when current language I equal to element id. But when method is fired to do this check then I have log "undefined".
Why? How to set that class properly?
Is data object loaded latter then component render?
<template>
<div>
<div class="star-box">
<div class="head">
<img :src="'images/flags/en.png'"
id="en"
class="student-img"
v-bind:class="{'activeLanguage': checkActiveLanguage('en')}"
alt=""
>
</div>
<div class="body">
<h5 class="heading">English</h5>
</div>
</div>
<div class="star-box">
<div class="head">
<img :src="'images/flags/de.png'"
id="de"
class="student-img"
v-bind:class="{'activeLanguage': checkActiveLanguage('de')}"
alt=""
>
</div>
<div class="body">
<h5 class="heading">Deutsch</h5>
</div>
</div>
</div>
</template>
JS
<script>
import {mapActions,mapGetters} from 'vuex';
export default {
name: 'Language',
data() {
return {
language: ''
}
},
methods: {
checkActiveLanguage: (lang)=> {
console.log(this.language);
if(lang==this.language) return true;
},
...mapGetters(['getCurrentLanguage']),
},
beforeMount(){
this.language = this.getCurrentLanguage();
}
}
</script>
I am new to Vue and created a basic authentication application.
AuthService.js
module.exports = {
isLoggedIn: function() {
if (localStorage.getItem("authUser") != null) {
return true;
} else {
return false;
}
},
Logout: function() {
localStorage.removeItem("authUser");
},
}
App.vue
<template>
<div id="app" >
<top-progress ref="topProgress"></top-progress>
<div class="nav is-light is-fixed-top">
<div class="container">
<span class="nav-toggle" v-on:click="toggleNav" v-bind:class="{ 'is-active': isActive }">
<span></span>
<span></span>
<span></span>
</span>
<div class="nav-right nav-menu" v-bind:class="{ 'is-active': isActive }">
<router-link v-ripple to="/" class="nav-item r-item"><i class="fa fa-home"></i>Home</router-link>
<router-link v-ripple to="faq" class="nav-item r-item"><i class="fa fa-file"></i>Features</router-link>
<router-link v-ripple to="dashboard" class="nav-item r-item"><i class="fa fa-dashcube"></i>Dashboard</router-link>
<router-link v-ripple to="faq" class="nav-item r-item"><i class="fa fa-quora"></i>Faq</router-link>
<a class="nav-item r-item" v-if="LoggedIn"><i class="fa fa-sign-out" #click.prevent="Logout"></i>Logout</a>
<div class="nav-item" v-if="!LoggedIn">
<p class="control">
<router-link to="login" class="button is-primary is-outlined">
<span class="icon">
<i class="fa fa-download"></i>
</span>
<span> Join Now</span>
</router-link>
</p>
</div>
</div>
</div>
</div>
<br>
<router-view></router-view>
<footer class="footer is-secondary">
<div class="container">
<div class="columns">
<div class="column">
<p>And this right here is a spiffy footer, where you can put stuff.</p>
</div>
<div class="column has-text-right">
<a class="icon" href="#"><i class="fa fa-facebook"></i></a>
<a class="icon" href="#"><i class="fa fa-twitter"></i></a>
</div>
</div>
</div>
</footer>
</div>
</template>
<script>
import {isLoggedIn,Logout} from "./service"
import miniToastr from 'mini-toastr'
import topProgress from 'vue-top-progress'
export default {
name: 'app',
components:{topProgress},
data:function(){
return {
isActive:false,
LoggedIn:false,
}
},
created(){
this.LoggedIn=isLoggedIn();
},
mounted(){
miniToastr.init()
this.$refs.topProgress.start()
setTimeout(() => {
this.$refs.topProgress.done()
}, 2000)
},
methods:{
Logout:function(){
Logout();
this.$router.push("login");
},
}
}
</script>
<style lang="sass">
..//
</style>
After successful login, I am using this.$router.push("home") to navigate to home component but the Login/Logout button not hiding/showing until I refresh the page.
The problem you are having is the created: hook is only called when App.vue is first created. Since this component holders the router-view it is always there as you move around the app — it's never destroyed, so it never needs to be created again. As a result your this.LoggedIn is only updated when you first load the app (or as you discovered, hit refresh).
You need to find a different way to update this.LoggedIn. One obvious possibility it to set it in the logih/logout methods.
Logout:function(){
Logout();
this.LoggedIn = isLoggedIn(); // or simply this.LoggedIn = false
this.$router.push("login");
},
It looks like users will login in a different component, so you will need to send an event from the child component to App.vue and trigger a method on App.vue to set this.LoggedIn when users login.
There are probably other possibilities that will come to mind now that you see why it isn't working.
Like the title says, Im trying to append a vue-component to an existing v-model
The only thing i found on the internet was the v-for solution to add more elements, but that is not what I want to do since this v-model could have multiple components
chat.vue
The V-model im trying to append to
<template>
<ul class="chat-ul" v-model="messages">
<chat-entry :name="name" :message="text" direction="left"></chat-entry>
</ul>
</template>
Here is where im stuck, how i can add the components
<script>
Vue.component('chat-entry', require('./entry.vue'));
module.exports = {
data: function () {
return {
name: 'Default name',
message: 'Default message',
messages: null,
}
},
mounted: function () {
// This is the what i want to achive
this.messages.append('chat-entry', {direction: 'left', message: 'hello', name: 'HeatON'});
this.$http.get('/etc/yelp.xi').then(response => {
for (let q in response.body.arr) {
this.messages.append('chat-entry', {direction: 'left', message: q.message, name: q.name});
}
});
}
}
entry.vue
<template>
<div v-if="direction == 'left'">
<li>
<div class="message-data">
<span class="message-data-name"><i class="fa fa-circle you"></i> {{ name }}</span>
</div>
<div class="message you-message"> {{ message }} </div>
</li>
</div>
<div v-else>
<li class="clearfix">
<div class="message-data align-right">
<span class="message-data-name">{{ name }}</span> <i class="fa fa-circle me"></i>
</div>
<div class="message me-message float-right"> {{ message }} </div>
</li>
</div>
</li>
Is something like this even possible? if not, what is the proper approach?
Thanks in advance
I'm making an application with Vue.js, currently working on the login/logout functionality. Basically I have in my navbar a button that is supposed to say Login when the user hasn't logged in and logout when the user session is active. The user session information is being stored in the browsers sessionStorage, so basically my question is, what's the best way to handle the communication between the components?
Here's my main component, which has a variable called "currentUser" that will pass to child components as prop for further usage.
<template>
<div id="application">
<div id="navbar">
<navbar :current-user.sync="currentUser"></navbar>
</div>
<router-view :current-user.sync="currentUser"></router-view>
</div>
</template>
<script>
var nav = require('./src/components/navbar.vue');
module.exports = {
components: {
'navbar': nav
},
ready: function(){
this.currentUser = {
userId: window.sessionStorage.getItem('userId'),
username: window.sessionStorage.getItem('user'),
scope: window.sessionStorage.getItem('scope')
}
},
data: function(){
return {
currentUser: {
userId: '',
username: '',
scope: ''
}
}
}
}
</script>
Then we have the navbar component that will basically show/display links according to the users permissions
<template>
<nav>
<div class="nav-wrapper blue darken-4">
DINAF
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li>
<a v-link="'/organization/map'">Mapa de organizaciones</a>
</li>
<li>
<a v-link="'/'">Listar organizaciones</a>
</li>
<li >
<a v-link="{path: '/organization/new'}">Agregar organización</a>
</li>
<li v-if="!isLoggedIn">
<a v-link="{path: '/login'}">Login</a>
</li>
<li v-else>
<span>Welcome {{currentUser.scope}} </span><a v-on:click="logout()" class="waves-effect waves-light btn blue darken-1">Logout</a>
</li>
</ul>
</div>
</nav>
</template>
<script>
var Vue = require('vue');
var config = require('../../config.js');
module.exports = {
name: 'navbar',
props: ['currentUser'],
ready: function(){
this.isLoggedIn = this.currentUser.scope != ''
console.log(this.isLoggedIn)
},
methods: {
logout: function(){
this.$http.get(config.baseUrl() + '/v1/logout').then(function(response){
window.sessionStorage.removeItem('user');
window.sessionStorage.removeItem('userId');
window.sessionStorage.removeItem('scope');
this.currentUser = {
userId: '',
username: '',
scope: ''
}
this.$route.router.go('/');
},function(error){
console.log(error);
})
}
},
data: function(){
return {
isLoggedIn: false,
}
}
};
</script>
And the login component that logs in and saves the information into the local storage
<template >
<div class="loginContainer">
<div class="row">
<div class="col s6 offset-s3 ">
<div class="card blue-grey darken-2 white-text">
<div class="card-content">
<div class="row">
<div class="input-field col s12">
<i class="material-icons prefix">email</i>
<input id="username" type="text" v-model="user.username" data-error="Ingrese su nombre de usuario" class="validate">
<label for="username">Username</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<i class="material-icons prefix">lock</i>
<input id="password" type="password" v-model="user.password" class="validate">
<label for="password"><i class="fa fa-lock" aria-hidden="true"></i> Contraseña</label>
</div>
</div>
</div>
<div class="card-action">
<a type="submit" id="loginButton" class="waves-effect waves-light btn blue-grey darken-4" v-on:click="logIn">Iniciar Sesión</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
var swal = require('sweetalert');
var config = require('../../config.js');
module.exports = {
name: 'login',
props: ['currentUser'],
methods: {
logIn: function(){
this.$http.post(config.baseUrl() + '/v1/login', this.user).then(function(response){
this.currentUser = {
username: response.json().user,
userId: response.json()._id,
scope: response.json().scope
}
window.sessionStorage.setItem('user', this.currentUser.username);
window.sessionStorage.setItem('userId', this.currentUser.userId);
window.sessionStorage.setItem('scope', this.currentUser.scope);
console.log(this.currentUser)
this.$route.router.go('/');
},function(error){
swal('Error', 'Usuario o password incorrecto', 'error');
});
}
},
data: function(){
return {
user: {},
}
}
}
</script>
The problem I have right now is that when you log in, the sessionStorage is being updated but not the data that's on the navbar component so the login in the navbar never changes.