How to extract Route Params via Props - javascript

I am learning VueJs and trying to understand how to extract route params via props.
I was looking at the following documentation, where it seems to have three options to have this done, but I cannot understand it quite well so far - https://github.com/vuejs/vue-router/tree/dev/examples/route-props.
I have tried adding props: true to my router object array (routes.js file posted below) with no success as well.
As this is vue-cli study project I will post the separate pertinent blocks of code in order to try to illustrate this properly.
Main - App.vue below:
<template>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
<h1>Routing</h1>
<hr>
<app-header></app-header>
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import Header from './components/Header.vue'
export default {
components: {
appHeader: Header
}
}
</script>
<style>
</style>
Component - Header.vue below:
<template>
<ul class="nav nav-pills">
<router-link to="/" tag="li" active-class="active" exact><a>Home</a></router-link>
<router-link to="/user/10" tag="li" active-class="active"><a>User 1</a></router-link>
<router-link to="/user/5" tag="li" active-class="active"><a>User 2</a></router-link>
</ul>
</template>
Component - User.vue below:
<template>
<div>
<h1>The User Page</h1>
<hr>
<br>
<p>Route ID: {{id}}</p>
<button class="btn btn-primary" #click="goHome">Go to Home</button>
</div>
</template>
<script>
export default {
data(){
return {
id: this.$route.params.id
}
},
watch: {
'$route'(to, from) {
this.id = to.params.id;
}
},
methods: {
goHome(){
this.$router.push('/')
}
}
}
</script>
main.js below:
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import {routes} from "./routes";
Vue.use(VueRouter)
export const router = new VueRouter({
routes,
mode: 'history'
})
new Vue({
el: '#app',
router,
render: h => h(App)
})
routes.js below (located at SRC folder):
import User from './components/user/User.vue'
import Home from './components/Home.vue'
export const routes = [
{path: '', component: Home},
{path: '/user/:id', component: User}
]
Do I need to also set Props at User.vue component as well in order to make it work and quit using watch?
In other words, I would like to see my user route being listened at <p>Route ID: {{id}}</p> from this hardcoded 10 to 5 using this new method which I cannot understand, mentioned at the top of this post.
Could anyone please walk me through this issue in this specific situation?
Thanks in advance to all.

According to the docs on passing Props to Route components, you can decouple it, with the props option on the router config.
import User from './components/user/User.vue'
import Home from './components/Home.vue'
export const routes = [
{path: '', component: Home},
{path: '/user/:id', component: User, props: true}
]
<template>
<div>
<h1>The User Page</h1>
<hr>
<br>
<p>Route ID: {{id}}</p>
<button class="btn btn-primary" #click="goHome">Go to Home</button>
</div>
</template>
<script>
export default {
props: ['id'],
methods: {
goHome() {
this.$router.push('/')
}
}
}
</script>

Related

imported component into views not showing / VUE js

TLDR: Ive nested a component into a views folder and it wont show, I believe I have imported it correctly and think I'm missing something / any help here would be great
im importing a component into a views file which is then to be displayed via the vue router - the issue is the component or view does not show -
Oddly this code works if I ask the router to display the component as a stand alone, but not when I nest the component into the views file:
File structure is:
VIEWS FILE importing the component to be nested inside of it:
src/views/Contact.vue <<<< i imagine I have an issue here but I cannot seem to figure this out
<template>
<div class="contact_container">
<Contact/>
</div>
</template>
<script>
import Contact from '#/components/Contact.vue'
export default {
name: 'Contact',
components: {
Contact
}
}
</script>
Component file
src/components/Contact.vue
<template>
<div>
<h1>Welcome to the contact page</h1>
</div>
</template>
<script>
export default {
name: 'Contact',
data(){
return{
}
}
}
</script>
Views Router File:
src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Contact from '../views/Contact.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/contact',
name: 'Contact',
component: Contact
}
]
const router = new VueRouter({
routes
})
export default router
and finally App.veu
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/contact">Contact</router-link>
</div>
<router-view/>
</div>
</template>
/// UPDATED ///
In the code below - what does the name property do? 
(the code below is defining a component) 
<template>
    <div>
        <h1>Welcome to the contact page</h1>
    </div>
</template>
<script>
export default {
    name: 'Contact-Component', <<<< what does this do? 
    data(){
        return{
        }
    }
}
</script>
The reason why i'm a little lost here is because when we use this component we import it into another file using the below: 
import Contact from '../views/Contact.vue'
In that process we have defined the component as Contact (so it is not referenced by its name any more) .... so why did we define a name?
If there is any documentation on this that would be amazing and while it seems like a silly question (and probably is) it is distracting me with curiosity...
Thanks for any help -
Wally
change your code with this
<template>
<div class="contact_container">
<Contact/>
</div>
</template>
<script>
import Contact from './components/Contact.vue'
export default {
name: 'Contact',
components: {
"Contact":contact
}
}
</script>
The name is used to allow the component to recursively invoke itself in its template. It also helps when debugging as it allows for a more helpful error message.
Have a read over this to get a better idea: https://v2.vuejs.org/v2/api/#name
You have to let the app know it's a Vue component:
<script lang="js">
import Vue from 'vue';
export default Vue.extend({
name: 'Contact',
...
});

on-click method on a button that directs to another component

I have a main app page component with a search bar. The Search results that come back is in cards And I am looking to set up a "Click here to view more detail" that would be placed in each card. And it would link to the Details page of the one result clicked. How do I link these components on Vue and if the id could be passed? I am hoping that upon click of the button the component renders on the same page and not a new tab.
Thank you!
App.vue
<template>
<div id="app">
<Header/>
<SearchForm v-on:search="search"/>
<SearchResults
v-if="results.length > 0"
v-bind:results="results"
v-bind:reformattedSearchString="reformattedSearchString"/>
<Pagination
v-if="results.length > 0"
v-bind:prevPageToken="api.prevPageToken"
v-bind:next_page="api.scrollId"
v-on:prev-page="prevPage"
v-on:next-page="nextPage"
/>
</div>
</template>
<script>
import Header from './components/layout/Header';
import SearchForm from './components/SearchForm';
import SearchResults from './components/SearchResults';
import Pagination from './components/Pagination';
import Details from './components/Details'
import axios from 'axios';
export default {
name: 'app',
components: {
Header,
SearchForm,
SearchResults,
Pagination,
Details
},
data() {
return {
results: [],
reformattedSearchString: '',
api: {
baseUrl: 'https://test.org/api/v1/articles?',
max: 25,
q: '',
prevPageToken: '',
scrollId: ''
}
};
},
methods: {
search(searchParams) {
this.reformattedSearchString = searchParams.join(' ');
this.api.q = searchParams.join('+');
const { baseUrl, q, max} = this.api;
const apiUrl = `${baseUrl}&term=${q}&title_like=${q}&recent&max=${max}&full_results`;
this.getData(apiUrl);
},
prevPage() {
const { baseUrl, q, max, prevPageToken } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title_like=${q}&max=${max}&pageToken=${prevPageToken}`;
this.getData(apiUrl);
},
nextPage() {
const { baseUrl, q, max,scrollId } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title_like=${q}&max=${max}&recent&full_results&scroll_id=${scrollId}`;
this.getData(apiUrl);
},
getData(apiUrl) {
axios
.get(apiUrl)
.then(res => {
this.results = res.data.success.data;
this.api.prevPageToken = res.data.success.data.prevPageToken;
this.api.next_page = res.data.scrollId;
})
.catch(error => console.log(error))
}
}
};
</script>
Searchresults.vue
<template>
<div class="container mb-3">
<div class="d-flex mb-3">
<div class="mr-auto">
<h3>Search Results for "{{ reformattedSearchString }}"</h3>
</div>
<div class="btn-group ml-auto" role="group">
<button
#click="changeDisplayMode('grid')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'grid' }"
>
<i class="fas fa-th"></i>
</button>
<button
#click="changeDisplayMode('list')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'list' }"
>
<i class="fas fa-list"></i>
</button>
</div>
</div>
<div class="card-columns" v-if="displayMode === 'grid'">
<div class="card" v-bind:key="result._gddid" v-for="result in results">
<ArticleGridItem v-bind:result="result"/>
</div>
</div>
<div v-else>
<div class="card mb-2" v-bind:key="result._gddid" v-for="result in results">
<ArticleListItem v-bind:result="result"/>
</div>
</div>
</div>
</template>
<script>
import ArticleListItem from './ArticleListItem';
import ArticleGridItem from './ArticleGridItem';
import Details from './Details';
export default {
name: 'SearchResults',
components: {
ArticleListItem,
ArticleGridItem,
Details,
},
data() {
return {
title: 'Search Results',
displayMode: 'grid'
};
},
methods: {
changeDisplayMode(displayMode) {
this.displayMode = displayMode;
}
},
props: ['results', 'reformattedSearchString']
};
</script>
ArticleListItem.vue
<template>
<div>
<div class="card-body">
<h6 class="card-text">{{ result.title }}</h6>
<p
class="card-subtitle mb-2 text-muted"
>{{ result.publisher }} | {{ result.journal }} | {{ result.year }}</p>
<a :href="'https://test.org/api/articles?docid=' + result._gddid" target="_blank">
<i class="fa fa-download" alt="Download"> </i>
</a>
<router-link>
<v-btn dark to="{name:'Details', params: {id: article._gddid}}">
Click here for more Details
</v-btn>
</router-link>
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'ArticleListItem',
props: ['result'],
}
</script>
index.js
import Vue from 'vue';
import Router from 'vue-router';
import Details from '#/components/Details';
Vue.use(Router)
export default new Router({
routes: [
{
path: '/Details/:id',
name: 'Details',
component: Details
}
]
});
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js'
import moment from 'moment'
Vue.config.productionTip = false
Vue.filter('formatDate', function (value) {
if (!value) return ''
return moment(value.toString()).format('MM/DD/YYYY hh:mm')
})
new Vue({
router,
render: h => h(App),
}).$mount('#app')
use <router-link> tag for it to work
When using Vue Router, instead of using <a> tags, you can use <router-link> tags.
Here's a basic example similar to the Vue Router documentation for dynamic route matching.
If you have this...
const router = new VueRouter({
routes: [
{ path: '/Details/:id', component: DetailsPage, name: 'Details' }
]
})
You can have router-links declared in, say, your ArticleListItem.vue file like:
<v-btn dark to="{name:'Details', params: {id: article.id}}">
Click here for more Details
</v-btn>
You can't just go to a generic details page. There needs to be some dynamic part of the route (which I've called id in my example) to specify which article's page to go to.
Edit
An additional problem is in your Router setup:
From the Vue Router docs:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
...needs to be in your main.js file. The declaration of Vue.use in your index.js file isn't doing anything. Just for conciseness. Make it one file:
WhateverYourOneTrueFileIsGoingToBeCalled.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router';
import Details from '#/components/Details';
import moment from 'moment'
Vue.config.productionTip = false
Vue.filter('formatDate', function (value) {
if (!value) return ''
return moment(value.toString()).format('MM/DD/YYYY hh:mm')
})
Vue.use(VueRouter)
router = new VueRouter({
routes: [
{
path: '/Details/:id',
name: 'Details',
component: Details
}
]
})
new Vue({
router,
render: h => h(App),
}).$mount('#app')

Vue router does not render/mount root path component

i am making a page with vue, vue-router and laravel, the problem, when i enter in localhost/myproject/public_html/, the Home component is not rendered in the router-view, if i click in the router link to the Service component, it render the content normally, and when i click to the home path it render the home component content, so why this happens? this is my app.js structure
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import App from './views/App'
import Home from './views/Home'
import Service from './views/Service'
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: 'servicios',
'name' : 'services',
component: Service
}
],
});
const app = new Vue({
el: '#app',
components : { App },
router,
});
this is my App.vue
<template>
<div>
Hola
<router-link :to="{ name : 'services' }">
ir a servicios
</router-link>
<router-view>
</router-view>
</div>
</template>
<script>
import PageLogo from '../components/PageLogo'
import Loading from '../components/Loading'
export default {
mounted : function(){
console.log(this.$router.currentRoute.path)
},
components : {
'page-logo' : PageLogo,
'loading' : Loading
},
methods : {
ajaxOver : function() {
}
}
}
</script>
this is my Home.vue
<template>
<div class="row">
Home page
<router-link :to="{ name : 'services' }">
go to services
</router-link>
</div>
</template>
<script>
export default {
mounted: function() {
console.log('hola')
}
}
</script>
and this is Service.vue
<template>
<div>
Services page
<router-link :to="{ name : 'home' }">
go to home
</router-link>
</div>
</template>
<script>
export default {
mounted : function(){
console.log('services')
}
}
</script>
so how can i solve this? if i reload the page in any route, the component should be mounted, but in the vue router is not being displayed, so the component is not mounted.
Edit:
As requested, in App.vue the log is /myproject/public_html/
I've never used Laravel before, but I think you're looking for base.
Vue docs:
type: string
default: "/"
The base URL of the app. For example, if the entire single page application is served under /app/, then base should use the value "/app/".
Since you're serving your project at localhost/myproject/public_html/, vue is seeing /myproject/public_html/ instead of /.
You can change this by adding the base route to the vue-router constructor.
const router = new VueRouter({
mode: 'history',
base: '/myproject/public_html/',
...
}

Template not being displayed on click on Vue link

I'm creating a Laravel website that will be a single page application. I'm new to VueJS even if I have experience with javascript in general.
I need to display several routes, such as /blog/new, /blog/:id, /blog/edit, etc. /blog itself is defined by Laravel, and works as the blog index.
I installed vue-router, and tried to follow some examples I saw but nothing happens:
app.js
import './bootstrap';
import Vue from 'vue';
import VueRouter from 'vue-router';
const blogIndex = { template: require('./components/blog/index.vue') };
const blogNew = { template: require('./components/blog/new.vue')};
const routes = [
{
path: '/blog',
component: blogIndex,
name: 'blog-index'
},
{
path: '/blog/new',
component: blogNew,
name: 'blog-new'
}
];
const router = new VueRouter({
routes,
mode: "history"
});
Vue.use(VueRouter);
Vue.component('example', require('./components/Example.vue'));
Vue.component('navbar', require('./components/Navbar.vue'));
Vue.component('blog_index', require('./components/blog/index.vue'));
console.log(testvar);
const app = new Vue({
router
}).$mount('#app');
components/blog/index.vue
<template>
<div>
<h1>Blog index</h1>
<router-link :to="{ name: 'blog-new' }">New article</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
mounted() {
console.log('blog#index mounted');
}
}
</script>
components/blog/new.vue
<template>
<h2>Here is blog/new y'all</h2>
</template>
<script>
export default {
mounted() {
console.log('blog#new mounted');
}
}
</script>
So far, the only thing that works is my address bar that gets modified, but the template itself doesn't seem to be loaded (I go from /blog to /blog/new).
I've seen that I need add <router-view></router-view> in my html file, but this triggers an error and blocks VueJS's display.
My question is: what am I missing, or what did I do wrong?
Thank you in advance
You need to use <router-view></router-view> in your main App.vue file, which is the part that new routes are loaded into.
<template>
<router-view></router-view>
</template>
You currently state you have this in components/blog/index.vue - which I believe is just for your nested routes. https://router.vuejs.org/en/essentials/nested-routes.html

vue-router this.$route object is always empty with single file components

I am using single file components with vue-router and vue-2.0 and I am having an issue I can't seem to be able to resolve. The this.$route object called from a component always returns empty values.
e.g.
Messages.vue
<template>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Post List</div>
<div class="panel-body">
<li v-for="item in items">
{{ item.message }}
</li>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
created() {
console.log(this.$route);
},
mounted() {
console.log('Component mounted.')
},
data() {
return {
items: [
{message: 'Foo'},
{message: 'Bar'}
]
}
},
}
</script>
App.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Messages from './components/Messages';
Vue.use(VueRouter);
const routes = [
{ path: '/user/get/:id', component: Messages},
]
const router = new VueRouter({
routes
})
const app = new Vue({
router,
el: '#app',
components: { Messages }
});
Any help will be greatly appreciated.
You just need to tell Vue here to render the router views you can do this by modifying the Vue initialization by adding a template that holds the route-view directive.
const app = new Vue({
router,
el: '#app',
template: '<router-view/>',
components: { Messages }
})
That should make it work for you.
It is difficult to know what the problem is because you haven't included the HTML representing the template that the root component uses to render.
If your root component is rendering a router-view component then the route params should be available in the Messages component instance.
Here is an example using your setup: https://codepen.io/autumnwoodberry/pen/WEBEKd?editors=1010

Categories