VueJS: Router Children in Children - vue.js

i want to do two different operations,
domain.com/myaccount
When I log in, the menu I specified below appears and I can do whatever I want from this menu, but,
//User information
When I click edit in the dropdown menu content
well
path
domain.com/myaccount/edit
when i login
//Basket Transactions
name: 'cart.show',
domain.com/myaccount/
It looks like it's opened.
How do I tell they are different?
How should I go about the router-link?
const routes = [
{
path: '/myaccount',
name: 'myaccount',
component: MyaccountView,
children: [
//User information
{
path: '',
name: 'profile.show',
component: RouterView,
children: [
{
path: 'edit',
name: 'edit.show',
component: EditView
}
]
},
//Basket Transactions
{
path: '',
name: 'cart.show',
component: RouterView,
children: [
{
path: 'buy',
name: 'buy.show',
component: BuyView
}
]
}
]
},
{path: '*', component: NotFound}
]
export default routes
<ul>
<!-- User information -->
<li>
<router-link custom :to="{name:'profile.show'}" v-slot="{href, route, navigate,isActive}">
<div>
<span>Profile</span>
</div>
</router-link>
<ul class="dropdown">
<li>
<router-link :to="{name:'edit.show'}">
<span>Edit</span>
</router-link>
</li>
</ul>
</li>
<!-- Basket Transactions -->
<li>
<router-link custom :to="{name:'cart.show'}" v-slot="{href, route, navigate,isActive}">
<div>
<span>Cart</span>
</div>
</router-link>
<ul class="dropdown">
<li>
<router-link :to="{name:'buy.show'}">
<span>Basket</span>
</router-link>
</li>
</ul>
</li>
</ul>
<div>
<router-view />
</div>

Related

How to emit event in Vue.js depending on the width of the screen

I've got the following code from my sidebar. When any link is clicked, I'm also emitting an event to my root component and then invoking a function to hide the sidebar. This is working well but now I would like the event to be emitted only when the width of the screen is less than 768px so that I can emit the event only when I want to invoke the function.
<div class="side-links">
<ul>
<li>
<router-link #click="$emit('navLinkClicked')" :to="{ name: 'Home' }"
>Home</router-link
>
</li>
<li>
<router-link #click="$emit('navLinkClicked')" :to="{ name: 'Blog' }"
>Blog</router-link
>
</li>
<li>
<router-link #click="$emit('navLinkClicked')" :to="{ name: 'About' }"
>About</router-link
>
</li>
</ul>
</div>
You can use window.innerWidth to check the width of the current viewport. Global scope variables however is not available in the template, because its scoped to your component. You can either create a handler method:
<script>
methods: {
onNavLinkClicked() {
if (window.innerWidth < 768) {
this.$emit('navLinkClicked')
}
}
}
</script>
<template>
<div class="side-links">
<ul>
<li>
<router-link
#click="onNavLinkClicked"
:to="{ name: 'Home' }"
>
Home
</router-link
</li>
</ul>
</div>
</template>
or create a computed variable that defines if the width is below the treshold:
<script>
computed: {
isMobile() {
return window.innerWidth < 768;
}
}
</script>
<template>
<div class="side-links">
<ul>
<li>
<router-link
#click="isMobile ? $emit('navLinkClicked') : null"
:to="{ name: 'Home' }"
>
Home
</router-link
</li>
</ul>
</div>
</template>
Personally i'd go with the first option, so you'll only have to do the check once.
In Vue 2 it is also recommended to use kebab-case for event names.

Nested routes don't follow the defined pattern. VUEjs

I'm facing a new issue with nested routes. I've 2 levels of nested routes, and they're not working correctly.
Here are the routes object:
import Login from './components/auth/Login.vue';
import Dashboard from './components/dashboard/Dashboard.vue';
import Signup from './components/auth/Signup.vue';
import Home from './components/shared/Home.vue';
import ProductsHome from './components/dashboard/Products/ProductsHome.vue';
import ProductOverview from './components/dashboard/Products/ProductsOverview.vue';
import CreateProduct from './components/dashboard/Products/CreateProduct.vue';
import CreateCategory from './components/dashboard/Category/CreateCategory.vue';
import EditCategory from './components/dashboard/Category/EditCategory.vue';
import {store} from './store/store.js';
export const routes = [
{path: '/', component: Home},
{ path: '/login', component: Login },
{ path: '/registrarse', component: Signup },
{
path: '/dashboard',
component: Dashboard,
name: 'dashboard',
beforeEnter: (to, from, next) => {
if(to.name === 'dashboard') {
if(store.state.User.credentials.tokenId === null) {
next('/');
//TODO, Hacerlo global y verificar que el dashboard y sus hijos no accedan.
} else {
next();
}
}
next();
},
children: [
{
path: 'productosHome',
component: ProductsHome,
children: [
{
path: '',
component: ProductOverview
},
{
path: 'crearProducto',
component: CreateProduct
},
{
path: 'crearCategoria',
component: CreateCategory
},
{
path: 'editarCategorias',
component: EditCategory
}
]
}
]
}
];
The thing is with the dashboard. When I enter the dashboard or any of its sub-routes (with its router-link component) they don't follow the proper URL path. For example: If a visit the 'productosHome' the URL it's just '/productosHome' and not '/dashboard/productosHome. This same problem applies for every productosHome's child route.
Now, let me show to you my templates:
Dashboard.vue
<template>
<div>
<div class="container is-fluid has-background-light">
<h1 class="title has-text-centered pt-3">Dashboard</h1>
</div>
<section class="section py-3 has-background-light prueba section-dashboard">
<div class="container is-fluid remove-padding dashboard">
<aside class="nav-aside has-background-white">
<div>
<!--El anchor sera nuestro router-link-->
<!-- aside-link-active -->
<a href="#" class="aside-link">
<span class="icon">
<i class="fas fa-users"></i>
</span>
<span class="aside-link-text">Clientes</span>
</a>
</div>
<div>
<router-link class="aside-link"
active-class="aside-link-active"
to="productosHome">
<span class="icon">
<i class="fas fa-tags"></i>
</span>
<span class="aside-link-text">Productos</span>
</router-link>
</div>
<div>
<a href="#" class="aside-link">
<span class="icon">
<i class="fas fa-boxes"></i>
</span>
<span class="aside-link-text">
Pedidos
</span>
</a>
</div>
</aside>
<div class="main-content has-background-white">
<router-view></router-view>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.aside-link.aside-link-active:hover {
color: white;
}
</style>
ProductsHome.vue
<template>
<div>
<div class="tabs is-medium pt-2 mb-0">
<ul>
<!-- is-active -->
<router-link to="crearProducto"
active-class="is-active"
tag="li">
<a>Crear</a>
</router-link>
<li><a>Editar</a></li>
<li><a>Buscar</a></li>
<router-link to="crearCategoria"
active-class="is-active"
tag="li">
<a>| Crear categoría</a>
</router-link>
<router-link to="editarCategorias"
active-class="is-active"
tag="li">
<a>Editar categoria</a>
</router-link>
</ul>
</div>
<div class="main-content__content block p-2">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
Note that Products Home also have children.
Also, I'm debugging routes with the famous Vue plugin and see what says:
'/' is marked has active, Is that correct?
I'm confused :D
The routes file is correct. The issue is with the router link on both files. there are 2 ways you can define router-link>to.
Absolute / Relative Path.
Absolute Path(From any file)
<router-link to="/dashboard/productosHome/">Products Home</router-link>
Relative Path(From dashboard page)
<router-link to="productosHome">Products Home</router-link>
Route object. (From any file)
<router-link :to="{ path: `/dashboard/productosHome/`}">Products Home</router-link>
No 2 gives you a bit more control. you can now name the route and use them. Like you did for dashboard main route. name: 'dashboard',. Now you can name the subroute.
{
path: 'crearProducto',
component: CreateProduct,
name: 'crearProducto'
},
router link will be
<router-link :to="{ name: 'crearProducto'}">Crear Producto</router-link>
Check Documentation for more https://router.vuejs.org/api/#to
When declaring paths in the to attribute of router-link, you need to define the full path including the root "/". E.g.: "/dashboard/productosHome"

Trouble iterating over array using :v-for

I am attempting to iterate over a simple array using the v-for directive in my Nuxt.js App. Please see below.
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<nuxt-link class="navbar-brand" to="/">
<img class="image nav-logo" :src="logoSrc" alt="Logo" />
</nuxt-link>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav" >
<li :v-for="link in links" :key="link.label">
{{ link.label }}
</li>
</ul>
</div>
</nav>
</template>
<script>
const linkArray = [
{
label: "Home",
href: "/",
class: ""
},
{
label: "ABout",
href: "/",
class: ""
},
{
label: "Our Menu",
href: "/",
class: ""
},
{
label: "Contact Us",
href: "/",
class: "db-outline-cta"
}
]
export default {
name: "Nav",
data() {
return {
logoSrc: '/img/davidsbarlogo.png',
links: linkArray
}
}
}
</script>
As you can see, this is my component. I am going to be dynamically getting data for this component inside asyncData() later on when my cms is wired up, but I wanted to have some placeholder content.
I am repeatedly getting this error:
ERROR [Vue warn]: Error in render: "TypeError: Cannot read property 'label' of undefined" 00:00:49
I have tried with and wihtout the :key property, I know I should include one. I am fairly new to vue, if anyone has a recommendation I would be most grateful.
<ul class="navbar-nav">
<li v-for="link in links" :key="link.label">{{link.label}}</li>
</ul>
Works like a charm
https://codesandbox.io/s/recursing-kapitsa-rnr4h
Try this
<li v-for="(link, index) in links" :key="index">
{{ link.label }}
</li>
However, using index as key isn't the best practice (as explained in this post) but since there's no truly unique id in your link object, this will do.

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.