How to pass id to modal window component - vue.js

Im using Vue and Ionic, I dont know how to pass my lesson.id to the method openModal()
explanation: I have a card with my data - lesson data, where are also comments, when user clicks on them, modal window is open, I need to pass id of the lesson to my modal window as props, so I can display comments for the lesson.
<ion-content>
<div
class="lesson-card"
v-for="lesson in lesson.video_lessons"
:key="lesson"
>
<div class="lesson-content">
<h2>{{ lesson.content_description }}</h2>
<div class="tags">
<span v-for="tag in lesson.tags" :key="tag">
#{{ tag }}
</span>
</div>
<img
v-if="lesson.content_thumbnail"
:src="`${lesson.content_thumbnail}`"
alt="theme-img"
height="600"
/>
</div>
<div class="sidebar-icons">
bookmark
heart
<p>{{ lesson.likes }}</p>
<a #click="openModal">comments</a>
<p>lesson id: {{ lesson.id }}</p>
</div>
</div>
</ion-content>
this is my method
async openModal() {
const modal = await modalController.create({
component: CommentsModal,
componentProps: { id: 1 }, // i need to replace this 1 with id of the lesson
})
return modal.present()
},

In template, pass it like
<a #click="openModal(lession.id)">comments</a>
and in method
async openModal(payload) { // change added
const modal = await modalController.create({
component: CommentsModal,
componentProps: { id: payload}, // Change added
})
return modal.present()
},

Related

Passing props dynamically to a component inside a v-for loop

I have a v-for loop that iterates through an array of meetings (meetings between sellers and potential buyers of used cars) and prints a card for each meeting, a basic display of who the meeting is with, what car it is about and the scheduled date. Now, I implemented a button that when clicked, opens a dialog with a Google Maps component that shows the marker for the agreed location of the meeting.
My problem is that no matter what card I click on, the dialog will always display the location of the LAST card, regardless of which has been clicked. I would think that since Im calling the component INSIDE the v-for loop it would pass props dynamically for each card, on each iteration, but that does not seem to be the case.
Here is the HTML:
<div
v-for="meeting in meetings"
:key="meeting.did"
class="col-12 col-md-6 col-lg-3 q-pa-md q-mx-xl"
>
<q-card class="my-card homeCard q-pa-md">
<q-dialog class="mapDialog flex column" v-model="mapDialog">
<MeetMapComponent
:key="componentKey"
:mapDiv="mapDiv"
:mapData="meeting.address"
:buyerName="meeting.name"
/>
</q-dialog>
<q-card-section
class="tipCardImage flex row justify-end"
:style="`background-image: url(${meeting.car.carImg})`"
>
<router-link
:to="`/user/meet/edit/${meeting.did}`"
style="text-decoration: none"
>
<q-icon
#click="fetchMeeting(meeting.did)"
name="fa-solid fa-pencil editNameIcon q-mb-sm q-ml-sm"
></q-icon>
</router-link>
<q-icon
name="fa-solid fa-trash editNameIcon q-mb-sm q-ml-sm"
#click="triggerDelete(meeting.did)"
></q-icon>
</q-card-section>
<q-card-section>
<div class="cardTitle">
<span>Encuentro Con</span> {{ truncateString(meeting.name, 30) }}
</div>
<div class="tipCardText">
<span>Agendado para el </span>
<p>{{ truncateString(meeting.date, 120) }}</p>
</div>
<div class="flex row justify-end">
<q-btn
#click="mapDialog = true"
class="text-white cardButton"
:class="{ cardButtonMobile: $q.screen.lt.md }"
>Ver UbicaciĆ³n</q-btn
>
</div>
</q-card-section>
</q-card>
</div>
And here is the code for the MeetMapComponent:
<template>
<div class="meetMapContainer">
<div ref="mapDiv" style="width: 100%; height: 500px" />
<h5 class="text-center text-white">{{ props.mapData.address }}</h5>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useAuthStore } from "stores/auth";
import { storeToRefs } from "pinia";
import { Loader } from "#googlemaps/js-api-loader";
const props = defineProps({
mapData: Object,
buyerName: String,
});
const GOOGLE_MAPS_API_KEY = "...";
const loader = new Loader({ apiKey: GOOGLE_MAPS_API_KEY });
const mapDiv = ref(null);
async function mapRender() {
await loader.load();
const map = new google.maps.Map(mapDiv.value, {
mapTypeId: "roadmap",
center: props.mapData.coordinates,
zoom: 13,
});
console.log(map);
new google.maps.Marker({
position: props.mapData.coordinates,
map,
title: `Encuentro con ${props.buyerName}`,
});
}
mapRender();
</script>
I will help you as much as I understand. You use the mapDialog variable to open the dialogue. But even if this variable is used in v-for, its reference does not change. For this reason, when you want to open a modal, all modals may be opened and the last one may appear because it is the last one opened. Please check the dom.
I think this method can solve the problem.
in script
const meetings = [
{
did: 'some value',
address: 'some address',
name: 'some name',
// add modal flag
showMapModal: false
}
]
template
<div
v-for="meeting in meetings"
:key="meeting.did"
class="col-12 col-md-6 col-lg-3 q-pa-md q-mx-xl"
>
<q-card class="my-card homeCard q-pa-md">
<q-dialog class="mapDialog flex column" v-model="meeting.showMapModal">
<MeetMapComponent
:key="componentKey"
:mapDiv="mapDiv"
:mapData="meeting.address"
:buyerName="meeting.name"
/>
</q-dialog>
</q-card>
</div>

How to pass props to sibling and child component in vue

The structure of my code is like this:
So in the Product component, I am making an API call:
<template>
<button class="btn button col-2" #click="addToCart()">
Add to cart
</button>
</template>
<script>
methods:{
addToCart: function () {
let amount = this.itemsCount !== "" ? this.itemsCount : 1;
if(this.variationId != null) {
this.warningMessage = false;
cartHelper.addToCart(this.product.id, this.variationId, amount, (response) => {
this.cartItems = response.data.attributes.items;
});
} else {
this.warningMessage = true;
}
console.log(this.cartItems)
},
}
</script>
And what I am trying to do is the response (this.cartItems) should be shown in Cart component. And my Navbar component:
<template>
<nav class="navbar navbar-expand-lg shadow">
<div class="container navbar-container">
<div class="navbar navbar-profile">
<div class="dropdown">
<button class="btn dropdown-toggle" type="button" id="dropdownCart" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-cart-arrow-down"></i>
<span></span>
</button>
<div #click="$event.stopPropagation()">
<CartBox :cartItems="cartItems"/>
</div>
</div>
</div>
</div>
</nav>
</template>
<script>
export default {
props: {
cartItems:Object
},
components: {CartBox},
}
And CartBox:
<template>
<Cart/>
</template>
<script>
import Cart from '../components/Cart'
export default {
components: {
Cart
}
}
</script>
And my Cart component:
<template>
<div
class="dropdown-menu cart"
aria-labelledby="triggerId"
>
<div class="inner-cart">
<div>
<div class="cart-items">
<div>
<a class="remove">Remove</a>
</div>
</div>
</div>
<hr/>
<div class="cart-items-total">
<span>Total:</span>
Clear Cart
</div>
<hr/>
<router-link :to="{name: 'order'}" class="btn button-secondary">Go To Cart</router-link>
</div>
</div>
</template>
<script>
export default {
computed: {
},
methods: {
}
};
</script>
I am really confused how to pass the props to sibling component and then the child component but if you could pass it to Cart component, that would really help me.
There are two approaches for your request:
1. Using props, provide and inject
This could be accomplished with Provide / inject, after passing your response to a parent. Basically, you will emit your response from your Product component to a parent, maybe like your App.vue as the prop myData, then you provide it for every child, no matter where it is nested, like this:
provide: {
providedData: this.myData
}
In any child you can now use:
inject: ['providedData']
Please note, that this data will only be available if your Product component received it. The second approach is recommended.
2. Using a store
Using a store like vuex is a bit more complex than approach 1, but it will save a lot of time in the future. You would recieve your response in your Product component, dispatch it to the store and could call the state of information from this store anywhere in your app. See further information in this documentation: Vuex | Getting Started

How to render the content of an object in vue?

apologies if that's too basic, but I'm stuck.
I have created an object in vue with three properties (slug, title and content). I successfully console.logged the object. How can I now use the object in my page in order to render its content?
There is no need for me to loop through the object, at it has only one item in it.
<template>
<div class="relative py-16 overflow-hidden bg-white">
<div class="relative px-4 sm:px-6 lg:px-8">
<div class="mx-auto text-lg max-w-prose">
<h1>
<span
class="block text-base font-semibold tracking-wide text-center text-indigo-600 uppercase"
>Hello</span
>
<span
class="block mt-2 text-3xl font-extrabold leading-8 tracking-tight text-center text-gray-900 sm:text-4xl"
>Here is the name</span
>
</h1>
<p class="mt-8 text-xl leading-8 text-gray-700"></p>
<div
v-bind="this.data.content"
class="text-lg font-medium leading-6 text-gray-900"
></div>
</div>
</div>
</div>
</template>
<script>
const Cosmic = require("cosmicjs");
const api = Cosmic();
const bucket = api.bucket({
slug: "((BUCKETNAME))",
read_key: "((KEY))",
});
const data = bucket
.getObject({
id: "((BUCKET ID))", // Object ID
props: "slug,title,content", // get only what you need
})
.then((data) => {
const about = data.objects;
console.log(data);
});
export default {
name: "data",
data() {
return {
data,
};
},
};
</script>
As other commenters have suggested, it would be useful to read the Vue syntax guide here https://v2.vuejs.org/v2/guide/syntax.html
But to answer your question with the most minimal of code changes, you'd want to move your data request to the lifecycle hook of your vue component.
<template>
<h1>{{ dataObjects.title }}</h1>
<p>{{ dataObjects.slug }}</p>
<p>{{ dataObjects.content }}</p>
</template>
<script>
export default {
name: "data",
data() {
return {
dataObjects: null,
};
},
mounted() {
bucket.getObject({
id: "((BUCKET ID))", // Object ID
props: "slug,title,content", // get only what you need
})
.then((data) => {
// Assign the return value to the dataObjects propery of the vue instance.
this.dataObjects = data;
});
}
};
</script>
In the template section, you can see that I've used curly braces to render the contents of dataObjects (I wasn't sure what structure your data is in).
You can also learn from examples on the Vue Cookbook site

VueJS: dynamic component loses it`s props after reloading

I have a django rest_framework as a server and vuejs for a client side. There is an index page like '/' where i`m using v-for to iterate an array of objects to show some item's cards. Each card has a button to the '/info'(which is dynamically changed with /:slug in a router) like so:
...
<RouterLink :to="{name: 'contentItemInfo', params: {item: item, slug: item.slug} }">
<button class="card-button btn fade-in">
...
</button>
</RouterLink>
...
Then, when we press the button, we are going to '/:slug' and passing the item param to props, using it like so:
...
<video class="video" type="video/mp4" autoplay loop :src="item.trailer"/>
<h3 class="video-info-h3 px-5 py-1 rounded">{{ item.title }}</h3>
<h5 class="video-info-h5 px-5 py-1 rounded">{{ item.subtitle }}</h5>
<p class="video-info-p px-5 py-1 text-wrap rounded">{{ item.description }}</p>
...
export default {
name: "ContentItemInfo",
components: {Footer, Navbar},
props: [
'item'
],
}
Works for now, but when i reload the page, all the content disappeare and got undefined of all item`s values
How should i make it work after reloading?
Passing complex objects by route param only works in-memory. Once you reload the page, the only data you have is strings from the URL. You'll have to load the item data from your backend for fresh page loads.
Try something like this, making the item prop optional and loading the data if it's not set
export default {
name: "ContentItemInfo",
components: {Footer, Navbar},
props: {
item: {
type: Object,
default: null
},
slug: String // set from the route param
}
data: vm => ({
video: vm.item // initialise a local copy so you can update it if required
}),
async created () {
if (this.video === null) {
// just guessing with this bit
const { data } = await axios.get(`/api/video-for-slug/${encodeURIComponent(this.slug)}`)
this.video = data
}
}
}
and your template
<div v-if="video">
<video
class="video"
type="video/mp4"
autoplay
loop
:src="video.trailer"
/>
<h3 class="video-info-h3 px-5 py-1 rounded">{{ video.title }}</h3>
<h5 class="video-info-h5 px-5 py-1 rounded">{{ video.subtitle }}</h5>
<p class="video-info-p px-5 py-1 text-wrap rounded">{{ video.description }}</p>
</div>
<p v-else>Loading...</p>
In the above, I assumed that your routes are passing params as props. If not, see https://router.vuejs.org/guide/essentials/passing-props.html

Update component data from the template

Not too sure what is wrong here, it seems fine to me! I'm simply trying to update the data property display to true when I click the input within my component.
I have passed the data to the slot scope, so can't see that being the issue. It just simply won't update, using a function to toggle it works, however not what I really want to do, seems pointless.
<time-select>
<div slot-scope="{ time }" class="is-inline-block">
<label for="businessHoursTimeFrom"><i class="fas fa-clock"></i> From</label>
<input type="text" name="businessHoursTimeFrom[]" v-model="time" v-on:click="display = true">
</div>
</time-select>
The code behind:
<template>
<div>
<p>{{ display }}</p>
<slot :time="time" :display="display"></slot>
<div class="picker" v-if="display">
<p>Test</p>
</div>
</div>
</template>
<script>
export default {
props: [],
data: function () {
return {
time: '',
display: false
}
},
mounted() {
},
methods: {
}
}
</script>