I want to know if it's possible to send a URL with a query to a component page?
For example, this is the URL /physicians/?apptId=123&npi=123456789 and I want it to go to BookAnAppointment.vue. I know it works if I have a route defined like this /physicians/:npi?apptId=123 but that's not what I want.
On the PhysicianLanding page if I click the "Book" button it will add the params to the URL but I can't figure out how to send it to the BookAnAppointment component.
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import PhysicianLanding from '#/components/PhysicianLanding'
import PhysicianProfile from '#/components/PhysicianProfile'
import BookAnAppointment from '#/components/BookAnAppointment'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/physicians',
component: PhysicianLanding
},
{
path: '/physicians/profile/:url',
component: PhysicianProfile
},
{
path: '/physicians/:npi',
component: BookAnAppointment,
props: true
}
]
})
src/components/PhysicianLanding.vue
<template>
<div class="container">
<h1>{{ msg }}</h1>
<!-- I know this works -->
<button type="button" #click="$router.push({ path: '/physicians/' + physicianNpi, query: { appt_id: apptId }})">Book an Appointment</button>
<!-- I want this one to work -->
<button type="button" #click="$router.push({ path: '/physicians/', query: { appt_id: apptId, npi: physicianNpi }})">Book</button>
</div>
</template>
<script>
export default {
name: 'PhysicianLanding',
data () {
return {
msg: 'Welcome to the physicians landing page',
apptId: '05291988',
physicianNpi: '1346264132'
}
}
}
</script>
src/components/BookAnAppointment.vue
<template>
<div class="container">
<h1>Book an Appointment</h1>
<p>This is where you will book an appointment</p>
<h2>Query Params</h2>
<p>appt_id is {{ $route.query.appt_id }}</p>
<button type="button" #click="$router.push({ path: '/physicians' })">Go back</button>
</div>
</template>
<script>
export default {
name: 'BookAnAppointment',
props: ['npi'],
created () {
console.log('npi is ' + this.$route.params.npi)
console.log('appt_id is ' + this.$route.query.appt_id)
},
data () {
return {}
}
}
</script>
If you really want to use same url but with difference in just query params there is solution based on routes and their order - add same route but with different name and hook to redirect if query exist, and as optional you can set props dynamically for BookAnAppointment component:
// router
{
path: '/physicians',
component: PhysicianLanding,
beforeEnter(to, from, next) {
if (to.query && to.query.npi) {
// redirect to route below
next({ name: 'some route name', query: to.query })
} else
next()
}
},
{
path: '/physicians',
component: BookAnAppointment,
name: 'some route name',
// add props
props: (route) => ({ appt_id: route.query.appt_id, npi: route.query.npi })
}
// ... other routes
So when you use router in code to redirect user on button click you can directly use route 'some route name':
<button type="button" #click="$router.push({ name: 'some route name', query: { appt_id: apptId, npi: physicianNpi }})">Book</button>
Or if you use url based redirect it will be handled by hook from first route:
<button type="button" #click="$router.push({ path: '/physicians/', query: { appt_id: apptId, npi: physicianNpi }})">Book</button>
Related
I would like to grab a child component's "meta" property from parent. Is it possible somehow ?
I know there is a solution with an emit method, but is there some easier way to make it happen ?
// Default.vue <-- parent component
<template>
<h1>{{ pagetitle }}</h1>
<router-view />
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'LayoutDefault',
computed: {
pagetitle () {
let title = this.$route.meta.title // <--- I want to access child's component meta here
// if title not provided, set to empty string
if (!title) title = ''
return title
}
}
})
</script>
// router/routes.js
const routes = [
{
path: '/',
component: () => import('layouts/Default.vue'),
children: [
{
path: 'dashboard',
name: 'dashboard',
meta: { title: 'Dashboard', auth: true, fullscreen: false }, // <--- TAKE THIS
component: () => import('pages/dashboard.vue')
}
]
}
]
// pages/dashboard.vue <-- child component
<template>
<div>
dashboard content
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Dashboard',
meta: { // <--- this should be reachable from the parent component (Default.vue)
title: 'Dashboard',
auth: true,
fullscreen: false
}
})
</script>
You can get component info via $route.matched.
Here's a PoC:
const Dashboard = Vue.defineComponent({
template: "<div>Some dashboard</div>",
meta: { title: "Dashboard" },
})
const router = new VueRouter({
routes: [{ path: "/", component: Dashboard }],
})
const app = new Vue({
router,
computed: {
// Note that this takes the *last* matched component, since there could be a multiple ones
childComponent: (vm) => vm.$route.matched.at(-1).components.default,
},
}).$mount('#app')
<div id="app">
<h1>{{ childComponent.meta.title }}</h1>
<router-view />
</div>
<script src="https://unpkg.com/vue#2/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router#3/dist/vue-router.js"></script>
As suggested by Estus Flash in a comment, instead of taking the last matched component we can take the last matched component that has meta defined. To do that, replace the following:
vm.$route.matched.at(-1).components.default
with:
vm.$route.matched.findLast((r) => "meta" in r.components.default)
.components.default
Some approaches I could figure from the web:
Using ref by this.$refs.REF_NAME.$data (As done here: https://stackoverflow.com/a/63872783/16045352)
Vuex or duplicating the logic behind stores (As done here: https://stackoverflow.com/a/40411389/16045352)
Source: VueJS access child component's data from parent
I want to make search page which after I click its button will be redirected to another page. And this page will be like this
http://localhost:8080/search?q=foo
and my router index.js looks like this
const routers = [
{
path: '/search',
name: 'Search',
component: SearchPage,
props: route => ( { query: route.query.q } )
}
]
and the question is how do i get the query value in target page SearchPage, in Vue.js 3?
This Answer is still confusing me, because not using composition API and not in vuejs 3
Using useRoute
You don't need to send the query as a prop. It's better to use useRoute because it's simpler and it can be accessed from any component, not just the page view component.
import { useRoute } from 'vue-router';
export default {
setup() {
const route = useRoute();
console.log(route.query);
}
}
Using a prop
First change the router mapping so that query maps to the query object:
props: route => ({ query: route.query })
In the destination component, SearchPage, create a query prop which will receive the query object from the router:
props: ['query']
And access it in setup via the props argument:
props: ['query'],
setup(props) {
console.log(props.query.q);
console.log(props.query.status);
}
another setup you can try, send propname by url
{ path: '/search/:propname', name: 'Search', component: SearchPage, props: true },
and on searchpage, on created() you can get recive it
this.$route.params.propname
For Vue Router 4 :: Compositon API :: Vue3
Router-link attach params
<router-link
style="width: 100%"
class="btn btn-success btn-block edit"
:to="{ name: 'editUser',params: {id: user.id}}">
<i class="fa-solid fa-user-gear"></i>
</router-link>
In a page you are receiving,
<script>
export default {
props: ["id"],
setup(props, context) {
console.log(props.id);
},
};
</script>
In Your app.js
import { createApp } from 'vue';
...
...
const app = createApp(root);
app.use(router);
router.isReady().then(() => {
app.mount('#app');
})
How Can I pass router to my child component.
I have this as my router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default function () {
const Router = new VueRouter({
mode: 'history',
routes : [
{
path: '/',
beforeEnter: ifAuthenticated,
component: () => {
return import('./../container/Index.vue')
}
},
{
path: '/login',
beforeEnter: ifNotAuthenticated,
component: () => {
return import('./../container/logn.vue')
}
}
],
})
return Router
}
Now my "/" (index.vue) route have a component Navbar and the Navbar have a logout button which logs out the user and redirect them to login page
Consider this to be my index.vue (with what I have done)
<template>
<q-layout>
<Navbar :thisInfo="routerAndStore"/>
</q-layout>
</template>
<script>
import Navbar from "./../components/navbar.vue";
export default {
name: "PageIndex",
components: {
Navbar
},
data() {
return {
routerAndStore: this
};
}
};
</script>
And then in my navbar.vue I have done something like this
<template>
<div class="nav-pages-main">
<a #click="logoutUser">
<h5>Logout</h5>
</a>
</div>
</template>
<script>
export default {
name: "navbar",
methods: {
logoutUser: () => {
return this.thisInfo.$store.dispatch("GOOGLE_PROFILE_LOGOUT").then(() => {
this.$router.push("/login");
});
}
},
props: {
thisInfo: {
type: Object
}
}
};
</script>
but this doesn't seem to be working (this is coming out to be undefined), So if someone can help me figure out how we can pass this to our child component
Please refer to Vue-Router official documentation here
Basically, in their use case, the main component (index.vue) take a router as argument and provide <router-view> in its template as placeholder for component that would be rendered based on the current route.
In your code, I see that you use it the other way around using router to render the main component.
routes : [
{
path: '/',
beforeEnter: ifAuthenticated,
component: () => {
return import('./../container/Index.vue')
}
},
...
]
Could you try it again using the right way described in the documentation and tell me the result?
Edit: According to the App.vue that you posted (assuming it's the app entry point) then you should provide router to the App component.
<template>
<div id="q-app"> <router-view/> </div>
</template>
<script>
import router from '/path/to/your/router';
export default { name: "App", router };
</script>
<style>
</style>
The full code for this can be found at Vue-Router example
I'm working on a blog project, fully static, fetching data from the wordpress rest api.
I'm tring to display the article page when clicking on the title displayed on the index file.
I need the route to be custom depending on the slug of the post.
But I get a "This page could not be found"
The route changed event info :
Structure
pages
--| article
----| _slug.vue
--| index.vue
index.vue
<template>
<div class="container">
<h1>Blog</h1>
<ul>
<li v-for="article in posts" :key="article.id">
<nuxt-link :to="{ name: 'article-slug', params: {slug : article.slug} }">{{ article.title.rendered }}</nuxt-link>
</li>
</ul>
</div>
</template>
<script>
import axios from 'axios'
export default {
asyncData({ req, params }) {
// We can return a Promise instead of calling the callback
return axios.get('https://dev.lesdeuxvagues.com/api/wp-json/wp/v2/posts/')
.then((res) => {
return { posts: res.data.slice(0, 5) }
})
},
head: {
title: 'List of posts'
}
}
</script>
_slug.vue
<template>
<div>
<h1>
{{ title.rendered }}
</h1>
<template>
{{ content.rendered }}
</template>
<p><nuxt-link to="/">Back to home page</nuxt-link></p>
</div>
</template>
<script>
import axios from 'axios'
export default {
validate({ params }) {
return !isNaN(+params.slug)
console.log(params)
},
async asyncData({ params, error }) {
try {
const { data } = await axios.get(`https://dev.lesdeuxvagues.com/api/wp-json/wp/v2/posts/?slug=${+params.slug}`)
return data
} catch (e) {
error({ message: 'User not found', statusCode: 404 })
}
}
}
router.js
export function createRouter () {
return new Router({
mode: 'history',
base: '/',
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
scrollBehavior,
routes: [
{
path: "/article/:slug?",
component: _540807ba,
name: "article-slug"
},
{
path: "/",
component: _ac3e7d78,
name: "index"
}
],
fallback: false
})
}
Thanks for your help
I get it. I was trying to check is a string was NaN.
Solved.
I am using VueJS 2.0 and vue-router 2 and am trying to show a template based on route parameters. I am using one view (WidgetView) and changing components displayed in that view. Initially I show a widget list component (WidgetComponent), then when the used selects a widget or the new button in in the WidgetComponent in the WidgetView I want to swap the WidgetComponent out and display the WidgetDetails component, and pass information to that component:
WidgetComponent.vue:
<template>
...
<router-link :to="{ path: '/widget_view', params: { widgetId: 'new' } }"><a> New Widget</a></router-link>
<router-link :to="{ path: '/widget_view', params: { widgetId: widget.id } }"><span>{{widget.name}}</span></router-link>
</template>
<script>
export default {
name: 'WidgetComponent',
data() {
return {
widgets: [{ id: 1,
name: 'widgetX',
type: 'catcher'
}]}
}
}
</script>
WidgetView.vue
<template>
<component :is="activeComponent"></component>
</template>
<script>
import WidgetComponent from './components/WidgetComponent'
import WidgetDetail from './components/WidgetDetail'
export default {
name: 'WidgetView',
components: {
WidgetComponent,
WidgetDetail
},
mounted: function () {
const widgetId = this.$route.params.widgetId
if (widgetId === 'new') {
// I want to pass the id to this component but don't know how
this.activeComponent = 'widget-detail'
}
else if (widgetId > 0) {
// I want to pass the id to this component but don't know how
this.activeComponent = 'widget-detail'
}
},
watch: {
'$route': function () {
if (this.$route.params.widgetId === 'new') {
// how to pass id to this compent?
this.activeComponent = 'widget-detail'
}
else if (this.$route.params.widgetId > 0){
// how to pass id to this compent?
this.activeComponent = 'widget-detail'
}
else {
this.activeComponent = 'widget-component'
}
}
},
data () {
return {
activeComponent: 'widget-component',
widgetId: 0
}
}
}
</script>
WidgetDetail.vue
<template>
<option v-for="manufacturer in manufacturers" >
{{ manufacturer.name }}
</option>
</template>
<script>
export default {
props: ['sourcesId'],
...etc...
}
</script>
router.js
Vue.use(Router)
export default new Router({
routes: [
{
path: '/widget_view',
component: WidgetView,
subRoutes: {
path: '/new',
component: WidgetDetail
}
},
{
path: '/widget_view/:widgetId',
component: WidgetView
},
]
})
I couldnt get route paramers working but I managed to get routes working by hard coding the route ie
<router-link :to="{ path: '/widget_view/'+ 'new' }"> New Widget</router-link>
But I dont know how to pass an id to the given template from the script (not template) code in WidgetView.
Here is a basic example http://jsfiddle.net/ognc78e7/1/. Try using the router-view element hold your components. Also, use props inside components to pass in variables from the URL. The docs explain it much better http://router.vuejs.org/en/essentials/passing-props.html
//routes
{ path: '/foo/:id', component: Bar, props:true }
//component
const Bar = { template: '<div>The id is {{id}}</div>',props:['id'] }
Not sure which way you want it, but you could have the /foo/ path actually be the creation widget and then have the dynamic /foo/:id path. Or you could do like I did here and the foo path is like a start page that links to different things.