Vue2Leaflet : how to display the tooltip of a specific marker - vue.js

I'm using nuxt-leaflet (with Vue2Leaflet) and I'm wondering how to display the tooltip of a specific marker after cliquing on a button ("Display tooltip") in my template vue file ?
<template>
<div>
<button #click="displayTooltipOfMarker(x)">Display tooltip</button>
<div id="map-wrap" style="height: 500px; width:100%">
<no-ssr>
<l-map :zoom="10" :center="positionInitiale">
<l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"></l-tile-layer>
<l-marker :icon="customIcon" :lat-lng="positionMarqueurHome"></l-marker>
<l-marker
v-for="marker in marqueurs"
:key="marker.id"
:lat-lng.sync="marker.position"
#click="alert(marker)"
>
<l-popup :content="marker.tooltip"/>
<l-tooltip :content="marker.tooltip"/>
</l-marker>
</l-map>
</no-ssr>
</div>
</div>
</template>
Is it possible ?

To open/close Tooltip on external event (like button is your case) the folowing solution could be considered:
get access to Leaflet marker object via $refs attribute:
<l-marker
v-for="(marker, index) in markers"
:key="index"
ref="markersRef"
:lat-lng="marker.position"
>
<l-popup :content="marker.name"/>
</l-marker>
and save it into array:
mounted: function() {
this.$nextTick(() => {
this.markerObjects = this.$refs.markersRef.map(ref => ref.mapObject);
});
}
Once the external event is triggered (e.g. button click) the tooltip is getting displayed like this:
<button #click="displayTooltip(1)">Display</button>
displayTooltip(selectedIndex) {
this.markerObjects[selectedIndex].openTooltip();
}
Here is a demo for your reference

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>

Vue dynamic ref show dropdown

I am using VueJS and want to use right click event to display the dropdown list. But I have multiple dropdown so need to use dynamic ref. How can I show the dropdown that I want when right click?
<div class="info" #contextmenu="handler($event)">
...
<b-dropdown size="sm" text="…" variant="transparent" no-caret :ref="`dropdown-${id}`" :item1="this.item1" :item2="this.item2">
<b-dropdown-item #click="showDetails(item1, item2)">Send</b-dropdown-item>
</b-dropdown>
</div>
handler(e) {
this.$ref.dropdown.show();
e.preventDefault();
}
You can send another parameter to the handler function that will be the value of the $ref you want to open:
<div class="info" #contextmenu="handler($event, id)">
...
<b-dropdown size="sm" text="…" variant="transparent" no-caret :ref="`dropdown-${id}`" :item1="this.item1" :item2="this.item2">
<b-dropdown-item #click="showDetails(item1, item2)">Send</b-dropdown-item>
</b-dropdown>
</div>
handler(e, id) {
this.$ref[`dropdown-${id}`].show();
e.preventDefault();
}
By the way you can use .prevent modifier instead of using the e.preventDefault:
<div class="info" #contextmenu.prevent="handler(id)">
// your methods
handler(id) {
this.$ref[`dropdown-${id}`].show();
}
You could use e.target to get the element (button) you just clicked and after that use a switch statement to determine which dropdown to show.

VueJS - How to Enable/Disable a specific input in a v-for loop

I need to enable/disable specific inputs on button click.
My problem here is when I click my button, ALL the inputs enable/disable I am having a hard time targeting a single one.
I use props “articles” which returns the “id”, “title” and “body” of my articles.
<div v-for="(article, index) in articles" v-bind:key="article.id">
<div class="flex">
<div class="flex-1 px-2 py-2">
<input
v-model="article.title"
type="text"
:disabled="isDiabled"
class="w-full disabled:opacity-75 bg-gray-300 focus:bg-white"
/>
</div>
<div class="flex-1 px-2 py-2">
<input
v-model="article.body"
type="text"
:disabled="isDiabled"
class="w-full disabled:opacity-75 bg-gray-300 focus:bg-white"
/>
</div>
<div class="px-2 py-2">
<button
class="px-2 py-2 border-gray-400 bg-gray-800 rounded text-white hover:border-gray-300 items-end"
#click="enableEdit"
>
Edit
</button>
</div>
</div>
</div>
<script>
export default {
props: ["articles"],
data() {
return {
isDiabled: true,
};
},
methods: {
enableEdit() {
this.isDiabled = false;
},
},
};
</script>
Every article has his own button and I want only the two inputs of the button that is clicked to be enabled/disabled and not all of them.
As being pointed you need to add also that property to your articles... of course you don't need to do this in the database, still using it as a property you need also to emit this change of the flag to the parent.
You could also use a local version of the articles where you add also this isDisabled property, you fill this new variable at the creation of the component based of course on the property you received from the parent,
created(){
this.local_articles = this.articles.map(e => {
return {...e, isDisabled: true} }
)
},
this way you don't need to propagate nothing to the parent as you can handle all internally.
This fiddle gives a solution to your problem.
Make a boolean property for each article, then change the binding of disabled to that property.

Quasar Framework (Vue) : Collapsible and mouseleave

I'm trying to figure out a collapsible component inside the quasar.
For some reason, the mouse hover event does not work.
Please tell me how you can replace the text output area with a button when the user hovers the pointer over .
Please help! :)
<q-collapsible
opened
icon="bookmark"
:label="$t('Custom dashboards')"
#mouseleave="var2 = true" //It doesn't work :(
#mouseenter="var2 = false" //It doesn't work
>
<q-item>
<q-btn
#click="func()"
icon="add"
:label="$t('Create')"/>
</q-item>
</q-collapsible>
enter image description here
Complete HTML/Script/CSS would be expected to solve your issue. Since that is not available, I basically what you did, but expanded to solve what was supplied in the image.
#mouseleave and #mouseenter are working correctly. You just have to set and use the value to hide/show the button.
I do not believe QCollapsible support a slot in the header (correct be if I am wrong), so it might be better to use a QToolbar in conjunction with QCollapsible and QList.
// Update the variables to toggle the display
new Vue({
el: '#q-app',
data: function() {
return {
listOpen: true,
hover: false
}
},
methods: {
toggleList: function() {
this.listOpen = !this.listOpen
},
addItem: function() {
this.$q.notify('clicked')
}
},
computed: {
hovering() {
return this.listOpen && this.hover
}
}
})
// Hide the existing collapse button (maybe there is a property for this)
div.q-collapsible.q-item-division.relative-position.q-collapsible-cursor-pointer>div>div.q-item.q-item-division.relative-position {
display: none
}
<!-- Create a div with the hoverover events -->
<div style="width:500px" #mouseleave="hover = false" #mouseenter="hover = true">
<q-toolbar>
<div class="on-left">
<q-icon name="bookmark">
<q-icon>
</div>
<q-toolbar-title v-show="!hovering">
Custom Dashboard
</q-toolbar-title>
<q-btn v-show="hovering" icon="add" label="Create" class="full-width" #click="addItem"></q-btn>
<q-btn flat icon="menu" #click="toggleList"></q-btn>
</q-toolbar>
<q-collapsible opened v-model="listOpen">
<div slot="side">test</div>
<q-list>
<q-item>
<q-item-side left>
<q-icon name="dashboard">
<q-icon>
</q-item-side>
<q-item-main>
dash 2
</q-item-main>
</q-item>
<q-item>
<q-item-side left>
<q-icon name="dashboard">
<q-icon>
</q-item-side>
<q-item-main>
dash 5
</q-item-main>
</q-item>
</q-list>
</q-collapsible>
</div>
Codepen

iview ui how to clear files after successfully uploaded?

I am trying to clear the file lists as iview ui by default show file list. The problem is, user can upload file different times and ivew ui upload component keeps the old files in the list. I don't from where the list is coming. I saw there is a method clearFiles but not sure how to use it. There is no example in doc.
This is how I am using.
One thing, if I make :show-upload-list to false the list doesn't show but the progress bar also doesn't show. I want the progress bar to stay and list shouldn't show up.
<Upload
:multiple="false"
:show-upload-list="true"
:on-success="handleSuccess"
:format="['jpg','jpeg','png', 'pdf', 'docx', 'txt', 'mp4', 'mp3', 'zip']"
:max-size="21048"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
type="drag"
:action="isFileUpload.url"
:data="isFileUpload.meta"
>
<div style="padding: 20px 0">
<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
<p>Click or drag files here to upload</p>
</div>
Thank you.
You can add ref="upload" to vue component and clear file with this.$refs.upload.clearFiles()
<template>
<Upload
ref="upload"
:multiple="false"
:show-upload-list="true"
:on-success="handleSuccess"
:format="['jpg','jpeg','png', 'pdf', 'docx', 'txt', 'mp4', 'mp3', 'zip']"
:max-size="21048"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
type="drag"
:action="isFileUpload.url"
:data="isFileUpload.meta"
>
<div style="padding: 20px 0">
<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
<p>Click or drag files here to upload</p>
</div>
</template>
<script>
export default {
...
methods: {
handleSuccess () {
this.$refs.upload.clearFiles()
}
}
...
}
</script>