Always render default component whenever the router links is clicked - vue.js

I am using Vue and Vue-router.
routes
{
path: '/admin', component: AdminPage,
children: [
{path: 'admin-user', component: AdminUser, name: 'admin-user'}
]
},
In the AdminPage component I have router links and its router view to navigate between pages. One of the route is of path admin-user, which uses the AdminUser component.
AdminPage.vue:
<template>
<div id="adminPage">
<ul id="adminNavList">
<li>... some nav links ...</li>
<li><router-link :to="{ name: 'admin-user', params: {front: true}}">User</router-link></li>
</ul>
<div class="adminViewPort" v-if="isAdmin">
<router-view></router-view>
</div>
</div>
</template>
In the AdminUser component there are two components which are displayed based on the conditions. When the route path is first loaded and the AdminUser is created, the AdminUserList component is displayed by default. And when clicks the button which fires show_user_bookings() button, the other component AdminUserBookings is displayed. Similarly when the user clicks the button which fires show_user_list() method, the AdminUserList component is displayed.
AdminUser.vue
<template>
<div id="AdminUserWrapper">
<admin-user-list v-if="component_name==='user_list'" :showUserBookings="show_user_bookings"></admin-user-list>
<admin-user-bookings v-if="component_name==='user_bookings'" :show_user_list="show_user_list"></admin-user-bookings>
</div>
</template>
<script>
import AdminUserList from './AdminUserList.vue'
import AdminUserBookings from './AdminUserBookings.vue';
export default {
name: 'AdminUserWrapper',
components: {
AdminUserList, AdminUserBookings
},
data() {
return {
component_name: '',
},
methods: {
show_user_list() {
this.component_name = 'user_list';
},
show_user_bookings() {
this.component_name = 'user_bookings';
}
},
created() {
this.show_user_list();
}
}
</script>
So the problem is, if AdminUserBookings component is displayed and clicks the link for the path admin-user, the default component AdminUserList is not displayed. Is there any way to display the default component AdminUserList whenever the admin-user path is requested from the AdminPage even if its already in the admin-user path when the AdminUserBookings component is displayed?

Related

vue2, view-router screen transition issue

I can't speak English, but vue is worse. I apologize.
I am working on page conversion after login using view router.
I am develop the screen below.
Click here.
The current screen is not exposed normally.
I think you are misunderstanding router-view.
I will show you my code. What could be wrong?
The calling sequence is as follows: index.html -> App.vue -> Login.vue (real first page) -> Main.vue
Route.js
import Menus from "../components/LeftSideMenu.vue";
import Login from "../views/Login.vue";
import Main from "../views/Main.vue";
Vue.use(VueRouter);
const route = [
{
path: "/",
component: Login
},
{
path : "/main",
name : "main",
components: {
default: Main,
MENU: Menus
},
}
]
App.vue
<template>
<div>
<router-view/>
</div>
</template>
Login.vue
<template>
<div>
<input type="submit" class="fadeIn fourth" value="Log In" #click="login">
</div>
</template>
<script>
methods: {
login() { this.$router.push("/main")
}
</script>
Main.vue
<template>
<div>
<MENU/>
<router-view/>
</div>
</template>
I have figured out what you are lagging here use the following correct in your code. Hope it will work for you.
Routes.js
import Menus from "../components/LeftSideMenu.vue";
import Login from "../views/Login.vue";
import Main from "../views/Main.vue";
Vue.use(VueRouter);
const route = [
{
path: "/",
component: Login
},
{
path : "main",
name : "main",
component: Main
}
]

Prop is not being passed to router component after page refresh in Vuejs

I have encountered a problem with Vue router recently, imagine that we have a Vue CLI project and our App component is like below:
<template>
<div id="app">
<div class="links">
<router-link to="one">one</router-link>
<router-link to="two">two</router-link>
</div>
<div class="routers">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
data: function(){
return{
}
},
created(){
this.$router.push({
name: 'two',
params:{
message: 'hello'
}
});
}
}
</script>
Our one and two components are:
<template>
<div>
two, the message is {{ message }}
</div>
</template>
<script>
export default {
props:[
"message"
]
}
</script>
and
<template>
<div>
one
</div>
</template>
and our router file is:
import Vue from 'vue'
import VueRouter from 'vue-router'
import one from '../components/one.vue'
import two from '../components/two.vue'
Vue.use(VueRouter);
export const router = new VueRouter({
routes:[
{
path: '/one',
name: 'one',
component: one
},
{
path: '/two',
name: 'two',
component: two,
props: true
}
]
});
The problem is, when I open the page for the first time, everything is fine and the second component recognizes the prop and shows "two, the message is hello". the router links all work fine when I click on them and the prop is passed properly.
The problem appears when I refresh the page, and it only shows "two, the message is".
What I have done to solve this: It seems that this.$router.push is not working correctly after the second page refresh, and the reason is the duplicated navigation error which doesn't let you navigate to the same route.
The questions are:
Did I recognize the problem correctly? Is it because of the duplicated navigation?
If that's the problem, how can I make a router component to always mount on the page refresh, with the prop passed to it properly?
Route params that are not included in the path (eg /route/:param) do not persist on page reload. They live only in-memory for the current session.
What I would do instead is
Remove the created hook in your App component
Set up a redirect from / to two in your router
{
path: "/",
redirect: { name: "two", params: { message: "hello" } }
}
Set a default value for the prop in two to handle reloads
props: {
message: {
type: String,
default: "hello"
}
}

Seperate Dashbord and Forum Component From App component in vuejs

Is there a way to mount multiple components on a single vue instance.
I have my admin dashboard and a forum page and i don't want header and navigation to show up on these pages.
Here's what I've tried:
import App from "./App.vue";
import Admin from "./Admin.vue";
import Forum from "./Forum.vue";
const app = new Vue({
router,
store,
components: {
App, Admin, Forum
}
}).$mount("#app");
Then in my App.vue, I have other child components
<template>
<div>
<div class="general-page">
<AppHeader></AppHeader>
<transition name="fade">
<router-view></router-view>
</transition>
<AppFooter></AppFooter>
</div>
</div>
</template>
<script>
import AppHeader from "./components/AppHeader";
import Login from "./components/Login.vue";
import Register from "./components/Register.vue";
import AppFooter from "./components/AppFooter.vue";
export default {
components: {
AppHeader,
Login,
Register,
AppFooter
}
};
</script>
In Forum.vue
<template>
<div>
<div class="forum-page">
<ForumHeader></ForumHeader>
<transition name="fade">
<router-view></router-view>
</transition>
<ForumFooter></ForumFooter>
</div>
</div>
</template>
<script>
import ForumHeader from "./components/ForumHeader";
import ForumFooter from "./components/ForumFooter.vue";
export default {
components: {
ForumHeader,
ForumFooter
}
};
</script>
Admin.vue
<template>
<div>
<div class="admin-page">
<AdminHeader></AdminHeader>
<transition name="fade">
<router-view></router-view>
</transition>
<AdminFooter></AdminFooter>
</div>
</div>
</template>
<script>
import AdminHeader from "./components/AdminHeader";
import AdminFooter from "./components/AdminFooter.vue";
export default {
components: {
AdminHeader,
AdminFooter
}
};
</script>
Routes for Forum and Admin
{
path: '/admin',
name: 'Admin',
component: Admin,
meta: {
requiresAuth: true
},
children: [
{
path: '',
name: 'Profile',
component: Profile
},
{
path: 'uploads',
name: 'Uploads',
component: Uploads,
meta: {
requiresCreatorAccess: true
}
},
{
path: 'add-post',
name: 'AddPost',
component: AddPost,
meta: {
requiresCreatorAccess: true
}
}
]
},
{
path: '/forum',
name: 'Forum',
component: Forum,
children: [
{
path: '',
name: 'Channel',
component: Channel
},
{
path: 'threads',
name: 'Threads',
component: Threads
},
{
path: 'topic',
name: 'Topic',
component: Topic
}
]
},
How do I dynamically go to each route and mount each component on el: #app ?
Without changing any routing and template structure, you could use CSS to hide the app header, footer.
Another option may be to v-if the app header,footer to not render when on those routes using something like $router.currentRoute for matching.
CSS
/*
Assuming app header and footer have an id attribute
Change to your needs
*/
#app-header,
#app-footer {
display: none;
}
v-if on currentRoute
We have to do a few of things here.
Create a data variable (showMe: true)
Create a method (evaluateShowMe)
Create a watcher for the route ('$route'()) Be aware of the quotes!
Note: Feel free to rename the variable and function to suit your needs.
We need to watch $route because this is outside of a <router-view/> so we need to do this dynamically so the variable performs the evaluator function every time the route changes.
App.vue:
<template>
<div>
<div class="general-page">
<AppHeader
v-if="showMe"
></AppHeader>
<transition name="fade">
<router-view></router-view>
</transition>
<AppFooter
v-if="showMe"
></AppFooter>
</div>
</div>
</template>
<script>
import AppHeader from "./components/AppHeader";
import Login from "./components/Login.vue";
import Register from "./components/Register.vue";
import AppFooter from "./components/AppFooter.vue";
export default {
components: {
AppHeader,
Login,
Register,
AppFooter
},
data() {
return {
showMe: true
}
},
methods: {
evaluateShowMe() {
// Get the substring of the path between first and second slash
// This will allow to include any child pathing
// NOTE: In my test the first index ([0]) was empty so used one ([1]) for the `filter`
const entryPath = this.$router.currentRoute.path.split('/').filter((x,i) => i === 1);
// We want to exclude the following paths i.e. hide when on these
// There should only be one item in the array so we extract with `[0]`
return (entryPath[0] !== 'admin' || entryPath[0] !== 'forum');
}
},
watch: {
'$route'() {
this.showMe = this.evaluateShowMe();
}
}
};
</script>

Why are my Vue instance's properties and methods not available in the template?

I set up a Vue.js CLI project.
On a page, I want to define a variable in data() and then use it in my template.
Why does the following code tell me:
Property or method "message" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
<template>
<div>
<h1>Page 2</h1>
<p> You can go back to
<router-link to="/">home</router-link>.</p>
<p>[{{message}}]</p>
</div>
</template>
export default {
name: "Page2",
data() {
return {
message: "here it is"
}
}
}
And this code:
<button class="btn" #click="test()">the test</button>
</p>
</div>
</template>
export default {
name: "Page2",
data() {
return {
message: "here it is"
}
},
methods: {
test() {
console.log('test');
}
}
}
Tells me similarly:
Property or method "test" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
What else do I need to do to make these variables and methods available in my template on the page?
This structure of code has worked in other Vue.js CLI projects so it much be something not set right in the project in the environment somewhere.
For instance, this code works in another project:
<template>
<div class="start alert alert-success" role="alert">
{{ msg }}
</div>
</template>
<script>
export default {
name: 'start',
data() {
return {
msg: 'Please choose an option.'
}
}
}
</script>
And it works on the Home page but not on Page1 or Page2. My index.js page looks like this:
import Vue from "vue";
import Router from "vue-router";
import Home from "#/components/Home";
import Page1 from "#/components/Page1";
import Page2 from "#/components/Page2";
Vue.use(Router);
export default new Router({
routes: [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/page1",
name: "Page1",
component: Page1
},
{
path: "/page2",
name: "Page2",
component: Page2
}
]
});
You need to add <script> tags around the javascript for your Page1 and Page2 components.
Otherwise, it seems like vue-loader just ignores that script and doesn't give you a relevant warning (just that the data being referenced in your template is missing).

How to create a custom link component in Vue.js?

This seems like your run-of-the-mill master/detail use case, but the examples in the Vue docs stop short of examples for this. I have a Mail Folder page (route /:mailbox_id) that displays a table of emails by Date, Subject, etc., and I want a nested route (/:message_id) that shows the email's text when the user clicks on a row.
I was able to do this in Ember (recreating this) because Ember makes a JavaScript onClick function to handle the routing and lets you set the HTML element to render, and then you just pass whatever objects to the child route.
But I'm new to Vue.js, and I've been going through the docs but can't grok how to accomplish the same thing. I can't figure out how to either create a custom link component, or how to use the built-in Vue <router-link>component (because I need it to be a <tr> instead of <a>) to both go to the child route, and pass along the contents of the message to it so it can be shown.
If it helps, here's some code:
The Router
export default new Router({
routes: [
{
path: '/:id',
name: 'mailbox',
component: Mailbox,
props: true,
children: [
{
path: 'mail/:id',
name: 'mail',
component: Mail,
props: true
}
]
}
]
})
Component: Mailbox.vue
<template>
<div>
<table>
<tr>
<th>Date</th>
<th>Subject</th>
<th>From</th>
<th>To</th>
</tr>
<Mail-List-Item v-for="message in messages" :key="message.id" v-bind:message="message"/>
</table>
<router-view></router-view>
</div>
</template>
<script>
import MailListItem from './Mail-List-Item'
export default {
components: { 'Mail-List-Item': MailListItem },
name: 'Mailbox',
props: ['messages']
}
</script>
Component: Mail.vue
<template>
<div class="mail">
<dl>
<dt>From</dt>
<dd>{{mail.from}}</dd>
<dt>To</dt>
<dd>{{mail.to}}</dd>
<dt>Date</dt>
<dd>{{messageDate}}</dd>
</dl>
<h4>{{mail.subject}}</h4>
<p>{{mail.body}}</p>
</div>
</template>
<script>
export default {
props: ['message', 'messageDate']
}
</script>
Component: Mail-List-Item.vue
<template>
<V-Row-Link href="mail" mailid="message.id" message="message">
<td>{{messageDate}}</td>
<td>{{message.subject}}</td>
<td>{{message.from}}</td>
<td>{{message.to}}</td>
</V-Row-Link>
</template>
<script>
var moment = require('moment')
import VRowLink from './V-Row-Link'
export default {
name: 'Mail-List-Item',
props: ['message'],
components: { VRowLink },
data: function () {
return {messageDate: moment(this.message.date).format('MMM Do')}
}
}
</script>
Component: V-Row-Link.vue (much of this copied from this repo)
<template lang="html">
<tr
v-bind:href="href"
v-on:click="go"
>
<slot></slot>
</tr>
</template>
<script>
import routes from '../Router'
export default {
props: ['href', 'mailid', 'message'],
methods: {
go (event) {
this.$root.currentRoute = this.href
window.history.pushState(
null,
routes[this.href],
this.href
)
}
}
}
</script>
Router link takes a tag attribute, that you can use to turn it into any element you like. An example would be...
<router-link tag="tr" :to="'/messages/' + MAIL_ID">{{ MAIL_TITLE }}</router-link>