How to switch vue.js router views without rerendering html - vue.js

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.

Related

How do I make a button inside a component change the vue router path

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>

Vue router dynamic link and children reload page - not load correctly component

I add to my routes file path with children:
path: '/warehouse/:id',
name: 'ShowWarehouse',
component: ShowWarehouse,
children: [{
path: 'edit',
name: 'EditWarehouse',
component: EditWarehouse
}
]
Now in component ShowWarehouse I have:
<div v-if="!changeEdit">
<div v-if="warehouseData">
<div>Name: {{ warehouseData.warehouse.name }}</div>
<div>
<router-link
:to="{ name: 'EditWarehouse', params: {id: warehouseData.warehouse.id }}"
>Edit</router-link>
</div>
</div>
</div>
<router-view v-else></router-view>
When the user click edit button I need load component EditWarehouse, but component ShowWarehouse must be disappear, and if user back (without /edit) disappear componet EditWarehouse and load component ShowWarehouse. I write method in watch:
watch: {
$route() {
if (this.$route.path == '/warehouse/' + id_get_from_API + '/edit') {
this.changeEdit = true;
} else {
this.changeEdit = false;
}
}
},
The problem is when the user is at mydomain.com/warehouse/23/edit and click reload page (F5), then Vue loads component ShowWarehouse instead of loading EditWarehouse.
I using mode: 'history'.
Problem:
From the Vue.JS website: "Vue does provide a more generic way to observe and react to data changes on a Vue instance: watch properties." When you refresh the page the watch() method will not be executed because it is a new Vue instance and no data has changed on the Vue instance yet. You should probably use a different pattern to determine which component to show. (https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property)
Solution:
I suggest making the EditWarehouse a sibling route to ShowWarehouse, and make EditWarehouse its own component (you already have this). Your router-link in the ShowWarehouse component can stay the same.
Code Snippet:
const ShowWarehouse = {
template: `<div><h1>ShowWarehouse</h1> <div v-if="warehouseData">
<div>Name: {{ warehouseData.warehouse.name }}</div>
<div>ID: {{ $route.params.id }}</div>
<div>
<router-link :to="{ name: 'EditWarehouse'}">Edit</router-link>
</div>
</div></div>`,
computed: {
warehouseData: function() {
let data;
let id = this.$route.params.id;
if (id) {
data = {
warehouse: {
name: 'Some Warehouse Name',
id: id
}
}
}
return data;
}
}
};
const EditWarehouse = {
template: "<h1>EditWarehouse [{{ $route.params.id }}]</h1>"
}
const router = new VueRouter({
routes: [{
path: '/warehouse/:id',
name: 'ShowWarehouse',
component: ShowWarehouse
},
{
path: '/warehouse/:id/edit',
name: 'EditWarehouse',
component: EditWarehouse
}
]
});
new Vue({
el: '#app',
router
});
<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">
<p>
<router-link :to="{ name: 'ShowWarehouse', params: { id: 123 }}">Go to Warehouse 123</router-link>
</p>
<router-view/>
</div>
Here is a jsfiddle with the same code:
https://jsfiddle.net/austinwasinger/oruswb3a/39/

all anchor tag's onclick function is called when click on one tag in vuejs and pug

i have 4 anchor tag with same onclick function, but when i click on one tag, all tag's onclick function is called
ul.menu-list
router-link(tag="li" to="/reports")
a.has-text-white(:onclick='selectTab("reports")') Report
router-link(tag="li" to="/adList")
a.has-text-white(:onclick='selectTab("adSet")') AdSet
router-link(tag="li" to="/adSetList")
a.has-text-white(:onclick='selectTab("adSetList")') AdSetList
router-link(tag="li" to="/sites")
a.has-text-white(:onclick='selectTab("sites")') Sites
and onclick function is:
selectTab(tabName: string) {
console.log(tabName);
}
So I want to know why when i click to Report, all of a tag's onclick function is called?
In this case, all of "report", "adSet", "adSetList", "sites" is logged
Your syntax is not right! It's not the vue events binding syntax.You need use #click="selectTab('reports')"
or v-on:click="selectTab('reports')";
As for the logger ,it is beacause when a vue component is initing, it auto invoke the getter of dependent data to get dependences!
If you put your click event on router-link and change the syntax a little bit that should work properly.You should change :onclick to v-on:click or #click and when using click event on router-link add .native to the event. Here is an example:
const Home = {
template: '<div id="home">Reports</div>'
}
const Component1 = {
template: '<div id="component1">Adset</div>'
};
const Component2 = {
template: '<div id="component2">Adsetlist</div>'
};
const Component3 = {
template: '<div id="component3">sites</div>'
};
const routes = [
{ path: '/reports', component: Home },
{ path: '/adList', component: Component1 },
{ path: '/adSetList', component: Component2 },
{ path: '/sites', component: Component3 },
]
const router = new VueRouter({
routes
});
new Vue({
el: '#app',
router,
data: {
},
methods: {
selectTab(tabName) {
console.log(tabName);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.2/vue-router.min.js"></script>
<body class="text-center body">
<div id="app">
<ul class="menu-list">
<router-link tag="li" to="/reports" v-on:click.native="selectTab('reports')">
<a class="has-text-white">Report</a>
</router-link>
<router-link tag="li" to="/adList" v-on:click.native="selectTab('adSet')">
<a class="has-text-white">AdSet</a>
</router-link>
<router-link tag="li" to="/adSetList" v-on:click.native="selectTab('adSetList')">
<a class="has-text-white">AdSetList</a>
</router-link>
<router-link tag="li" to="/sites" v-on:click.native="selectTab('sites')">
<a class="has-text-white">Sites</a>
</router-link>
</ul>
<router-view></router-view>
</div>
</body>

Vue js single page and siderbar with header issue

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>

Add event listener to <router-link> component using "v-on:" directive - VueJS

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>