I hope you are having a great day. Actually, I'm curious whether we can pass props through the router link in vue. js or not?
Scenario
Suppose i have two components.
(a) Shop Now (Parent Component) which contains array of products as follow:
<script>
export default {
data()
{
return{
Products:[
{id:1,name:'Shirts',Detail:'Lorem'},
{id:2,name:'Shoes',Detail:'Lorem'},
{id:3,name:'Bags',Detail:'Lorem'},
{id:4,name:'Glasses',Detail:'Lorem'},
]
}
}
}
</script>
Now, whenever a user click on one of the products the data of particular product should be displayed on "Product Details Component".
This is how the product will be displayed to user:
<div class="product-card" v-for="Product in Products" :key="Product.id">
<router-link :to="{name:'ProductDetail',params:{id:Product.id} }">
<h5>
{{Product.name}}
</h5>
</router-link>
</div>
I'm successfully getting the ID of the product but I want to pass the other data suppose detail as well that should be visible to "Product Details Component"
Any lead would be highly appreciated. Thanks :)
Related
I have a pretty simple implementation so I feel like this may be a silly question. Just starting with vue I am trying to make a component be able to list other components; kind of like a reusable List, Table, Grid, ect.
Parent component imports gridView as a normal component:
<template>
<div :id="id" class="grid-view">
<slot></slot>
</div>
</template>
And then in another component I attempt to build this:
<grid-view :dataSource="items">
<grid-view-item-test
v-for="(item, index) in items"
:key="index"
:title="item.title"
/>
</grid-view>
And then grid-view-item-test is simple. title being a prop. Works completely fine standing alone or without the use of a slot, but I wanted to make grid-view reusable to just take whatever other component I add and display as-is.
<template>
<div>
<div>{{ title }}</div>
</div>
</template>
If I were to do something like this:
<grid-view :dataSource="items">
<grid-view-item-test
:key="index"
:title="'Test 1'"
/>
<grid-view-item-test
:title="'Test 2'"
/>
<grid-view-item-test
:title="'Test 3'"
/>
</grid-view>
It works completely fine. Am I doing the loop wrong? I can confirm the loop works to get the number of items correctly. If I make a default fallback on the slot - I get the correct number, and I have printed it directly outside of the grid-view component.
Is this not a possibility? If not, would you just use HTML instead of a component for a reusable table as that seems to work fine.
Edit:
It works completely fine if I use an of strings, or numbers, but not objects.
I've tracked it down to items being an empty array as a computed variable and then throwing TypeError: null is not an object (evaluating 'oldChildren[i]'). Can confirm that items begins empty, and then is populated once a database call sets it, but I'm guess I'm not able to do something like that with slots?
After more testing it fails when you update the dataSet (items) at all. Is re-rending not possible with slots like this?
It works flawlessly if I have an empty div (or anything) when there are no items in the initial array.
You probably should provide more a more accurate code because I don't see any problem with what you provided
Working example:
const gridView = Vue.component('gridView', {
props: ['dataSource'],
template: `
<div class="grid-view">
<h4>My grid</h4>
<slot></slot>
</div>
`
})
const gridItem = Vue.component('grid-view-item-test', {
props: ['title'],
template: `
<div>
<div>{{ title }}</div>
</div>
`
})
const vm = new Vue({
el: '#app',
data: () => ({
items: [{
title: 'Test 1'
},
{
title: 'Test 2'
},
{
title: 'Test 3'
},
]
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<grid-view :data-source="items">
<grid-view-item-test v-for="(item, index) in items" :key="index" :title="item.title" />
</grid-view>
</div>
I have one header page that has two tabs. The two tabs direct to two different pages. I am using the area prop that I defined in the header page router view for the system page. It works fine when I am on the system page, but when I go to the product tab and come back to system tab, the prop in the system tab
loses the value. It shows as the string "area" in the prop. I would like to get the same value when I switch tabs.
<div class="tabs">
<ul>
<li #click="$router.push({ name: 'system' })">
<a>SYSTEM</a>
</li>
<li #click="$router.push({ name: 'product' })">
<a>Product</a>
</li>
</ul>
</div>
<router-view :area="area"/>
</div>
</template>
System view:
export default {
props: ['area'],
Instead of using #click binding you will be much better off using <router-link> especially since you are using the router-view
this will allow you to pass params simply in your app.
https://router.vuejs.org/api/
you declare your routes like so
const routes = [
{
path: '/glossary/:subjectName',
name: 'glossaryWithParams',
component: GlossaryPage,
},
]
Then in your code you can build the element simply like so:
<router-link
:to="`/glossary/${subjectName}`"
class="tabs__link"
>
Subject Glossary
</router-link>
```
Once you implement Router-link your history push state will also be resolved, and your params will work normally.
So new to Vue and haven't found anything that specifically addresses my issue.
I have a simple Vue app using VueRouter where I am trying to generate a bracket-style sports tournament that records the outcomes of the different games in the tournament.
I need to make an asynchronous axios call to a server to get info on a specific game. I do not know how to update my component properly to get this info.
App.vue is very simple. The home page is just an overview of the bracket
<template>
<div id="app">
<div id="nav">
<router-link :to="{ name: 'bracket'}">Bracket</router-link>
</div>
<router-view />
</div>
</template>
I have a view component, Bracket.vue, and, for now, all I want this view do is provide links to the different matchups in the tourney.
I have made this work pretty well dynamically as follows:
<template>
<div class="home">
<div id="nav">
<div v-for="round in rounds" :key="round">
<router-link v-for="game in gamesPerRound" :key="matchupKey(round, game)" :to="{ name: 'matchup', params: {round: round, game: game} }">Matchup {{ round }} {{ game }}</router-link>
</div>
</div>
</div>
</template>
When link is clicked, I pull up another view component, Matchup.vue, which I would like to display certain data about the matchup. (MatchupService is an axios API instance)
I pass the round # and the game # as props via router-link. These load fine in the Matchup.vue.
However, when I try to make an asynchronous call to get the matchup data, nothing happens. the matchup property never updates, not on the link click or ever. So I either get nothing (when I use a ternary as per below) or an error if I just try to use the matchup value
Matchup.vue
<template>
<div class="matchup">
<h1>Round {{ round }}</h1>
<h2>Game {{ game }}</h2>
<h6>Team 1: {{matchup.teams ? matchup.teams[0]: 0}}</h6>
<h6>Team 2: {{matchup.teams ? matchup.teams[1] : 0}}</h6>
</div>
</template>
<script>
import MatchupService from '#/services/MatchupService.js'
export default {
props: ["round", "game"],
data() {
return {
matchup: {},
}
},
async updated() {
let matchups = await MatchupService.getMatchups()
this.matchup = matchups.data.rounds[this.round].games[this.game]
},
}
</script>
I have tried many different approaches on this: updated vs. created vs. other hooks, I've tried to update a property post-load and hook that to v-if, etc.
I am just totally stymied on this so hoping for some help.
Looks like you need to use navigation hook beforeEnter in your router.js file or you can use beforeRouteEnter hook directly in your compoennt file. (NOTICE! Using beforeRouteEnter in a component file you can't access 'this', so maybe, there is a reason to use Vuex if you want to store some data within serfing your app). There you can define/fetch/set any data before redirect user to a specific page. By this way you do any actions you want and set it before redirecting.
More about you can find here:
https://router.vuejs.org/guide/advanced/navigation-guards.html
I started learning vue yesterday and I'm now fiddling around on the CLI3.
Currently I'm trying out the different approaches to inserting data into my markup.
Here, I basically want to make a "list of Lists".
This here is list1:
<template>
<div>
<ul v-for="item in items">
<li :text="item"></li>
</ul>
</div>
</template>
<script>
export default{
name:"list1",
data() {
return {
items: {
item1 : "itemA",
item2 : "itemB",
item3 : "itemC"
}
}
}
}
</script>
This is the list of lists:
<template>
<div>
<h1>All my stuff in a biiig list!</h1>
<listOfLists />
</div>
</template>
<script>
import listOfLists from '#/components/listOfLists.vue'
export default {
name: 'myComplexView.vue',
components: {
listOfLists
}
}
And this is inserted into myComplexView.vue inside views (im working with routing as well, though it doesnt work perfectly yet as you will see on the screenshot), which you can see here:
<template>
<div>
<h1>All my stuff in a biiig list!</h1>
<listOfLists />
</div>
</template>
<script>
import listOfLists from '#/components/listOfLists.vue'
export default {
name: 'myComplexView.vue',
components: {
listOfLists
}
}
</script>
This is the result Im getting:
https://imgur.com/H8BaR2X
Since routing doesnt work correctly yet, I had to enter the url into the browser manually. Fortunately, the site at least loaded that way as well, so I can tackle these problems bit by bit ^^
As you can see, the data was iterated over correctly by the v-for.
However, the data wasn't inserted in the text attribute of the li elements.
I'm a bit clueless about the cause though.
Maybe I'm not binding to the correct attribute? Vue is using its own naming conventions, based off standard html and jquery as far as I understood.
You've got this in your template:
<li :text="item"></li>
This will bind the text attribute to the value, outputting, e.g.:
<li text="itemA"></li>
You should be able to see this in the developer tools. In the picture you posted you hadn't expanded the relevant elements so the attributes can't be seen.
I assume what you want is to set the content. For that you'd either use v-text:
<li v-text="item"></li>
or more likely:
<li>{{ item }}</li>
Either of these will output:
<li>itemA</li>
On an unrelated note, I would add that this line will create multiple lists:
<ul v-for="item in items">
It's unclear if that's what you want. You're going to create 3 <ul> elements, each with a single <li> child. If you want to create a single <ul> then move the v-for onto the <li>.
I'm fairly new to Vue and I'm not even sure if I've phrased my question correctly. Here is what I am trying to achieve. I am using a card cascade from bootstrap, each card show part of a blog post. Each card has a link to a webpage for the whole blog.
To try and achieve this I have two vue files. cardCascade.vue and singleBlog.vue. My problem is at the moment I have to create a different singleBlog.vue files for each blog I have.
For example, suppose I have two blog posts in my database. cardCascade.vue will also have two links to individual blog posts. Blog post 1 will then use singleBlog1.vue and blog post 2 will then use singleBlog2.vue
What can I do so that I can achieve this more efficiently such that I only need one singleBlog.vue and it dynamically adjusts the content based on the link I select from cardCascade.vue?
What I have right now for parts of the cardCascade.vue
<b-card v-for="blog in blogs_duplicate" title="Title" img-src="https://placekitten.com/500/350" img-alt="Image" img-top>
<b-card-text>
<!--{{getBlogOfType("Vue",blog.id)}}-->
{{getBlogOfType("Vue",blog.id)}}
</b-card-text>
<b-card-text class="small text-muted">Last updated 3 mins ago</b-card-text>
</b-card>
Below is what I have right now for singleBlog.vue, keep in mind right now it just displays all the blog posts in a list.
<template>
<div id="single-blog">
<ul>
<article>
<li v-for="blog in blogs" v-bind:key="blog.id">
<h1>{{blog.title}}</h1>
<i class="fa fa-cogs" aria-hidden="true"></i>
<router-link v-bind:to="{name:'datascience-single', params: {blog_id: blog.blog_id}}">
<p>{{blog.content}}</p>
</router-link>
</li>
</article>
</ul>
</div>
</template>
<script>
import db from './firebaseInit'
export default{
data(){
return{
id:this.$route.params.id,
blogs:[],
}
},
created(){
//this.$http.get('http://jsonplaceholder.typicode.com/posts/' + this.id).then(function(data){
//this.blog = data.body;
db.collection('Blogs').orderBy('Type').get().then(querySnapshot =>{
querySnapshot.forEach(doc => {
//console.log(doc.data());
const data={
'id': doc.id,
'content': doc.data().Content,
'type': doc.data().Type,
'title': doc.data().Title,
'blog_id':doc.data().blog_id,
}
this.blogs.push(data)
})
})
}
}
</script>
It seems as though you should be giving your common component as a props information as to what it is rendering. Meaning you would make the call to the api in the parent and then make your child a "dumb" component. In your case you should make the calls to the api in cardCascade and then pass into your singleBlog component an id as props. Although in your case I do not see you using this component in the parent at all, where is it?
Like Michael said, the right way to do that is making a props and receives that prop from your router. Then, when you make your requisition, you'll pass this prop id. You can read more about props in here: https://v2.vuejs.org/v2/guide/components-props.html