How to understand vue router navigation? - vue.js

I am working with this piece of code:
<script>
import router from '../router'
export default {
name: 'Page2',
data () {
return {
id: 0,
msg: 'Hey Nic Raboy'
}
},
created() {
this.id = this.$route.params.id;
},
methods: {
navigate() {
router.go(-1);
}
}
}
</script>
This is printed on the second page Template B if you will but I am confused on the navigate portion. Specifically this:
navigate() {
router.go(-1);
}
I have not worked with VueJS router before, can anyone please explain what this code section does?

Vue-router package works in the same way with the HTML5 History mode as it stated in the vue-router documentation
This method takes a single integer as parameter that indicates by how
many steps to go forwards or go backwards in the history stack,
similar to window.history.go(n).
So basically this is what you should expect from router.go()
// go back by one record, the same as history.back()
router.go(-1)
You can also learn more about HTML5 History mode from here

Have a look at the router.go(n) documentation in the section on programmatic navigation. The example explains that n can be positive or negative and indicates the number of steps in the browser's history to move.
// go forward by one record, the same as history.forward()
router.go(1)
// go back by one record, the same as history.back()
router.go(-1)
So router.go(-1) is the equivalent of hitting the "back" button in your browser.

Related

vuejs serverPrefetch component data persists?

Stupid question but I haven't found a piece of doc that confirms it.
With vuejs ssr, if a value is set on a component data during serverPrefetch, is it meant to persist on client side?
export default {
data () {
return {
count:0
}
},
serverPrefetch(){
this.count = 1
return Promise.resolve(this.count)
},
mounted(){
// can I rely on this?
if(count===0){
// do stuff
}
}
}
My test shows that count is always zero on page refresh. If someone can please confirm this is standard behaviour and possibly explain why and/or direct me to a documentation about it.
Thank you

How to update multiple dashboard components on click - Vue.js

I've found it very difficult to find help online with this issue as no examples seem to match my use case. I'm basically wanting to check if I am on the right track in my approach.I have a single page Vue app:
Each row on the right is a component. On the left are listed three data sets that each possess values for the fields in the dashboard. I want it to be so that when you click on a dataset, each field updates for that set.
So if you click on 'Beds', the title becomes 'Beds' and all the fields populate the specific data for beds.
I want to do this without having separate pages for each dataset since that would seem to defeat the point of using a reactive framework like Vue. Only the embedded components should change, not the page.
I have installed Vue Router and have explored using slots and dynamic components but it is very hard to understand.
If someone experienced in Vue could just let me know the right broad approach to this I then know what I need to look into, at the moment it is difficult to know where to start. Thank you
You can use Vuex for that purpose.
Add property to the state, dataset for example. And mutation to change it. Every component on the right side should use that this.$store.state.dataset (or through mapState) for its own purposes. So when you're selecting one of listed datasets on the left side, it will mutate dataset in store with its own data.
Something like that:
store (there are alternate version, where we can use getter, but its little bit more complicated for just an example).
import Vue from 'vue';
const store = new Vuex.Store({
state: {
dataset: {}
},
mutations: {
setDataset(state, payload) {
Vue.set(state, 'dataset', payload);
}
}
});
one of the right side component
computed: {
dataset() {
return this.$store.state.dataset;
},
keywords() {
return this.dataset.keywords;
},
volume() {
return this.dataset.volume;
}
}
left menu
template:
{{dataset.title}}
code:
data() {
return {
datasets: [{
id: 1,
title: 'Sofas',
keywords: ['foo'],
volume: 124543
}]
}
},
methods: {
changeDataset(dataset) {
this.$store.commit('setDataset', dataset);
}
}
datasets is your data which you're loading from server.
BUT You can use some global variable for that, without Vuex. Maybe Vue observable, added in 2.6.

Dynamic router-link

I developed some kind of blog with VueJS and VueRouter. So I have a markdown editor in the administration in order to add blog posts.
My problem is: How to make router-links work with dynamic content?
For the time being, I can only add classic foo with the editor. And when the content gets rendered, it's a classic link so when a visitor clicks on the link, the entire website gets reloaded to display the content of the targeted link.
I think that the behaviour I'm looking for is to transform the internal links into router-link and the external links into classic links.
What is your strategy to achieve that in your projects, did someone had ever been confronted to that problem?
Thank you for your advices or ideas.
I explained my problem in a small JSFiddle if you want to see what I talk about: http://jsfiddle.net/El_Matella/museptre/1/
const Home = {
template: '<div>Home <div v-html="dynamicContent"></div></div>',
data () {
return {
dynamicContent: '<router-link to="/foo">This is a dynamic link</router-link> and and this is a classic link'
}
}
}
will only render the classic link
Ok, now I can see what you want to achieve.
Obviously, rendering <router-link></router-link> to static HTML won't work.
You need to generate <rotuer-link> in the template directly. You may use render method to get more flexibility/dynamism or as Vue docs says:
leverage the full programmatic power of JavaScript.
Then you bound to param and content of the link to some dynamic var eg. from data prop.
Example 1. with "simple dynamism" string template:
const Home = {
template: '<div>Home <router-link :to="dynamicTo">{{dynamicContent}}</router-link></div>',
data () {
return {
dynamicContent: 'This is a dynamic link',
dynamicTo: '/foo'
}
}
}
Example 2. with "more complex dynamism" and render method:
render: function(createElement) {
createElement(
'router-link', {
props: {
to: this.dynamicTo
}
}, this.dynamicContent)
}
I didn't check the second example has valid syntax, but you've got an idea how you can use JavaScript to generate a fully customizable template (you can use loops, variables, etc).
It is possible to create a custom component solving the problem and using the render function:
export default {
props: {
content: {
type: String,
required: true
}
},
render (h) {
return h(Vue.compile(this.content))
}
}
and using it this way:
<dynamic-vue-component :content="dynamic"></dynamic-vue-component>
Here is a JSFiddle demonstrating the problem solved: JSFiddle
Maybe kind of a hack, but I managed to work around this by adding an onclick attribute forcing the router to pick the link and preventing the page from reloading :
<a href="/the-link/" target="_self" onclick="event.preventDefault();
app._router.push('/the-link/');">Some text.</a>

VueJS 2 Simplert only covers the component

A brief description is, basically when i click on a "Submit" button per say, an alert should pop out. So I'm using Simplert for Vue to do that.
The project I'm currently doing is a Single Page Application (SPA) so this is where the problem is. The alert only covers the component that I'm in and does not cover the whole page.
Problem:
Is there a way for the alert to cover the whole page?
As I see it you have at least two options to solve it: one dirty and quick and the other a little bit more design oriented
Option 1
override the css position property of the mask:
from: position: absolute to: position: fixed
Option 2
Globally declare the Simplert component, since probably you will use it a lot
// somewhere in your main/app/bootsrap.js file
Vue.compoment('simplert', require('vue2-simplert'))
Now in your root component or where you have your main <router-view> component (if you are using vue-router):
// root component
<template>
<div>
<simplert :useRadius="true" :useIcon="true" ref="simplert"/>
<router-view></router-view>
</div>
</template>
<script>
export default {
mounted () {
this.$on('trigger:simplert', (data) => this.triggerSimplert(data))
},
methods: {
triggerSimplert (data) {
this.$refs.simplert.openSimplert(data)
}
}
}
</script>
Now in any component you need to trigger the modal simply do:
...
someMethod () {
let obj = {
title: 'Custom Function',
message: 'Click close to trigger custom function',
type: 'info',
onClose: this.onClose
}
this.$emit('trigger:simplert', obj)
}
...
Hope this helps.

In Vue.js, how do you prevent navigation for a subroute?

The nice thing about beforeRouteLeave is that you can prevent navigating away under certain conditions.
I have a setup that uses a subroute to render part of the page. I would like a navigation guard on the subroute to prevent switching to another one if the data is not saved.
{
path: '/customers/view',
component: ViewCustomerShell,
children: [
{path: ':id', name: 'ViewCustomer', component: ViewCustomer}
]
},
So when I visit /customers/view/12 and make a change, if they try to load /customers/view/13, I want to pop up the usual confirmation and potentially stop navigation. Since beforeRouteLeave is not called in this situation, what is the recommended approach for preventing navigation? It seems that watching $route would be too late, because then the navigation has already occurred.
Note: As mentioned above, beforeRouteLeave is not called in this situation; it doesn't work.
Note: Using onbeforeunload doesn't work because it only triggers when the entire page changes.
I have also posted the same answer here.
Dynamic route matching is specifically designed to make different paths or URLs map to the same route or component. Therefor, changing the argument does not technically count as leaving (or changing) the route, therefor beforeRouteLeave rightly does not get fired.
However, I suggest that one can make the component corresponding to the route responsible for detecting changes in the argument. Basically, whenever the argument changes, record the change then reverse it (hopefully reversal will be fast enough that it gets unnoticed by the user), then ask for confirmation. If user confirms the change, then use your record to "unreverse" the change, but if the user does not confirm, then keep things as they are (do not reverse the reverse).
I have not tested this personally and therefor I do not gurantee it to work, but hopefully it would have cleared up any confusion as to which part of the app is responsible for checking what change.
I know that this post is very old. but it was the first one I found when looking for the same problem.
I have no idea if there is a better solution nowadays but for those who are looking for a solution, I can share mine:
1. Define a global state
let isRouteChangeBlocked: boolean = false;
export function blockRouteChange(set?: boolean): boolean {
if (arguments.length == 1) {
isRouteChangeBlocked = !!set;
return isRouteChangeBlocked;
}
return isRouteChangeBlocked;
}
2. Replace the route function
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function(location: RawLocation) {
if (blockRouteChange()) {
if (confirm("Du hast ungespeicherte Änderungen, möchtest du fortfahren?")) {
blockRouteChange(false);
return originalPush.call(this, location) as any;
}
return;
}
return originalPush.call(this, location) as any;
};
3. Set the state
#Watch("note.text")
private noteTextChanged() {
blockRouteChange(true);
}
This does exactly what I want. If nowadays there is a better solution, let me know. You can get the full runnable example here: https://github.com/gabbersepp/dev.to-posts/tree/master/blog-posts/vuejs-avoid-routes/code/example
You could use a $route object inside your component to watch if it changes and then raise up the confirmation modal... This will get called whenever your route changes!
const Baz = {
data () {
return { saved: false }
},
template: `
<div>
<p>baz ({{ saved ? 'saved' : 'not saved' }})<p>
<button #click="saved = true">save</button>
</div>
`,
watch: {
'$route': function () {
if (this.saved || window.confirm('Not saved, are you sure you want to navigate away?')) {
// do something ...
}
}
}