Buefy Timepicker watching on change - vue.js

Good morning.
I have a problem with the buefy timepicker.
It works if I use the "clear" or "Now" buttons but not on selecting time. This seems to have started with a recent buefy update.
https://codepen.io/anon/pen/JqBKGE?&editable=true&editors=101
<div id="app" class="container">
<textarea style="width: 100%;" readonly>{{ time }}</textarea>
<b-field label="Select time">
<b-timepicker v-model="time"
inline
placeholder="Click to select...">
<button class="button is-primary"
#click="time = new Date()">
<b-icon icon="clock"></b-icon>
<span>Now</span>
</button>
<button class="button is-danger"
#click="time = null">
<b-icon icon="close"></b-icon>
<span>Clear</span>
</button>
</b-timepicker>
</b-field>
</div>
JS
const example = {
data() {
return {
time: new Date()
}
},
watch: {
time: {
handler: function() {
console.log(this.time);
}
}
}
}
const app = new Vue(example)
app.$mount('#app')
Any suggestions?

Seems to be an issue with the buefy Timepicker
https://github.com/buefy/buefy/issues/1418
Edit: This was fixed with Buefy release 0.7.7

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 Component interact with page where its in

I'm using 2 components in my page, one for a comment and inside the comment component is the comment reply component, I did it this way to add some functionality that I needed but now I don't know how to get the buttons in the components to do the stuff they did before I turned them into components. For example the edit function opens a dialog that is in the page where the comment component is located but now it doesn't do anything, same with the reply functionality.
How can I get this to work, or is there a way to keep those methods in the main page and not have to put them in the components? I've never made my own component before so I'm not sure how to do this.
This is the Comment component code, the editComment and openReply are methods from the main page
<template>
<div class="comment">
<el-card shadow="never" v-if="comment.parent_id === null">
<el-row v-if="comment.deleted === 0">
<el-row style="margin-bottom: 15px">
<el-col :span="20" v-if="forum.anonymous === 0">
<p class="comment-user-name">{{comment.user.name}} dice: </p>
</el-col>
<el-col :span="20" v-if="forum.anonymous === 1">
<p class="comment-user-name">Anónimo dice: </p>
</el-col>
<el-col :span="4">
<div style="text-align: end"
v-if="comment.user_id === $page.auth.user.auth.user.id">
<div v-if="canEdit" class="btn-link-edit action-button"
#click="editComment(comment)">
<i class="fas fa-pencil-alt"></i>
</div>
<div class="btn-link-delete action-button"
#click="remove(comment.id)">
<i class="fas fa-trash"></i>
</div>
<div class="btn-link-preview action-button"
#click="openReply(comment)">
<i class="fas fa-reply"></i>
</div>
</div>
<div style="text-align: end"
v-if="comment.user_id !== $page.auth.user.auth.user.id">
<div class="btn-link-preview action-button"
#click="openReply(comment)">
<i class="fas fa-reply"></i>
</div>
</div>
</el-col>
</el-row>
<el-row style="margin-top: 10px">
<p class="comment-comment">{{comment.comment}}</p>
</el-row>
<el-row class="fa-pull-right pb-1">
<p class="comment-user-time ">{{formatDate(comment.comment_time)}}</p>
</el-row>
</el-row>
<el-row v-if="comment.deleted === 1">
<el-row style="margin-top: 15px">
<p class="comment-comment">{{comment.comment}}</p>
</el-row>
<el-row class="fa-pull-right pb-1">
<p class="comment-user-time ">{{formatDate(comment.comment_time)}}</p>
</el-row>
</el-row>
<el-row style="margin-left: 30px">
<div v-for="reply in comment.replies">
<ReplyComponent :reply="reply" :forum="forum"/>
</div>
</el-row>
</el-card>
</div>
</template>
<script>
import moment from "moment";
import ReplyComponent from "./ReplyComponent";
export default {
name: "Comment",
props: {
comment: Object,
forum: Object,
},
components: {
ReplyComponent
},
data() {
return {
canEdit: Boolean,
interval: null,
mode: '',
form: {
comment: '',
},
};
},
methods: {
openReply(row) {
this.dialogReplyVisible = true;
this.parent = row;
},
editComment(item) {
this.mode = 'Editar';
this.form = _.cloneDeep(item);
this.dialogFormVisible = true;
},
checkTime() {
var minutes = moment().diff(moment(this.comment.comment_time), 'minutes');
if (minutes >= 30) {
this.canEdit = false;
// here you could also already clear the interval, since it won't change
} else if (minutes <= 29) {
this.canEdit = true;
}
}
},
created() {
this.checkTime();
this.interval = setInterval(() => {
this.checkTime();
}, 10000);
},
beforeDestroy() {
clearInterval(this.interval);
}
};
</script>
If I understood your questions correctly, you want to communicate (pass data) from Child components to parent component (page).
The way we can achieve this is by $emitting events from Child components and have the parent component respond to these events.
So, in your editComment and openReply methods you would fire events like this:
openReply(row) {
this.$emit('openReply', {any payload that you would want pass to parent});
},
editComment(item) {
this.$emit('editComment', {any payload that you would want pass to parent}
},
And, in your parent component / page you would subscribe and handle those events. Pseudo-code below:
<Comment v-on:openReply="handleOpenReply"
v-on:editCommnet="handleEditComment"/>
Further Reading:
Passing Data to Child Components With Props
Listening to Child Components Events

How can I access text inside an input within a template?

My objective is to get text from an input that's in a template. Not sure how to go about retrieving this. I'm using Vue; Note must be available in Vue.js, no external sources
The Template:
<template id="addmodal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
Enter Course Information:
</slot>
</div>
<div class="modal-body">
<slot name="body">
Course Name
<input type="text" ref="coursename" placeholder="Numbers Don't Lie 101">
Course Grade
<input type="text" ref ="coursemark" placeholder="100">
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button" #click="confirmCourse">
Submit New Course
</button>
<button class="modal-cancel-button" #click="cancelCourse">
Cancel
</button>
</slot>
</div>
</div>
</div>
</div>
</template>
I need to access coursename and coursemark. This can be done fairly easily when not inside a template. As it is right now the code executes stating .value is undefined.
var app = new Vue({
el: "#app",
data: {
courses: [],
confirmModal: false,
confirmAdd: false,
selectedCourse: null
},
methods: {
addCourse2: function addCourse2() {
this.confirmAdd = false;
var course = this.$refs.coursename.value;
var mark = this.$refs.coursemark.value;
if (course) {
this.courses.push(new Course(course, mark));
this.$refs.newcourse.value = "";
this.$refs.newmark.value = "";
}
}
}
});
EDIT:
Forgot to add the component section
Vue.component("add-modal", {
template: "#addmodal",
props: ["open", "course", "mark"],
methods: {
confirmCourse: function confirmCourse() {
alert(this.$refs.coursename.value);
this.$emit("confirm");// GET
},
cancelCourse: function cancelCourse() {
this.$emit("cancel");
}
}
});
Forgive me in advance, I feel this is something rather easy I'm missing as a beginner
use v-model. or if it is in other component. you can use $emit

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>

How can I get value in datetimepicker bootstrap on vue component?

My view blade, you can see this below :
...
<div class="panel-body">
<order-view v-cloak>
<input slot="from-date" data-date-format="DD-MM-YYYY" title="DD-MM-YYYY" type="text" class="form-control" placeholder="Date" name="from_date" id="datetimepicker" required>
<input slot="to-date" data-date-format="DD-MM-YYYY" title="DD-MM-YYYY" type="text" class="form-control" placeholder="Date" name="to_date" id="datetimepicker" required>
</order-view>
</div>
...
My order-view component, you can see this below :
<template>
<div>
<div class="col-sm-2">
<div class="form-group">
<slot name="from-date" required v-model="fromDate"></slot>
</div>
</div>
<div class="col-sm-1">
<div class="form-group" style="text-align: center">
-
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<slot name="to-date" required v-model="toDate"></slot>
</div>
</div>
<div class="col-sm-4">
<button v-on:click="filter()" class="btn btn-default" type="button">
<span class="glyphicon glyphicon-search"></span>
</button>
</div>
</div>
</template>
<script>
export default {
data() {
return{
fromDate: '',
toDate: ''
}
},
methods: {
filter: function() {
console.log(this.fromDate)
console.log(this.toDate)
}
}
}
</script>
I using v-model like above code
But, when I click the button, the result of
console.log(this.fromDate)
console.log(this.toDate)
is empty
It display empty
Why it does not work?
How can I solve it?
You cannot bind a slot using v-model and expect that Vue will attach that automatically to your slot input, but I can't see any reason why you need to use a slot here anyway. It looks like you just want an input that you can attach custom attributes to and you can do that by passing the attributes as a prop and use v-bind to bind them:
<template>
<div>
<input v-bind="attrs" v-model="fromDate" />
<button #click="filter">filter</button>
</div>
</template>
export default{
props: ['attrs'],
methods: {
filter() {
console.log(this.fromDate)
}
},
data() {
return {
fromDate: ""
}
}
}
new Vue({
el: "#app",
data: {
fromDateAttrs: {
'data-date-format': "DD-MM-YYYY",
title: "DD-MM-YYYY",
type: "text",
class: "form-control",
placeholder: "Date",
name: "from_date",
id: "datetimepicker",
}
}
});
Now you can just pass your attrs as a prop in the parent:
<my-comp :attrs="fromDateAttrs"></my-comp>
Here's the JSFiddle: https://jsfiddle.net/rvederzc/
EDIT
In reference as to how to create a date picker component, here's how I would implement a jQuery datepicker using Vue.js:
<template id="date-picker">
<div>
<input v-bind="attrs" v-model="date" #input="$emit('input', $event.target.value)" v-date-picker/>
</div>
</template>
<script type="text/javascript">
export default {
props: ['attrs'],
directives: {
datePicker: {
bind(el, binding, vnode) {
$(el).datepicker({
onSelect: function(val) {
// directive talk for 'this.$emit'
vnode.context.$emit('input', val);
}
});
}
}
}
}
</script>
You can then bind that with v-model in the parent:
<date-picker v-model="myDate"></date-picker>
Here's the JSFiddle: https://jsfiddle.net/g64drpg6/
Cant expect any javascript technology to be complete before it becomes famous. Going by that, I tried all the recommendations from using moment to vue-datapicker. All recommendations heavily broke design and needed hardcode of the div id's in the vue initialisation under mounted. Cant introduce hacks into my project this way. Messes up design and implementation neatness.
I fixed it using plain old jsp. On Save, I just did this
vuedata.dateOfBirthMilliSecs = $("#dateOfBirth").val() ;
I'll figure out conversion of date format to milliseconds in my java controller.