I am creating vue js application. I have login screen and after login, left sidebar for options like dashboard, users, settings.. and header for signout and notification feature.
My architecture is : I have 1 common file(main layout) in which header and sidebar are added. Now on 1st time open after login, dashboard is called in which main layout is imported.
I want to call this sidebar and header only once.. but the problem is whenever I click on sidebar, it opens respective screen in right side in container but sidebar and header also calls as I have imported main file to each component.
Due to this my firebase listener attached in header calls multiple times. I want to load header only once after login so that I can use firebase listener correctly.
My architecture is below:
main layout file:
<template>
<div id="appOne">
<div class="col-sm-3 col-lg-2 hamburger" style="padding-left: 0;">
<Sidebar></Sidebar>
</div>
<div class="col-sm-9 col-lg-10 fixed">
<Header class="header"></Header>
<div class="dynTemplate" id="dynTemplate"></div>
</div>
</div>
</template>
Dashboard vue file:
<template>
<div>
<Mainlayout></Mainlayout>
<div>
<span><h1 align="center"> Welcome </h1> </span>
</div>
</div>
</template>
<script>
import Mainlayout from './shared/Mainlayout.vue';
export default {
el: '#appOne',
components: {
Mainlayout,
}
}
</script>
What is correct way to use header, sidebar and other component which will call on click on sidebar options.
Try this snippet. The mounted() and created() in header will be called only once.
Or if you need more dynamic view components, try named view
const Login = {
template: `
<div>
<div>Login Page</div>
<router-link to="/foo">click here</router-link>
</div>
`
}
const Sider = {
template: '<div>This is sider</div>'
}
const Header = {
template: '<div>This is header</div>',
mounted() {
console.log("header mounted")
},
created() {
console.log("header created")
},
}
const MainLayout = {
template: `
<div>
<mysider></mysider>
<div>
<myheader></myheader>
<router-view></router-view>
</div>
</div>
`,
components: {
mysider: Sider,
myheader: Header
}
}
const Foo = {
template: `
<div>
<div>This is Foo</div>
<router-link to="/bar">go to Bar</router-link>
</div>`
}
const Bar = {
template: `
<div>
<div>This is Bar</div>
<router-link to="/foo">go to Foo</router-link>
</div>`
}
const router = new VueRouter({
routes: [{
path: '/',
component: Login
},
{
path: '/main',
component: MainLayout,
children: [
{
path: '/foo',
component: Foo
},
{
path: '/bar',
component: Bar
},
]
}
]
})
const app = new Vue({
router
}).$mount('#app')
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-view></router-view>
</div>
Related
I have this default vue navbar which I want to make it more dynamic by using v-for.
Before changes, this is the code:
<template>
<div>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
</template>
After changes, this is the code:
<template>
<div>
<div v-for="navbar in navbars" :key="navbar.id">
<router-link :to="navbar.router">{{ navbar.names }}</router-link>
</div>
</div>
</template>
<script>
export default {
name: "Header",
navbars: [
{ names: "Home", router: "/" },
{ names: "About", router: "/About" }
]
};
</script>
But after converted it, the navbar doesn't show up. Anything I miss out?
You need to define navbars as a data of the component using data
<template>
<div>
<div v-for="navbar in navbars" :key="navbar.id">
<router-link :to="navbar.router">{{ navbar.names }}</router-link>
</div>
</div>
</template>
<script>
export default {
name: "Header",
data: function () {
return {
navbars: [
{ names: "Home", router: "/" },
{ names: "About", router: "/About" }
]
}
}
};
</script>
The connection in between components gets disrupted. Which shouldnt happen since I am using bootstrap-vue inbuilt router link (using to=" " instead of href="").
The app works perfectly fine when running without dist.
App.vue
<template>
<div class="container">
<app-header></app-header>
<div class="row">
<div class="col-xs-12">
<transition name="slide" mode="out-in">
<router-view></router-view>
</transition>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
export default {
components: {
appHeader: Header
},
created() {
this.$store.dispatch("initStocks");
}
};
</script>
Header.vue
<template>
<div>
<b-navbar toggleable="lg" type="dark" variant="info">
<b-navbar-brand to="/">Stock Broker</b-navbar-brand>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav>
<b-nav-item to="/portfolio">Portfolio</b-nav-item>
<b-nav-item to="/stocks">Stocks</b-nav-item>
<b-nav-item #click="endDay">End Day</b-nav-item>
<b-navbar-nav right>
<b-nav-item right>Funds: {{ funds }}</b-nav-item>
</b-navbar-nav>
</b-navbar-nav>
</b-collapse>
</b-navbar>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
data() {
return {
isDropdownOpen: false
};
},
computed: {
funds() {
return this.$store.getters.funds;
}
},
methods: {
...mapActions({
randomizeStocks: "randomizeStocks",
fetchData: "loadData"
}),
endDay() {
this.randomizeStocks();
},
saveData() {
const data = {
funds: this.$store.getters.funds,
stockPortfolio: this.$store.getters.stockPortfolio,
stocks: this.$store.getters.stocks
};
this.$http.put("data.json", data);
},
loadData() {
this.fetchData();
}
}
};
</script>
vue.config.js
module.exports = {
pluginOptions: {
prerenderSpa: {
registry: undefined,
renderRoutes: ["/", "/portfolio", "/stocks"],
useRenderEvent: true,
headless: true,
onlyProduction: true
}
}
};
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/stocks',
name: 'Stocks',
component: () => import(/ '../views/Stocks.vue')
},
{
path: '/portfolio',
name: 'Portfolio',
component: () => import('../views/Portfolio.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
I figured it a minute after making this post haha.
The issue was that in App.vue I had my main div with a class of "container" instead of id of "app".
Corrected below:
<template>
<div id="app"> //correction here
<app-header></app-header>
<div class="row">
<div class="col-xs-12">
<transition name="slide" mode="out-in">
<router-view></router-view>
</transition>
</div>
</div>
</div>
</template>
I am using vue and the official router. My app has some views and one view has a form with some inputs. Now I am looking for a solution to switch between to views without loosing the input data / rerendering the view. An extra save button in the form isnt an option.
Probably my problem is close to different way how v-if and v-show works:
https://v2.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show
I build an example based on the vue router example:
https://jsfiddle.net/okuc64d2/6/
const Foo = {
data: function () {
return {
text: "abc"
}
},
template: '<div>VIEW A: <input v-model="text" placeholder="edit me"></div>'
}
const Bar = {
data: function () {
return {
text: "123"
}
},
template: '<div>VIEW B: <input v-model="text" placeholder="edit me"></div>'
}
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
// Now the app has started!
.router-link-active {
color: red;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<ul>
<li>Go to Foo</li>
<li>Change input</li>
<li>Go to Bar</li>
<li>Go back to Foo</li>
<li>Input is resetted</li>
</ul>
<p>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>
You can save input on keyup to vuex store and use that to your component.
I have buttons inside of a component. I am using vue router for vuejs 2.5 and I want to the path to simply change if I click this button. How do I make the path update whenever I click the button.
const Home = { template: '<div>Home <br> <button id="t2">Click Me</button><br><button>go back</button></div>' }
const Foo = { template: '<div>Foo<br this is a page</div>' }
const hello = {
template:'t1',
data(){
return {
}
}
}
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/foo', component: Foo }
]
})
new Vue({
router,
el: '#app',
data: {
msg: 'Hello World'
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link to="/foo">/foo</router-link>
<router-view></router-view>
<script type="text/x-template" id="t1">
<button>hello</button><br>
</script>
</div>
You can simple add a click listener to the button:
<template>
<div id="app">
<button #click="redirectToHome()">Go Back</button>
<button #click="redirectToFoo()">Click ME</button>
</div>
</template>
and implement them inside methods
<script>
export default {
name: "#app",
methods: {
redirectToHome() {
this.$router.push({ path: '/' });
},
redirectToFoo() {
this.$router.push({ path: '/foo' });
}
}
};
</script>
For further details on programmatic navigation:
https://router.vuejs.org/guide/essentials/navigation.html#programmatic-navigation
You can use the "to" prop of vue-route https://router.vuejs.org/api/#to
And on tag prop you must set button value for to render button
https://router.vuejs.org/api/#tag
<router-link
to="/foo"
tag="button">
foo
</router-link>
I'm attempting to add a custom handler InlineButtonClickHandler to a <router-link> component's click event, so that I can emit a custom appSidebarInlineButtonClick event.
But, my code isn't working. What am I doing wrong?
<template>
<router-link :to="to" #click="InlineButtonClickHandler">
{{ name }}
</router-link>
</template>
<script type="text/babel">
export default {
props: {
to: { type: Object, required: true },
name: { type: String, required: true }
},
methods: {
InlineButtonClickHandler(event) {
this.$emit('appSidebarInlineButtonClick');
}
}
}
</script>
You need to add the .native modifier:
<router-link
:to="to"
#click.native="InlineButtonClickHandler"
>
{{name}}
</router-link>
This will listen to the native click event of the root element of the router-link component.
<router-link:to="to">
<span #click="InlineButtonClickHandler">{{name}}</span>
</router-link>
Maybe you can try this.
With vue 3 and vue router 4 the #event and tag prop are removed according to this and instead of that you could use v-slot:
const Home = {
template: '<div>Home</div>'
}
const About = {
template: '<div>About</div>'
}
let routes = [{
path: '/',
component: Home
}, {
path: '/about',
component: About
}, ]
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes,
})
const app = Vue.createApp({
methods: {
test() {
console.log("test")
}
}
})
app.use(router)
app.mount('#app')
<script src="https://unpkg.com/vue#3"></script>
<script src="https://unpkg.com/vue-router#4"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<router-link to="/" v-slot="{navigate}">
<span #click="test" role="link">Go to Home</span>
</router-link>
<br/>
<router-link to="/about">Go to About</router-link>
</p>
<router-view></router-view>
</div>