UserStore (Vuex) in vue and Laravel 5.4 - vue.js

I have promble to implement vuex
this my code :
laravel/resources/assets/js/app.js
import router from './routes.js';
import store from './store.js'
require('./bootstrap');
router.beforeEach((to,from,next) => {
if(to.matched.some(record => record.meta.requiresAuth)){
const authUser = JSON.parse(window.localStorage.getItem('authUser'))
if(authUser && authUser.access_token){
next()
}else{
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
}
next()
})
Vue.component('top-menu',require('./components/topMenu.vue'))
const app = new Vue({
el: '#app',
router,store
});
laravel/resources/assets/js/store.js
import Vue from 'vue'
import Vuex from 'vuex'
import userStore from './components/user/userStore.js'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !=='production'
export default new Vuex.Store({
module:{
userStore
},
strict: debug
})
laravel/resources/assets/js/components/user/userStore.js
const state = {
authUser: null
}
const mutations = {
SET_AUTH_USER (state, userObj){
state.authUser = userObj
}
}
const actions ={
setUserObject: ({commit}, userObj) => {
commit('SET_AUTH_USER',userObj)
}
}
export default {
state, mutations, actions
}
this topMenu laravel/resources/assets/js/components/topMenu.vue
<script>
import {mapState} from 'vuex'
export default {
computed: {
mapState(){
userStore: state => state.userStore
}
},
created() {
const userObj = JSON.parse(window.localStorage.getItem('authUser'))
this.$store.dispatch('setUserObject',userObj)
}
}
</script>
<template>
<div>
<pre>{{ userStore }}</pre>
<nav class="navbar navbar-default navbar-static-top" >
<div class="container">
<div class="navbar-header">
<!-- Collapsed Hamburger -->
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- Branding Image -->
<a class="navbar-brand" href="/">
Laravel
</a>
</div>
<div class="collapse navbar-collapse" id="app-navbar-collapse">
<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
</ul>
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right" v-if="userStore.authUser !== null && userStore.authUser.access_token">
<!-- Authentication Links -->
<router-link to="/login" tag="li"><a>Login</a></router-link>
<router-link to="/register" tag="li"><a>Register</a></router-link>
<router-link to="/vendor/profile" tag="li"><a>Profile</a></router-link>
</ul>
</div>
</div>
</nav>
</div>
</template>
if I run the code I have erro
[vuex] unknown action type: setUserObject
[Vue warn]: Property or method "userStore" is not defined on the
instance but referenced during render. Make sure to declare reactive
data properties in the data option. (found in at
/var/www/html/wingding/resources/assets/js/components/topMenu.vue)
[Vue warn]: Error in render function: (found in at
/var/www/html/wingding/resources/assets/js/components/topMenu.vue)
TypeError: Cannot read property 'authUser' of undefined
[Vue warn]: Error in mounted hook: (found in at
/var/www/html/wingding/resources/assets/js/components/Home.vue)
Please help me and thanks!!

The error:
TypeError: Cannot read property 'authUser' of undefined
Should get fixed by adding following null check.
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right" v-if="userStore && userStore.authUser && userStore.authUser.access_token">

Related

How to pass props to sibling and child component in vue

The structure of my code is like this:
So in the Product component, I am making an API call:
<template>
<button class="btn button col-2" #click="addToCart()">
Add to cart
</button>
</template>
<script>
methods:{
addToCart: function () {
let amount = this.itemsCount !== "" ? this.itemsCount : 1;
if(this.variationId != null) {
this.warningMessage = false;
cartHelper.addToCart(this.product.id, this.variationId, amount, (response) => {
this.cartItems = response.data.attributes.items;
});
} else {
this.warningMessage = true;
}
console.log(this.cartItems)
},
}
</script>
And what I am trying to do is the response (this.cartItems) should be shown in Cart component. And my Navbar component:
<template>
<nav class="navbar navbar-expand-lg shadow">
<div class="container navbar-container">
<div class="navbar navbar-profile">
<div class="dropdown">
<button class="btn dropdown-toggle" type="button" id="dropdownCart" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-cart-arrow-down"></i>
<span></span>
</button>
<div #click="$event.stopPropagation()">
<CartBox :cartItems="cartItems"/>
</div>
</div>
</div>
</div>
</nav>
</template>
<script>
export default {
props: {
cartItems:Object
},
components: {CartBox},
}
And CartBox:
<template>
<Cart/>
</template>
<script>
import Cart from '../components/Cart'
export default {
components: {
Cart
}
}
</script>
And my Cart component:
<template>
<div
class="dropdown-menu cart"
aria-labelledby="triggerId"
>
<div class="inner-cart">
<div>
<div class="cart-items">
<div>
<a class="remove">Remove</a>
</div>
</div>
</div>
<hr/>
<div class="cart-items-total">
<span>Total:</span>
Clear Cart
</div>
<hr/>
<router-link :to="{name: 'order'}" class="btn button-secondary">Go To Cart</router-link>
</div>
</div>
</template>
<script>
export default {
computed: {
},
methods: {
}
};
</script>
I am really confused how to pass the props to sibling component and then the child component but if you could pass it to Cart component, that would really help me.
There are two approaches for your request:
1. Using props, provide and inject
This could be accomplished with Provide / inject, after passing your response to a parent. Basically, you will emit your response from your Product component to a parent, maybe like your App.vue as the prop myData, then you provide it for every child, no matter where it is nested, like this:
provide: {
providedData: this.myData
}
In any child you can now use:
inject: ['providedData']
Please note, that this data will only be available if your Product component received it. The second approach is recommended.
2. Using a store
Using a store like vuex is a bit more complex than approach 1, but it will save a lot of time in the future. You would recieve your response in your Product component, dispatch it to the store and could call the state of information from this store anywhere in your app. See further information in this documentation: Vuex | Getting Started

How to render the content of an object in vue?

apologies if that's too basic, but I'm stuck.
I have created an object in vue with three properties (slug, title and content). I successfully console.logged the object. How can I now use the object in my page in order to render its content?
There is no need for me to loop through the object, at it has only one item in it.
<template>
<div class="relative py-16 overflow-hidden bg-white">
<div class="relative px-4 sm:px-6 lg:px-8">
<div class="mx-auto text-lg max-w-prose">
<h1>
<span
class="block text-base font-semibold tracking-wide text-center text-indigo-600 uppercase"
>Hello</span
>
<span
class="block mt-2 text-3xl font-extrabold leading-8 tracking-tight text-center text-gray-900 sm:text-4xl"
>Here is the name</span
>
</h1>
<p class="mt-8 text-xl leading-8 text-gray-700"></p>
<div
v-bind="this.data.content"
class="text-lg font-medium leading-6 text-gray-900"
></div>
</div>
</div>
</div>
</template>
<script>
const Cosmic = require("cosmicjs");
const api = Cosmic();
const bucket = api.bucket({
slug: "((BUCKETNAME))",
read_key: "((KEY))",
});
const data = bucket
.getObject({
id: "((BUCKET ID))", // Object ID
props: "slug,title,content", // get only what you need
})
.then((data) => {
const about = data.objects;
console.log(data);
});
export default {
name: "data",
data() {
return {
data,
};
},
};
</script>
As other commenters have suggested, it would be useful to read the Vue syntax guide here https://v2.vuejs.org/v2/guide/syntax.html
But to answer your question with the most minimal of code changes, you'd want to move your data request to the lifecycle hook of your vue component.
<template>
<h1>{{ dataObjects.title }}</h1>
<p>{{ dataObjects.slug }}</p>
<p>{{ dataObjects.content }}</p>
</template>
<script>
export default {
name: "data",
data() {
return {
dataObjects: null,
};
},
mounted() {
bucket.getObject({
id: "((BUCKET ID))", // Object ID
props: "slug,title,content", // get only what you need
})
.then((data) => {
// Assign the return value to the dataObjects propery of the vue instance.
this.dataObjects = data;
});
}
};
</script>
In the template section, you can see that I've used curly braces to render the contents of dataObjects (I wasn't sure what structure your data is in).
You can also learn from examples on the Vue Cookbook site

Error : vue Maximum call stack size exceeded

i'm building an app, i tried to do router for login page but it give me this error :
vue.runtime.esm.js?2b0e:4484 Uncaught RangeError: Maximum call stack size exceeded, is there anything i did wrong
thank you for your help hopefully u can give me a hint on how to fix it
this is the Login Components
<template>
<div class="hello">
Login
</div>
</template>
<script>
export default {
name: '',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
Home Components
<template>
<div class="hello">
<header class="globalNav noDropdownTransition">
<div class="container-lg">
<ul class="navRoot">
<li class="navSection logo">
<a class="rootLink item-home colorize" href="/"><h1>Charity Finder</h1></a>
</li>
<li class="navSection primary">
<a class="rootLink item-products hasDropdown colorize" data-dropdown="products">
Products
</a>
<a class="rootLink item-developers hasDropdown colorize" data-dropdown="developers">
Developers
</a>
<a class="rootLink item-company hasDropdown colorize" data-dropdown="company">
Company
</a>
</li>
<li class="navSection secondary">
<a class="rootLink item-support colorize" href="">
Support
</a>
<router-link class="rootLink item-dashboard colorize" to="/login"> Sign in</router-link>
</li>
</ul>
</div>
</header>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
This the router Router
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: Login
}
]
const router = new VueRouter({
routes
})
export default router

Laravel Vuejs2 missing param for named route

I am having a hard time trying to isolate the console error.
My code is as follows:
myside.blade.php has:
<div class="nav-container">
<ul class="nav nav-icons justify-content-center" role="tablist">
<li class="nav-item">
<div>
<router-link class="nav-link text-primary" :to="{ name: 'property', params: { customerId: {{ $property_data[0]->listingId }} }}" #click.native="isVisible = !isVisible">
<i class="nc-icon nc-key-25"></i>
<br> Property
</router-link>
</div>
</li>
<li class="nav-item">
<router-link class="nav-link text-primary" :to="{ name: 'booking' }" #click.native="isVisible = !isVisible">
<i class="nc-icon nc-chart-pie-36"></i>
<br> Bookings
</router-link>
</li>
<li class="nav-item">
<a class="nav-link text-primary" href="" role="tab">
<i class="nc-icon nc-preferences-circle-rotate"></i>
<br> Performance
</a>
</li>
<li class="nav-item">
<a class="nav-link text-primary" href="" role="tab">
<i class="nc-icon nc-money-coins"></i>
<br> Revenue
</a>
</li>
<li class="nav-item">
<a class="nav-link text-primary" href="" role="tab">
<i class="nc-icon nc-layers-3"></i>
<br> Integration
</a>
</li>
</ul>
</div>
<property-page :customer-Id="{{ $property_data[0]->listingId }}" v-if="isVisible"></property-page>
<router-view></router-view>
My routes.js file:
import VueRouter from 'vue-router';
let routes = [
{
path: '/property/:customerId',
name: 'property',
component: require('./components/PropertyPage'),
props: true
},
{
path: '/booking',
name: 'booking',
component: require('./components/BookingPage')
}
];
export default new VueRouter({
routes,
linkActiveClass: 'nav-link text-success'
});
my app.js file:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
import router from './routes';
import PropertyPage from './components/PropertyPage.vue';
import BookingPage from './components/BookingPage.vue';
new Vue({
el: '#root',
router,
data: {
NavPrimaryClass: 'nav-link text-primary',
NavClass: 'nav-link',
isVisible: true
},
components: {
'property-page': PropertyPage,
'booking-page': BookingPage
}
})
my PropertyPage.vue file:
<template>
<div>
<div class="ajax-loader">
<img src="/loader/ajax-loader.gif" width="300px" v-if="loading" />
</div>
<div v-if="propertyDataCheck">
</div>
</div>
</template>
<script>
import moment from 'moment';
import axios from 'axios';
export default {
props: {
customerId: {
type: Integer,
required: true,
default: 0
}
},
data() {
return {
propertyData: [],
loading: false
}
},
computed: {
propertyDataCheck () {
return this.propertyData.length;
}
},
mounted() {
this.loading = true;
axios.get('/ajax/propertydata/' + this.customerId)
.then(function(response) {
this.propertyData = response.data;
this.loading = false;
}.bind(this))
.catch(function() {
this.loading = false;
}.bind(this));
}
}
</script>
<style>
.ajax-loader {
position: absolute;
left: 40%;
top: 15%;
margin-left: -32px; /* -1 * image width / 2 */
margin-top: -32px; /* -1 * image height / 2 */
}
</style>
The end result, a console error which I have spent hours trying to work out where it is coming from.
Error:
[vue-router] missing param for named route "property": Expected "customer" to be defined
I needed a component to be loaded upon page load (outside of vue-router) which is why I have <property-page :customer-Id="{{ $property_data[0]->listingId }}" v-if="isVisible"></property-page> tags and is bound to a method which will switch upon a router-link click (in case you were wondering).
My understanding of this error is the variable for the first router-link which is generated from a Laravel model DB query {{ $property_data[0]->listingId }} should be checked within a v-if wrapped around the vue-router? I have also done this to no avail.
Any help would be much appreciated!
Believe it or not, you need to use strict kebab-case (all lower case!) html-properties in vue-templates. Change customer-Id to customer-id
<property-page :customer-Id=... // does not get mapped to this.customerId
<property-page :customer-id=... // does get mapped to this.customerId, yay!

Vue.js Router change url but not view

I see this bug in console:
[Vue warn]: Property or method "product" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
My template id="productDetail" not receive the property "product" of the template id="product" I don't know how I can push this, please see my cod.
HTML LIST
That's ok when I click the router-link the url change to:
/product/iphone-x-64-gb for example.
<template id="product" functional>
<div class="wrapper">
<ul class="row">
<li v-for="(product, index) in products" class="col l4 m6 s12">
<div class="card-box">
<div class="card-image">
<img :src="product.images" :alt="product.images" class="responsive-img"/>
</div>
<div class="card-content">
<h3>{{ product.brand }}</h3>
<span class="price-used"><i class="used">{{ index }} gebrauchte Roomba 651</i></span>
</div>
<div class="card-action row">
<span class="col s6 price"><span>{{ product.price }}</span>
</div>
<div>
<router-link class="btn btn-default light-green darken-3" :to="{name: 'product', params: {product_id: product.id}}">meer detail</router-link>
</div>
</div>
</li>
</ul>
</div>
HTML PRODUCT DETAIL (THAT NO RECEIVE THE "product")
<template id="productDetail" functional>
<div class="row">
<div class="col s12 m6">
<img src="images/iphone-8-64-gb.jpg" alt="product.images" class="responsive-img"/>
</div>
<div class="col s12 m6">
<h3>{{ product.title }}</h3>
<h5>{{ product.price }}<h5>
<div class="col s12 m6">
<a class="waves-effect waves-light btn light-green darken-3"><i class="material-icons left">add_shopping_cart</i>kopen</a>
</div>
<div class="col s12 m6">
<router-link class="btn btn-default light-green darken-3" :to="{path: '/'}">
<span class="glyphicon glyphicon-plus"></span><i class="material-icons left">arrow_back</i>terug
</router-link>
</div>
</div>
</div>
THE .JS
var List = Vue.extend(
{
template: '#product',
data: function ()
{
return {products: [],};
},
created: function()
{
this.$http.get('https://api.myjson.com/bins/17528x').then(function(response) {
this.products = response.body.products;
}.bind(this));
},
});
const Product =
{
props: ['product_id'],
template: '#productDetail'
}
var router = new VueRouter(
{
routes: [
{path: '/', component: List},
{path: '/product/:product_id', component: Product, name: 'product'},
]
});
var app = new Vue(
{
el: '#app',
router: router,
template: '<router-view></router-view>'
});
Thank for your help.
Your props should be props: ['product'] instead of props: ['product_id']
<parent-component :product="product"></parent-component>
ChildComponent.vue
export default {
name: 'child-component',
props: ['product']
}
First activate props on the route:
var router = new VueRouter({
...
path: '/product/:product_id',
component: Product,
name: 'product',
props: true // <======= add this line
},
...
Now the product_id will be set on the Product component.
So, you want to display the whole product information, but at this moment you only have the product_id. The solution is to fetch the product:
const Product = {
props: ['product_id'],
template: '#productDetail',
data: function() { // <============ Added from this line...
return {
product: {} // add {} so no error is thrown while the product is being fetched
};
},
created: function() {
var productId = this.product_id;
// if there is a URL that fetches a given product by ID, it would be better than this
this.$http.get('https://api.myjson.com/bins/17528x').then(function(response) {
this.product = response.body.products.find(function (product) { return product.id == productId });
}.bind(this));
} // <============ ...to this line.
}
Check JSFiddle demo here of the solution above.
Alternative solution: passing the whole product as prop
Pass the product in the params: (along with product_id):
<router-link class="btn btn-default light-green darken-3" :to="{name: 'product',
params: {product_id: product.id, product: product}}">meer detail</router-link>
^^^^^^^^^^^^^^^^^^
Activate props on the route:
var router = new VueRouter({
...
path: '/product/:product_id',
component: Product,
name: 'product',
props: true // <======= add this line
},
...
Finally, add product so you can use it:
const Product = {
props: ['product_id', 'product'], // <======= added 'product' here
template: '#productDetail'
}
Demo JSFiddle for this solution here.