Prop is not being passed to router component after page refresh in Vuejs - vue.js

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"
}
}

Related

Vue props undefined on component

I am struggling with passing props to my child component and reading through many many examples, there are quiet a few in my position. Seems this shouldn't be so complicated, right?
Ideally, when I drop my component on a html page I want to be able to pass a url as an attribute. Example
<landingpage myUrl="http://localhost"><landingpage> but when I inspect with the Vue Dev Tools in browser, it is always undefined. I've seen a hack using JQuery to select the element and then get the attribute but I would like to do it in pure Vue.
In my code below, no variation of "title" is passed to my component.
In my index.html page I have this
<body>
<p>Hello world, this is some text. Howdy.</p>
<div id="NewWidget">
<div id="app" data-title="mario" :data-title="luigi" :title="princess">
<landingpage title="hello!" :title="spaghetti" v-bind:title="Nervos"></landingpage>
</div>
</div>
<!-- built files will be auto injected -->
</body>
In my App.vue I have
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
And in my landingpage.vue I have this
export default {
name: 'landingpage',
data () {
return {
categories: [],
}
},
props: {
title: {
type: String
}
},
...
My router index.js
export default new Router({
routes: [
{
path: '/',
name: 'LandingPage',
component: LandingPage,
props: true
},
...
In my LandingPage component, this.title is always null/undefined.
I am using Vue 2.5.2 / Vue Router 3.0.1
Only thing I can think of is my VueRouter usage in App.vue is burning the props?

Why is the activated lifecycle hook not called on first visit

I have a problem where a component within a router-view that is being kept alive does not call its activated lifecycle hook when first created. The created and mounted lifecycle hooks are being called. On a second visit, the activated hook is being called.
The scenario is quite complicated as there is a bit of nesting and slot using involved.
I've tried to create a minimal example which you can find below, or a bit more detailed on https://codesandbox.io/s/251k1pq9n.
Unfortunately, it is quite large and still not as complicated as the real code which I unfortunately cannot share.
Worse, I failed to reproduce the actual problem in my minimal example. Here, the created, mounted, and activated lifecycle hooks are all called when first visiting SlotExample.
In my real code, only the created and mounted, lifecycle hooks are called on the first visit, the activated hook is called on subsequent visits. Interestingly, all lifecycle hooks are called as expected for SlotParent.
The real code involves more nesting and makes use of slots to use layout components.
My code is using Vue 2.5.16 and Vue-Router 3.0.1 but it also doesn't work as expected in Due 2.6.7 and Vue-Router 3.0.2. I am also using Vuetify and Vue-Head but don't think think this has anything to do with my problem.
index.js.
Does anyone have an idea what I could have been doing wrong. I actually suspect a bug in vue-router
when using multiple nested slots and keep-alive but cannot reproduce.
index.js
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import Start from "./Start.vue";
import SlotExample from "./SlotExample.vue";
const routes = [
{
path: "/start",
component: Start
},
{
path: "/slotExample/:id",
component: SlotExample,
props: true
}
];
const router = new VueRouter({ routes });
Vue.use(VueRouter);
new Vue({
render: h => h(App),
router,
components: { App }
}).$mount("#app");
App.vue
<template>
<div id="app">
<div>
<keep-alive><router-view/></keep-alive>
</div>
</div>
</template>
SlotExample.vue
<template>
<div>
<h1>Slot Example</h1>
<router-link to="/start"><a>start</a></router-link>
<router-link to="/slotExample/123">
<a>slotExample 123</a>
</router-link>
<slot-parent :id="id">
<slot-child
slot-scope="user"
:firstName="user.firstName"
:lastName="user.lastName"/>
</slot-parent>
</div>
</template>
<script>
import SlotParent from "./SlotParent.vue";
import SlotChild from "./SlotChild.vue";
export default {
name: "slotExample",
components: { SlotParent, SlotChild },
props: {
id: {
type: String,
required: true
}
}
};
</script>
SlotParent.vue
<template>
<div>
<div slot="header"><h1>SlotParent</h1></div>
<div slot="content-area">
<slot :firstName="firstName" :lastName="lastName" />
</div>
</div>
</template>
<script>
export default {
name: "slotParent",
props: {
id: {
type: String,
required: true
}
},
computed: {
firstName() {
if (this.id === "123") {
return "John";
} else {
return "Jane";
}
},
lastName() {
return "Doe";
}
}
};
</script>
SlotChild.vue
<template>
<div>
<h2>SlotChild</h2>
<p>{{ firstName }} {{ lastName }}</p>
</div>
</template>
<script>
export default {
name: "slotChild",
props: {
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
},
created() {
console.log("slotChild created");
},
mounted() {
console.log("slotChild mounted");
},
activated() {
console.log("slotChild activated");
}
};
</script>
I think you need to put SlotChild within keep-alive block.
Take a look at vue js doc about activated hook

vue-router not rendering new components on route change

I have a really simple minimal example page with vue-router which is only partially working.
app.js
import 'babel-polyfill';
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './app.vue';
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
app.vue
<template>
<div class="container">
<div id="nav">
<router-link to="/">Page 1</router-link>|
<router-link to="/page2">Page 2</router-link>
<router-link to="/sretbs">No page</router-link>
</div>
<div style="border:5px solid green">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {};
</script>
router.js
import Vue from 'vue'
import Router from 'vue-router'
import Page1 from './page1.vue';
import Page2 from './page2.vue';
import ErrorPage from './error.vue';
Vue.use(Router)
export default new Router({
routes: [
{ path: '/page1', name: 'page1', component: Page1 },
{ path: '/page2', name: 'page2', component: Page2 },
{ path: '*', component: ErrorPage }
],
redirect: {
"/": "page1"
}
})
The <router-view> appear to work on first page load. It shows the component I expect for whatever route is currently in the url (e.g. localhost/test#/page2), or if I explicitly route.push before binding Vue to #app it also shows the expected component.
Navigating to other routes doesn't appear to fully work, or at least it's not rendering the new route. When I add debug output to the page components like so:
<template>
<div class="container">
<h2>this is page 2</h2>
<buttons></buttons>
</div>
</template>
<script>
export default {
beforeRouteEnter: function(to, from, next) {
console.log(`Entering page 2:`);
next();
},
beforeRouteLeave: function(to, from, next) {
console.log(`Leaving page 2`);
next();
},
beforeRouteUpdate: function() {
console.log(`Before route update`);
}
};
</script>
it is picking up the routeenter/leave events. I can seemingly navigate back and forth between routes, but you don't see the result until a page reload
I can't recreate the problem in a jsfiddle, and I've scrapped everything and started again but am getting the same result. I can't see anything I'm doing wrong from looking at the Vue Router documentation. Any idea what I'm missing here?
In this instance the answer was painfully simple. There was another Vue instance on the page elsewhere. Even though my understanding is separate Vue instances bound to different container elements should be able to exist in harmony, in this instance it wasn't. It was also what was breaking the Vue dev tools, showing a Vuex instance for example but not showing any of the loaded components and giving a Cannot read property '__VUE_DEVTOOLS_UID__' of undefined error in the Vue Dev Tools errors.

Passing ID through router-link in Vue.js

I have 2 router links that link to the same page (definition page) but has different ids, in my definition page I have an if else loop that checks the id and then posts the apropriate definition for that id.my problem is that my loop can't properly read my id and goes straight to my else statment, this is the closest that I've gotten it to work.
My 2 router-links in page 1
<router-link :to="{ path: '/Pulse/Definition',id:'Alignment'}" v-bind:tooltip="Alignment" append><a >Read more ></a></router-link>
<router-link :to="{ path: '/Pulse/Definition'}" id="Trust" append><a >Read more ></a></router-link>
My definition page
<template>
<div class="PulseDefinition page row">
<h2 v-if=" id=='Alignment'">hello world {{id}}</h2>
<h2 v-else-if=" id=='Trust'">hello world {{id}}</h2>
<h2 v-else>Sorry try again</h2>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.PulseDefinition{
margin-top:2.5rem;
margin-left:3rem;
background-color: aquamarine;
width:50rem;
height:50rem;
}
</style>
Router
import Vue from 'vue';
import Router from 'vue-router';
import Community from '../components/PulseCommunity';
import Home from '../components/Home';
import Definition from '../components/Definition.vue';
Vue.use(Router)
export default new Router({
routes:[
{
path:'Tuba',
name:'Tuba',
component: Default
},
{
path:'/Pulse',
name:'Pulse',
component:PulseNav,
children:[{
path:'/Pulse/Overview',
name:'Overview',
component:Overview
},
{
path:'/Pulse/Personal',
name:'Personal',
component:Personal
},
{
path:'/Pulse/Community',
name:'Community',
component:Community
},
{
path:'/Pulse/Definition/:id',
name:'Pulse Definition',
component:Definition
}
]
},
{
path:'/Coaching',
name:'Coaching',
component:Coaching
},
{
path:'/Comunication',
name:'Comunication',
component:Comunication
},
{
path:'/Home',
name:'Home',
component:Home
},
]
})
Normally when your using the router inside of a Vue application you'll want to use route parameters, check out the dynamic routing link here.
Using the same example:
const router = new VueRouter({
routes: [
// dynamic segments start with a colon
{ path: '/user/:id', component: User }
]
})
Here in our router whenever we navigate to a url where /user/ is present providing we then add something after we can match the /:id section of it. Then inside of our component we are able to query the parameters for the ID that was sent in our url:
console.log(this.$route.query.id)
Using this we could then save that value into our component or we could build reactivity around this.$route.query.
In your case you'd only need to append to the string that you pass into that router link by simply using your data / methods or if you require further rules you could use a computed method. This might become or something simmilar:
<router-link :to="{ path: '/Pulse/Definition'+ this.alignmentType}" v-bind:tooltip="Alignment" append><a >Read more ></a></router-link>
i found a solution thx to the help of li x and a senior coworker of mine,here is the awnser.
my working router-link in page 1
<router-link :to="{ path: '/Pulse/Definition/'+'Alignment'}" v-bind:tooltip="Alignment" append><a >Read more ></a></router-link>
im adding the id(Alignment) to my url with[:to="{ path: '/Pulse/Definition/'+'Alignment'}"]
my definition page
<template>
<div class="PulseDefinition page row">
<h2 v-if=" this.$route.params.id=='Alignment'">hello world {{this.$route.params.id}}</h2>
<h2 v-else-if=" this.$route.params.id=='Trust'">hello world {{this.$route.params.id}}</h2>
<h2 v-else-if=" this.$route.params.id=='undefined'">Sorry try again {{this.$route.params.id}}</h2>
<h2 v-else>XXXSorry try againXXX{{this.$route.params.id}}</h2>
<!-- {{console.log("hi")}} -->
</div>
</template>
<script>
// console.log(this.$route.query.id);
export default {
}
</script>
im using [this.$route.params.id] to retrieve my id, and my router page stayed the same.
thank you all for the great help ;)

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).