Pass component as prop in object param - vue.js

I have the following Card Grid set up in Vue3:
<template>
<div class="max-w-5xl mx-auto">
<div class="ml-20 pr-72">
<h2 class="text-4xl pb-8">{{ title }}</h2>
<p class="font-normal text-base pb-20">{{ description }}</p>
</div>
<div class="grid grid-cols-2 gap-5">
<div
v-for="card in cards"
:key="card.title"
class="rounded-xl py-6 px-5 bg-cover bg-center flex flex-col justify-between items-start h-60"
:style="{ backgroundImage: `url(${card.imageUrl})` }"
>
<div
class="rounded-full p-4 bg-gradient-to-r from-prospire-blue to-prospire-light-blue"
>
{{ card.icon }}
</div>
<p class="text-white font-light text-2xl">{{ card.title }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
title: string;
description: string;
cards: any[];
}>();
</script>
<style scoped></style>
Which i call using:
<CardGrid
:title="$t('aboutUs.cardGrid.title')"
:description="$t('aboutUs.cardGrid.description')"
:cards="[
{
title: $t('aboutUs.cardGrid.cards[0].title'),
imageUrl:
'https://images.pexels.com/photos/5595573/pexels-photo-5595573.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
icon: <BeakerIcon />,
},
]"
/>
This however gives me a BeakerIcon' refers to a value, but is being used as a type here. Did you mean 'typeof BeakerIcon'? error.. Is it possible to pass a 3rd party component (icon in this case), to a component in a way?

One option is to pass card.icon as a string, such as 'BeakerIcon' and do
<div class="rounded-full p-4 bg-gradient-to-r from-prospire-blue to-prospire-light-blue">
<component :is="card.icon" />
</div>
...granted the icon you pass has been registered as a component.
It also work without registering the component globally.
Here is an example on sfc.vuejs.org passing the imported components in the props.

Related

How can I preserve data between switching between tabs in Vue?

I'm trying to learn some Vue, I have tab component it has 2 tabs with some dropdowns and text fields inside, the problem is when I switch between tabs it loss data like selected items from dropdowns or text values, I've tried with v-model but nothing, any help?
Here is the tab view
<script setup>
import { ref } from 'vue';
const nestedRouteItems = ref([
{
label: 'Personal',
to: '/uikit/ficha'
},
{
label: 'Seat',
to: '/uikit/ficha/seat'
},
]);
</script>
<template>
<div class="grid">
<div class="col-12 md:col-12">
<div class="card p-fluid">
<h5>Ficha</h5>
<div class="col-12 md:col-12">
<div class="card card-w-title">
<h5>Tab container</h5>
<p>Lorem ipsum dolor sit amet, consectetur.</p>
<TabMenu :model="nestedRouteItems" />
<router-view />
</div>
</div>
</div>
</div>
</div>
</template>
Here is the content of first tab:
<template>
<div class="flex align-items-center py-5 px-3">
<div class="card p-fluid" style="width:800px">
<h5>Datos personales</h5>
<div class="field">
<label for="name1">Nombre</label>
<InputText id="name1" type="text" />
</div>
</div>
</div>
</template>
Here is the content of second tab:
<script setup>
import { ref } from 'vue';
const dropdownItems = ref([
{ name: 'Principal', code: 'Principal' },
{ name: 'Laboral', code: 'Laboral' },
{ name: 'Familiar', code: 'Familiar' }
]);
const dropdownItem = ref(null);
</script>
<template>
<div class="flex align-items-center py-5 px-3">
<div class="card p-fluid" style="width:800px">
<h5>Domicilio</h5>
<div class="field">
<label for="tipoDomicilio">Tipo de domicilio</label>
<Dropdown id="tipoDomicilio" v-model="dropdownItem" :options="dropdownItems" optionLabel="name" placeholder="Elegir opciĆ³n..."></Dropdown>
</div>
</div>
</div>
</template>
You can use KeepAlive to do this.
<KeepAlive> is a built-in component that allows us to conditionally
cache component instances when dynamically switching between multiple
components.
If your tab components are dynamic then use KeepAlive like this-
<KeepAlive>
<component :is="tabComponent" />
</KeepAlive>
If you are using tab components individually then do like this-
<KeepAlive>
<tab-a></tab-a>
<tab-b></tab-b>
</KeepAlive>
You can read more about KeepAlive in the documentation.

Nuxt Props pass to data and set images

how do i pass props into data ? i have a object that pass into prop and i want prop to set the image url with data "mainImage", must keep the mainImage.
<template>
<!-- start Product Header -->
<div class="ps-product__header">
<div class="ps-product__thumbnail" data-vertical="false">
<div class="ps-wrapper">
<div class="ps-product__gallery">
<div class="col-12 px-md-2 d-none d-md-block">
<div class="" style="cursor: pointer">
<b-img :src="mainImage" alt="" style="width: 100%" class="image" #click="showMainImage()"></b-img>
</div>
</div>
</div>
</div>
<div class="ps-product__variants">
<div class="col-12 d-none d-md-block my-4">
<div class="row">
<div class="col-3" v-for="(image, index) in product.images" :key="index">
<div class="thumbnail" style="cursor: pointer" #click="changeMainImage(image.src)">
<b-img :src="`${image.src}`" style="width: 100%" alt="" class="image" :class="mainImage === image ? 'activess' : ''"></b-img>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
here is the script
<script>
export default {
name: 'ShopHeader',
props: {
product: {
type: Object,
required: true
}
},
data() {
return {
mainImage: String,
}
},
methods: {
changeMainImage(image) {
this.mainImage = image
}
},
mounted() {
this.mainImage = this.product.image
console.log(this.mainImage)
}
}
</script>
sample : https://stackblitz.com/edit/nuxt-starter-pake3o?file=components%2FShopHeader.vue
when you type on the stackblitz url with /product, the image will show up but when you go through the link the image wont show up, so anyone know how to fix this problem ? i assume it was mounted only load once and component wont render after it.
add v-if to tag
<img v-if="mainImage" :src="mainImage" class="image">

Vue is not rendering on right tags component

The way i am defining my props;
<template>
<div>
<vinput id="login_name" label="Phone number"></vinput>
<div class="flex flex-col">
<label for="email-phone">School slug</label>
<input type="text" id="email-phone" class="border py-2 rounded">
</div>
</div>
</template>
<script>
import vinput from '../../components/Input.vue'
export default {
components:{
vinput
}
}
</script>
The way i am passing the props
<vinput id="login_name" label="Phone number"></vinput>
Please why is the browser rendering it this way:
<div class="mt-4">
<div class="flex flex-col" id="login_name" label="Phone number">
<label></label>
<input type="tex" class="border py-2 rounded">
</div>
<div class="flex flex-col"><label for="email-phone">School slug</label><input type="text"
id="email-phone" class="border py-2 rounded"></div></div>
I was expecting interpolation in the label and input not in the div
====Edited====
vinput component
<template>
<div class="flex flex-col">
<label :for="id">{{ label }}</label>
<input :type="type ? type : 'tex'"
:class="classes ? classes :''" class="border py-2 rounded"/>
</div>
</template>
<script>
export default {
data(){
return {
props:['label', 'id','type', 'classes']
}
}
}
</script>
Move your props:['label', 'id','type', 'classes'] away from data.
Those are totally different things.
<script>
export default {
props: ['label', 'id','type', 'classes'],
data(){
return {};
},
};
</script>

How to send & receive object as a props in Vue.js

I have a Product object and I am taking it from API call. So in parent component I want to send this object into the child object which is SoldTickets component.
So here is my parent component:
<template>
<section id="cart-page" class="row">
<div v-for="item in cart.attributes.items" :key="item.id">
<div class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
<div class="title">WARENKORB</div>
<SoldTickets :item = item />
</div>
</div>
</section>
</template>
And my child:
<template>
<div id="sold-tickets">
<div class="card">
<div class="sold-tickets-actions properties">
<div class="sold-tickets-inner">
<div class="ticket-details">
<div class="ticket-prop">
<div class="ticket-name">{{ item.product_name }}</div>
<div class="ticket-type">{{ item.variation_name }}</div>
</div>
</div>
<DeleteButton #click.prevent="removeProductFromCart(item.id)" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
data: {
item: Object,
}
},
}
</script>
So I am quite new in Vue so I am not really confident with props. COuld you please help me with this.
You mostly have it right, but there are a couple of errors:
Your props aren't declared correctly in the child component, you shouldn't have the "data" in there. All props you want to declare go directly under the "props" key of the component declaration:
export default {
props: {
item: Object,
},
}
You're also missing quotes around the attribute value in the parent component, so that's invalid HTML:
<SoldTickets :item = item />
should be
<SoldTickets :item = "item" />
Just wrap the bound value with "" or '' like :
<SoldTickets :item="item" />

Prop not getting updated in child component on value change

In my App component (parent) I have:
<div class="bg-dark text-white text-center p-3 content">
<div class="form-group">
<input class="form-control" v-model="dogBreed" />
</div>
<my-child-comp greeting="Hello from parent" v-bind:dog-breed="dogBreed" />
</div>
...
data() {
return {
dogBreed: "Pit Bull"
}
},
And in the child view:
<template>
<div class="bg-primary text-white text-center m-2 p-3 content">
<h3>Dog: {{ dog }}</h3>
</div>
</template>
...
props: ["greeting", "dogBreed"],
data() {
return {
dog: this.dogBreed,
}
},
I do get the initial value for dogBreed from the parent component inside of the child component, but when I change the value of the input field the same change is not reflected, why?
The prop in the parent component is not named properly, it should be the exact name of the props which is dogBreed you used in the child
component
<div class="bg-dark text-white text-center p-3 content">
<div class="form-group">
<input class="form-control" v-model="dogBreed" />
</div>
<my-child-comp greeting="Hello from parent" v-bind:dogBreed="dogBreed" />
</div>
...
data() {
return {
dogBreed: "Pit Bull"
}
},
Your child component can remain the same but use computed instead of data. Having the same name will not cause any computational
problems and using computed instead of data should fix up the dynamic
nature of your code.
<template>
<div class="bg-primary text-white text-center m-2 p-3 content">
<h3>Dog: {{ dog }}</h3>
</div>
</template>
...
props: ["greeting", "dogBreed"],
computed: {
dog(){
return this.dogBreed
}
},
If however you are keen on using dog-breed then you must rename the prop in you child component to dog-breed before binding it in your parent component.