Use a Vue component for multiple templates - vue.js

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

Related

How to pass props through router link in VUE.JS?

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

vue.js "TypeError: Cannot read property 'path' of undefined"

I know this is a common question, but I have been going through my files now so many times without being able to the locate the error.
I am getting this error when I try to route to my components in my navigation menu.
My app.vue file:
<template>
<div id="app">
<Navbar
:nav-links="navLinks"
/>
<router-view/>
</div>
</template>
<script>
import Navbar from '#/components/Navbar'
export default {
components: {
Navbar
},
data: () => ({
navLinks: [
{
text: 'Home',
path: '/home'
},
{
text: 'About',
path: '/about'
},
{
text: 'Contact',
path: '/contact'
}
]
})
}
</script>
My Navbar component (This is where the error happens)
<template>
<nav>
<ul>
<li v-for="{link, index} in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false">
<router-link :to="link.path">
{{ link.text }}
</router-link>
</li>
</ul>
</nav>
</template>
<script>
export default {
props: ['navLinks'],
data(){
return {
hover: false,
}
}
}
</script>
How do I fix this?
<li v-for="{link, index} in navLinks" :key="index"...
should be
<li v-for="(link, index) in navLinks" :key="index"...
As it's now (destructured), link refers to a link property inside the object, not the object itself. Additionally, index is probably undefined, since the navLinks objects probably don't have an explicit property index. Therefore Vue might also complain about using invalid indexes in v-for.
Since you're only using the path prop, you could actually use destructuring, like this:
<li v-for="({ path }, index) in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false">
<router-link :to="path">
</li>
Another, unrelated note: hover property is currently being shared across all navLinks. If you expect it to somehow be related to the currently hovered element, yo uhave to save that separately (probably inside the navLink itself).
As for :nav-links="navLinks", what you've done is not only perfectly legal, but the recommended way of doing it (it's according to the HTML spec). Using :navLinks="navLinks" relies on Vue's HTML parser, which converts it to nav-links behind the scenes - inspect the HTML element and you'll notice it).
If you want to get into the details, you could have a look at this discussion on the subject. The result was: use either, but if you use camelCase it will be inconsistent with the rendered markup. If you use kebab-case, it will be consistent with rendered markup, so you won't have to deal with this difference when writing tests, for example, should you ever need to select elements by their attributes (jest converts camelCase to lowercase - hence it's inconsistent with the rendered markup, so the tests start passing/failing based on whether mount or shallowMount is used. Goes without saying, that's not really a good testing setup. )
The same exact discussion goes for using <SomeComponent /> vs <some-component />. While both work, using first needs to be addressed when writing tests if you need to select stubbed subcomponents.
Besides, vue/attribute-hyphenation (the way you did it) is part of the following vue linting presets:
plugin:vue/strongly-recommended
plugin:vue/vue3-recommended
plugin:vue/recommended
A prop in the Navbar component is named navLinks but you access it outside as nav-links.
This should work:
:navLinks="navLinks"
Incorrect syntax for v-for with {}. Use ():
li v-for="(link, index) in navLinks
You have done two mistakes here.
one is:
<template>
<div id="app">
<Navbar
:nav-links="navLinks"
/>
<router-view/>
</div>
Here you are binding with different name(nav-links), you should keep same name with which you are binding data and the name inside the props(navLinks).
Both names should be same.
Second one:
v-for="{link, index} in navLinks"
The syntax is wrong, the correct syntax should be
v-for="(link, index) in navLinks"

How to update properties of component for dynamic router-link in vuejs

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

Why isn't the value of the object properties inserted here?

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

Using plain JS in Vue.js Component

So, I want to create a navbar and rather than re-invent the wheel, I am using some public code to speed up my MVP dev.
Essentially, I am using this nav-bar code - https://codepen.io/PaulVanO/pen/GgGeyE.
But I am not sure of how I can implement jquery part within my Vue code (I have made a component, copied over html and css, now just need to integrate the jquery functionality within it.)
Here is the Jquery code I need to integrate.
$('#toggle').click(function() {
$(this).toggleClass('active');
$('#overlay').toggleClass('open');
});
It would be really thankful if anyone could help me accomplish with this.
Assuming you have your markup (html and css) as part of one component, getting the toggle to add/remove a class would be really simple, you just need to have a method toggle the active state and a data property to keep the data. An example would be better, so here it goes.
In your component object:
{
data() {
return {
isActive: false
}
},
methods: {
toggleMenu(){
this.isActive = !this.isActive
}
}
}
In your markup you need this
<div class="button_container" id="toggle" :class="{'active': isActive}" #click="toggleMenu">
<span class="top"></span>
<span class="middle"></span>
<span class="bottom"></span>
</div>
------------------------------------
<div class="overlay" id="overlay" :class="{'open': isActive}">
<nav class="overlay-menu">
<ul>
<li >Home</li>
<li>About</li>
<li>Work</li>
<li>Contact</li>
</ul>
</nav>
That should get you going, just note i used the shorthand form for v-on and for v-bind
EDIT:
Here's also a link to an updated pen with the whole example