In Vue Route, the parent path component display with the child path component - vue.js

I have 2 components, AboutView.vue and HomeView.vue.
AboutView.vue
<template>
<div class="about">
<h1>This is an about page</h1>
<router-link to="/about/add">Add</router-link>
<router-view></router-view>
</div>
</template>
HomeView.vue
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
When I click the 'Add' button, I will be redirected to /about/add where /add is config inside index.js
{
path: '/about',
name: 'about',
component: AboutView,
children:[
{
path: 'add',
name: 'Add',
component: ()=> import ('../views/HomeView.vue'),
},
]
}
The path is working fine.
But the problem is when I click the 'Add' button, what I would expect is it brought me to the HomeView.vue, but instead, it showed the HomeView.vue below the Add button.
Here is the screen.
ScreenShot
it seems like the route-view is a part of the template, but I want it to display the HomeView.vue instead of AboutView + HomeView.
I tried to use <router-view name='name></router-view>, which doesn't help much. I can write a js function to check the path and make the code above disappear, but I expect something more professional.

Let's try to understand from the below image-
router-view is basically the view where the components are rendered.
It’s like the main div that contains all the components, and it
returns the component that matches the current route.
So, to fix this just remove the router-view from AboutView.vue component, so when you will redirect to /home, the component HomeView.vue, will render inside App.vue's router-view.

Related

Vue subrouting with navbar and sidebar

I'm trying to set up a routing system with vue. For my purpose, I need a fixed navbar on the top that needs to be displayed on every page and a sidebar that I want to display only on the settings page. Following the documentation I tried:
const routes = [
{
path: '/settings',
name: 'Settings',
component: Settings,
children: [
{
path: 'route1',
name: 'Route1',
component: Route1
},
{
path: 'route2',
name: 'Route2',
component: Route2
}
]
}
]
Then on the settings template:
<template>
<div class="flex items-start">
<div class="lg:w-3/12 w-12 sm:w-16 md:w-24 pb-10 lg:pr-8">
<Sidebar />
</div>
</div>
<div class="lg:w-9/12 w-full pt-10 pb-8 text-justify">
// My subroute goes here
</div>
</template>
I feel that I'm missing something. First, I can't understand how to properly display the subroutes. I tried with <router-view /> but it seems to refer to the parent navigation.
Second, I don't want the user to visit the /settings route but only /settings/route1 and settings/route2.
I can achieve this by simply adding the sidebar in every settings route but this seems bad because it forces the <Sidebar/> component to be mounted every time
Where am I wrong?
Thanks
As you probably have guessed, the <router-view /> element goes in your Settings component:
<template>
<div class="flex items-start">
<div class="lg:w-3/12 w-12 sm:w-16 md:w-24 pb-10 lg:pr-8">
<Sidebar />
</div>
</div>
<div class="lg:w-9/12 w-full pt-10 pb-8 text-justify">
<router-view /> <!-- Here is your router view -->
</div>
</template>
Then as it was pointed out in the comments, /settings will always be a valid route.
What you can do when the client directly navigates to /settings is to replace the current route with one of the two children (possibly based on some logic) in the mounted hook:
mounted() {
if(this.$router.currentRoute.path.endsWith('/settings')) {
this.$router.replace('/settings/route1')
}
}
Or use $router.push() instead based on what you want the navigation history to look like.

Nuxt.js could not found component with errors "Failed to mount component: template or render function not defined."

I am having problems with my Nuxt.js site.
I have defined a page, with a dynamic slug param, like this:
/solutions/:slug
If I visit the page directly in the browser, it loads correctly!
But if I click the nuxt-link in my NavBar component, I get the following error in the console, and the page does not load:
vue.runtime.esm.js?2b0e:619
[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <Anonymous>
<Nuxt>
<Layouts/default.vue> at layouts/default.vue
<Root>
I have a default layout file that looks like this:
layouts/default.vue
<template>
<div>
<NavBar />
<Nuxt />
</div>
</template>
<script>
import NavBar from "~/components/Layout/NavBar"
export default {
components: {
NavBar,
},
}
</script>
My navbar contains the following nuxt-link:
components/Layout/NavBar.vue
<template>
<b-navbar wrapper-class="container" fixed-top>
<template slot="end">
<nuxt-link
:to="{
name: 'solutions-slug',
params: { slug: 'performance' },
}"
class="navbar-item"
target="self"
>
<span class="icon is-medium">
<i class="ic ic--larger ic-b1-graph-bars-chart-rise" />
</span>
<span class="label">
Performance
</span>
</nuxt-link>
</template>
</b-navbar>
</template>
I have a page, defined by the slug param:
pages/solutions/_slug.vue
<template>
<div class="solution">
This is my solution page.
</div>
</template>
I am trying to understand why clicking the nuxt-link fails to load the page, even though I see the URL change in the browser correctly.
Thanks
After version v2.13, Nuxt can auto-import your components when used in your templates.
check the nuxt.config.js if components attribute is true then you don't need to import your component on the .vue files.
in your layouts/default.vue remove script tag ;-)
<template>
<div>
<NavBar />
<Nuxt />
</div>
</template>
If you need to categorize your components by folder, do the following.
goto nuxt.config.js and change your components attribute
export default {
components: {
dirs: [
'~/components',
{
path : '~/components/site/',
prefix: 'Site'
},
{
path : '~/components/admin',
prefix: 'Admin'
},
{
path : '~/components/admin/sub',
prefix: 'AdminSub'
}
]
}
}
for example, we have these components :
components
| site
- header
| admin
- header
- footer
| sub
- header
- footer
when we need to call components just separate prefixes and component names with a dash or write camelcase.
in your layouts/default.vue remove script tag ;-)
<template>
<div>
<!-- with dash -->
<site-header></site-header>
<admin-header></admin-header>
<admin-sub-header></admin-sub-header>
<!-- cammel -->
<SiteHeader></SiteHeader>
<AdminHeader></AdminHeader>
<AdminSubHeader></AdminSubHeader>
</div>
</template>
Attention: For Root Components /components/nav.vue, We Must Use CammelCase <Nav/> and if we call this component like this <nav/> it doesn't work.
Probably the problem is not related to anything described above.
First, check if your configuration is correct. I see you are using 'nuxtjs/content' module, so you are probably using Contentful as well. In the past, I have encountered a similar situation ('Failed to mount component: template or render function not defined' issue) due to incorrect installation of the 'dotenv' module that I used to store variables.
In my case, the application did not load variables from the .env file. As a consequence, they went to the Contentful client unidentified and caused the js error. For some reason, this error did not always appear in the console. Instead, the above-mentioned Warn appeared.
Make sure you have the 'dotenv' module correctly installed (if you use it). I remember that in my case it was necessary to install 'nuxtjs/dotenv' instead of the usual dotenv.
Let me know if this is the case. Good luck

Set up router for subpages in Vue.js

I wounder how I best set up the router in Vue.js for handling ”subpages”. For example I got a navbar that routes to different pages. From one of these pages I want to have links to subpages. How do I best set this up?
I have done like this so far:
App.js
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</div>
</template>
Then I set up my router:
export default new Router({
routes: [
{
path: "/",
name: "home",
component: Home
},
{
path: "/about",
name: "about",
component: About,
children: [
{
path: "/child1",
name: "child1",
component: Child1
}
]
}
]
})
And my About.vue where I provide the link to Child1
<template>
<div class="about">
<h1>This is an about page</h1>
<router-link to="/child1">Child1</router-link>
<router-view></router-view>
</div>
</template>
And finally my Child1.vue
<template>
<div class="child1">
<p>My message</p>
</div>
</template>
My problem is that the link to Child1 is displayed both on the About page and on Child1 page. I just want to display it on the about page and only the content from the Child1 on the Child1 page
How is the best practice of setting up things like this?
Thanks
My problem is that the link to Child1 is displayed both on the About page and on Child1 page. I just want to display it on the about page
Just to clarify what's happening here: the link to Child1 is always visible within the About component even if child routes are active, but you don't want to show the link when the child route is active.
Way 1
You can provide fallback content to <router-view> when there is no matching route (i.e. when no child route is active). This would be a good opportunity to show the link.
<template>
<div class="about">
<h1>This is an about page</h1>
<router-view>
<router-link to="/child1">Child1</router-link>
</router-view>
</div>
</template>
Way 2
The above solution may not work if your template is more complicated and if you want to situate the link elsewhere in the template.
So you'll have to manually control the visibility of the link by using v-if so that it is only visible when the child route is not active.
<template>
<div class="about">
<h1>This is an about page</h1>
<!-- Show only when no child routes are active -->
<router-link v-if="$route.name === 'about'" to="/child1">Child1</router-link>
<!-- Or, do not show when Child1 route is active -->
<router-link v-if="$route.name !== 'child1'" to="/child1">Child1</router-link>
<router-view></router-view>
</div>
</template>

vue-router: How to use view-router in more than one element?

A very simple example for using a vue-router in template is the following code:
<template>
<div id="app">
<router-view/>
</div>
</template>
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
what I understand is that the content of router-view will be switched by the relevant component to the path.
However, if I have a template with more than one element affected by router. For example,
<template>
<div id="app">
<h1> header </h1>
<router-view 1/>
<h1> Inner </h1>
<router-view 2/>
<h1> Footer </h1>
</div>
</template>
and let's say that router-view 1 and router-view 2 both can get different components based on the path.
In this case, how would you recommend me to use router?
Based on official doc, you have to use named-views.
Like that, you can have multiple router-view rendering differents components for the same path.
With your example, it becomes :
<template>
<div id="app">
<h1> header </h1>
<router-view /> // this will be the default
<h1> Inner </h1>
<router-view name="inner"/>
<h1> Footer </h1>
</div>
</template>
and your router will look like :
// Don't forget your imports
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: HeaderComponent, // Will render in default router-view
inner: InnerComponent // Will render in router-view named "inner"
}
}
]
})
More complex layouts are also describes in the official doc.

Vue Router is duplicating entries in window.history in Safari

My App.vue has the following setup:
// App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
And Home.vue has link to Gmap.vue:
// Home.vue
<template>
<div>
<router-link to="/gmap">vue2-google-maps</router-link>
</div>
</template>
And Gmap.vue has <GmapMap> component from vue2-google-maps:
// Gmap.vue
<template>
<div>
<GmapMap :center="{lat: 0, lng: 0}" :zoom="10">
</GmapMap>
</div>
</template>
Finally router.js is like this:
export default new Router({
mode: "history",
routes: [
{
path: "/",
name: "home",
component: Home
},
{
path: "/gmap",
name: "gmap",
component: Gmap
}
]
})
In Safari, at path /, when click <router-link>, Safari navigates to /gmap and puts /gmap path into Safari's window.history. So clicking Safari's back button will make Safari navigate back to /.
Then clicking <router-link> again will make Safari navigate to /gmap. However, at this time Safari will put /gmap path into Safari's window.history twice (first /gmap and second /gmap). So clicking Safari's back button won't make Safari navigate to /. Instead, Safari will navigate to the first /gmap.
I confirmed that:
when Safari went back and forward between first /gmap and second /gmap, popstate event wasn't triggered, and Vue Router didn't catch the changes so none of Navigation Guards were invoked.
this happens in Safari (macOS 10.13.6 and iOS 11.4.1)
this doesn't happen if routing to components that don't contain <GmapMap> component
this doesn't happen when routing without Vue Router
this doesn't happen in Chrome 67 (macOS 10.13.6 and iOS 11.4.1)
Why does Safari put same path twice?
Though not an exact answer, to solve the issue wrap <keep-alive> around <router-view> in App.vue:
// App.vue
<template>
<div id="app">
<!-- Note that <router-view> is wrapped by <keep-alive> -->
<keep-alive>
<router-view />
</keep-alive>
</div>
</template>
Source: GitHub discussion.