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.
Related
Hi Vue enthusiasts out there,
I have been working on an multi-tenant application and stuck at dynamic layout problem.
Requirement: Load tenant specific layout.vue file from public folder and wrap <router-view> around it.
Tried few things like dynamic imports, defineAsyncComponent etc but couldn't get it working.
// router:
import store from '../store/index';
import NestedApp from '../views/NestedApp.vue';
// const layoutA = () => defineAsyncComponent(import(store.getters.pageLayout('LayoutA')));
const routes = [
{
path: '/:tenant:/:locale',
name: 'NestedApp',
component: NestedApp,
children: [
{
path: 'about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
meta: { layout: () => import(store.getters.pageLayout('LayoutA')) }
}
]
]
// NestedApp.vue:
<template>
<div class="NestedApp">
<navbar/>
<component :is="layoutWrapper">
<router-view/>
</component>
</div>
</template>
<script>
import Navbar from '../components/Navbar.vue';
export default {
name: 'NestedApp',
components: {
Navbar,
},
computed: {
layoutWrapper() {
console.info(`layout: ${this.$route.meta.layout}`);
return this.$route.meta.layout || 'div';
}
}
}
// LayoutA.vue:
<template>
<div class="LayoutA">
<span>Layout A</span>
<slot/>
</div>
</template>
I get following error in browser console:
Got a workaround to this problem.
Sending component via template string from backend API call and then creating a component out of it via defineComponent and markRaw methods.
API response:
"Layouts": {
"LayoutA": {
"name": "LayoutAbout",
"template": "<div class='LayoutA' style='background-color: darkgray'><span>Layout A</span><slot/></div>"
}
},
and then use in App.vue:
import { defineComponent, markRaw } from 'vue';
export default {
name: 'App',
methods: {
loadLayout(pageLayout) {
const layout = this.$store.getters.pageLayout(pageLayout);
this.layoutWrapper = layout ? defineComponent(markRaw({...layout})) : 'div';
}
},
created() {
this.loadLayout(this.$route.meta.layout);
},
beforeRouteUpdate(to) {
this.loadLayout(to.meta.layout);
},
}
<template>
<div class="App">
<navbar/>
<component :is="layoutWrapper">
<router-view/>
</component>
</div>
</template>
I got "artists prop is Undefined" error in the console
Structure of components:
Discover > ArtistSlider > ArtistItem
Discover passes the prop artists to ArtistSlider and it does it only when all the data is received from API. But ArtistSlider throws the error that prop artists is undefined for some reason. At the same time, ArtistItem doesn't throw an error despite it is the child of ArtistSlider and it receives the prop artist from ArtistSlider.
I read about vue lifecycle but still cannot get why it happens. Especially in the middle of the ​component tree.
Discover:
<template>
<div>
<h1>Discover page</h1>
<artist-slider :artists="collection.new_artists"/>
</div>
</template>
<script>
import store from "#/store";
import Page from "#/mixins/Page";
import ArtistSlider from "#/components/ArtistSlider";
export default {
extends: Page,
async beforeRouteEnter(to, from, next) {
const collection = await store.dispatch("fetchUrl", {
url: "discover",
params: { location: "uae" }
});
console.log('Received collection from API', collection)
next(vm => (vm.collection = collection));
},
components: {
ArtistSlider,
},
created() {
console.log('Discover Created', this.collection)
}
};
</script>
ArtistSlider:
<template>
<section>
<h2>New Artists</h2>
<ul>
<artist-item
v-for="artist in artists"
:key="artist.slug"
:artist="artist"
/>
</ul>
</section>
</template>
<script>
import ArtistItem from './ArtistItem'
export default {
name: "AppArtistSlider",
props: {
artists: {
required: true,
type: Array
}
},
components: {
ArtistItem
},
created() {
console.log('Slider Created', this.artists)
}
};
</script>
<style lang="scss" scoped>
</style>
ArtistItem:
<template>
<li>
<app-image :src="artist.avatar.small" :alt="artist.full_name" />
<h3>{{artist.full_name}}</h3>
<p>{{artist.art_type.name}}</p>
</li>
</template>
<script>
import AppImage from './AppImage.vue'
export default {
name: 'ArtistItem',
props: {
artist: {
required: true,
type: Object
},
},
components: {
AppImage
},
created() {
console.log('Item Created', this.artist)
}
}
</script>
Will apreciate any help. Thanks!
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 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>
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.