How can I call ajax before display datepicker in vuetify? - vue.js

Look at this : https://codepen.io/positivethinking639/pen/mddejJN
My script of vue like this :
data: () => ({
modalTest: false,
dateTest: null,
time: null,
allowedTimes: ['8:30 am','9:00am','10:30am','1:30pm','3:30 pm']
}),
methods: {
saveData() {
this.$refs.dialogTest.save(this.dateTest)
},
allowedDates: val => parseInt(val.split('-')[2], 10) % 2 === 0,
setTime(time) {
this.time = time
}
I want before call datepicker, I call ajax first
How can I do it?

#Max proposal is not fully answers the question.
Let's add new data property which will trigger the show of calendar component:
isAjaxCompl: false,
Move the button out of template to directly change dialog v-model:
<v-btn color="success" #click="openDialog()">call date</v-btn>
Make the function which will be fired on dialog open:
openDialog() {
this.modalTest = true;
axios.get('https://reqres.in/api/users?delay=1').then((response) => {
this.isAjaxCompl = true;
})
},
Finally, add v-if which will show calendar component only when axios get the response:
<v-date-picker v-if="isAjaxCompl" v-model="dateTest" scrollable :allowed-dates="allowedDates">
Link to the corresponding CodePen:
https://codepen.io/RobbyFront/pen/RwwWewM

You need to move the dialog button outside and add a #click method for showing the dialog
Your Code
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">call date</v-btn>
</template>
New Code
Html
<v-btn color="success" dark #click="showDate">call date</v-btn>
Code
showDate(){
console.log("Ajax calling");
this.modalTest = true;
}
Here the pen

Related

Vuetify: How to add aria-labelledby to v-dialog?

I am trying to use aria-labelledby attribute on a Vuetify dialog, like this:
<v-dialog v-model="show" max-width="600px" aria-labelledby="testDialogTitle">
<span id="testDialogTitle">Test Dialog</span>
</v-dialog>
But the attribute is not added to the element with role="dialog", it is added to the element which references it from my main template. How can I add this attribute to my dialog with role="dialog"?
Thanks
Thanks to the hints in the comments, I was able to achieve this by doing:
<v-dialog v-model="show" max-width="600px" ref="dialog" aria-labelledby="testDialogTitle">
<span id="testDialogTitle">Test Dialog</span>
</v-dialog>
watch: {
show: {
immediate: true,
handler: function (newValue) {
if (newValue) {
this.$nextTick(() => {
const dialog = this.$refs?.dialog;
const content = dialog?.$refs?.content;
if (content) {
// Copy aria-labelledby from v-dialog to new rendered element.
content.setAttribute('aria-labelledby', dialog.$attrs['aria-labelledby']);
}
});
}
}
}
}

Vuetify: v-dialog won't close although v-model variable is set to false

I have a couple of dialogs which are created with v-for (exchangeTypeAbbreviation and exchangeType come from there). When I click on the activator button, the dialog opens and the value in the object I use for storing the dialogs' state is updated to "true".
But when I click the cancel or save button, the dialog won't close, although the object's value is updated to "false".
<v-list-item>
<v-dialog
max-width="400"
v-model="dialogs[exchangeTypeAbbreviation]"
>
<template v-slot:activator="{ on }">
<v-list-item v-on="on">
<v-icon class="pr-4">
mdi-plus
</v-icon>
Add Product Flow
</v-list-item>
</template>
<v-card>
<v-card-title>Add Product Flow</v-card-title>
<v-card-subtitle
v-text="exchangeType"
></v-card-subtitle>
<v-card-actions>
<v-btn
#click="
dialogs[exchangeTypeAbbreviation] = false;
createUnitProcessExchange(
exchangeTypeAbbreviation
);
"
>Save</v-btn
>
<v-btn
#click="dialogs[exchangeTypeAbbreviation] = false"
>Cancel</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
</v-list-item>
<script>
export default {
name: 'Activities',
data: () => ({
dialogs: {},
exchangeTypes: {},
unitProcessExchangesOptions: null,
}
}),
mounted() {
Promise.all([
this.loadUnitProcessExchangeOptions()
])
},
methods: {
async loadUnitProcessExchangeOptions() {
return this.$api
.options('/unitprocessexchanges/', {
headers: {
Authorization: 'Token ' + localStorage.getItem('token')
}
})
.then(response => {
this.unitProcessExchangesOptions = response.data.actions.POST
for (const exchangeType of this.unitProcessExchangesOptions
.exchange_type.choices) {
this.exchangeTypes[exchangeType.value] = exchangeType.display_name
this.dialogs[exchangeType.value] = false
}
})
},
async createUnitProcessExchange(exchangeTypeAbbreviation) {
this.newUnitProcessExchange.activity = this.activities[
this.selectedActivity
].url
this.newUnitProcessExchange.exchange_type = exchangeTypeAbbreviation
this.dialogs[exchangeTypeAbbreviation] = false
// eslint-disable-next-line no-debugger
debugger
}
}
}
</script>
I was able to figure out why it doesn't work. Due to limitations in JavaScript, Vue.js has some difficulties to observe changes in Objects and Arrays. This is documented here.
In my case, I added nested variables inside my "dialogs" variable by assigning them directly, e.g. like this
this.dialogs[index] = false
However, this creates a sub-element which can't be tracked by Vue.js. To make sure that changes on this element can be tracked, it either has to be pre-defined from the beginning or needs to be set by using the Vue.$set command. Always using the following command, solved the issue for me:
this.$set(dialogs, index, false)
I think the first problem is you are trying to change the object with an array notation i.e array[0] but it should be a dot notation with object property, in your case it would be dialogs.exchangeTypeAbbreviation = false.
With that one more problem would be that property doesn't exist so in
data: () => ({
dialogs: {exchangeTypeAbbreviation:Boolean},
exchangeTypes: {},
unitProcessExchangesOptions: null,
}
}),
with this now you can set the value of exchangeTypeAbbreviation.

how to detect change of actual value not just OnChange nuxt vuetify

As a result of
export default {
name: "Details",
async asyncData({ redirect, params, store }) {
if (
!store
I am returning a few values in which one of them is
return {
camera: c,
thumbnail_url: thumbnail_url,
camera, and then in my form fields where I am populating a Vuetify dialog, Text Field inputs
such as
<v-dialog v-model="dialog" max-width="600px">
<v-card>
<v-card-text>
<v-layout class="model-container">
<v-row>
<v-col cols="12" lg="7" md="7" sm="12" xs="12">
<v-text-field
v-model="camera.name"
class="caption bottom-padding"
required
>
<template v-slot:label>
<div class="caption">
Name
</div>
</template>
</v-text-field>
my issue is, I have a button as
<v-btn color="primary" text #click="updateCamera">
Save
</v-btn>
which I only want to make disable false, only if there is an actual change occurs to, this.camera, in updateCamera method, I can use the updated values as
async updateCamera() {
let payload = {
name: this.camera.name,
but I want to enable or disable the button on when change occurs,
I had tried #input, I have also tried to watch camera object
<v-text-field
v-model="camera.name"
class="caption bottom-padding"
required
#input="up($event, camera)"
>
This way I tried to get some info about event, such as which text field it is, so I can compare, but in up method it only passes input value.
in watch
camera: function() {
this.$nextTick(() => {
console.log(this.camera)
})
}
camera: {
handler: function(val) {
this.$nextTick(() => {
console.log(val)
})
/* ... */
},
immediate: true
}
I have tried this but nothing worked.
Of course, we can enable or disable a button on change but not just if the user places an A and then deletes it, not such change.
Any help would be wonderful
Update:
Even after using this
camera: {
handler: function(newValue) {
if (newValue === this.dumpyCamera) {
console.log(this.dumpyCamera)
console.log(newValue)
console.log("here")
this.updateButton = true
} else {
this.updateButton = false
}
},
deep: true
}
both new and old values are the same.
I have tried to add new variable dumyCamera and on mount I have assigned this.camera value to this.dumyCamera but when something changes in camera, it changes this.dumyCamera as well? why is this the case?
You should be able to recognize any changes made to this.camera by using a watcher
watch: {
camera: {
handler (newValue, oldValue) {
// do something here because your this.camera changed
},
deep: true
}
}

Dynamically update class based on individual form elements validation in Vuetify

Take a look at the official Vuetify form validation example.
The very first example, if you click in a field and then outside it, it is automatically validated. The entire field becomes red and you get a hint in red text.
What I would like is based on that built-in/native validation to add or remove a class (that turns the text red) on a completely separate HTML element.
It would be ideal if something like hint-for="" exists. Some way to connect a separate HTML element with the form field validation.
I have tried to condition the class with the "valid" property of the form element, something like this: this.$refs.form.$children[1].valid but this doesn't exist on page load and throws errors.
Right now I have some results by basically having double validation, the normal one that validates automatically based on the "rules" property on the form field, and a custom one that I call my self on #input and on #blur of the form field, but this is largely inefficient so I'm hoping there's a better way.
You can use the value of the v-form to track the validity of your form. In order to listen to changes you can use the input event like this
<template>
<div>
<v-form lazy-validation v-model="valid" #input="updateOtherElement">
<v-text-field
v-model="email"
:rules="emailRules"
label="Email"
required
></v-text-field>
</v-form>
</div>
</template>
<script>
export default {
data () {
return {
valid: true,
email: "",
emailRules: [
v => /.+#.+/.test(v) || 'E-mail must be valid',
],
}
},
methods: {
updateOtherElement(valid) {
// update other elements css
}
}
}
</script>
An alternative would be to track the changes with a watcher
This is what I came up with.
I had some trouble with validation being active immediately and nonexistent text fields on page load but with this setup, it works.
So once validation kicks in the fields will turn red by the native Vuetify validation if they are not valid, and I toggle the "invalid" class on a completely separate piece of HTML with custom functions. What is important here that each text field has it's own "subheader" which will turn red only if that single connected text-filed is invalid, not the entire form.
<template>
<v-form
ref="form"
lazy-validation
>
<v-subheader v-bind:class="passwordValid()">
Password *
</v-subheader>
<v-text-field
:rules="rules.password"
ref="password"
></v-text-field>
<v-subheader v-bind:class="passwordAgainValid()">
Password Again *
</v-subheader>
<v-text-field
:rules="rules.passwordAgain"
ref="passwordAgain"
></v-text-field>
</v-form>
<v-btn
v-on:click="save"
>
Save
</v-btn>
</template>
<script>
export default {
methods: {
save() {
let self = this
self.activateRules()
self.$nextTick(function () {
if (self.$refs.form.validate()) {
self.rules = {}
// submit...
}
})
},
activateRules () {
this.rules = {
password: [
v => v.length > 0 || ''
],
passwordAgain: [
v => v.length > 0 || ''
]
}
},
passwordValid: function () {
let passwordValid = true
if (this.$refs.password) {
passwordValid = this.$refs.password.valid
}
return {
'error--text': !passwordValid
}
},
passwordAgainValid: function () {
let passwordAgainValid = true
if (this.$refs.passwordAgain) {
passwordAgainValid = this.$refs.passwordAgain.valid
}
return {
'error--text': !passwordAgainValid
}
}
}
}
</script>

How to Create Component on the fly?

First of all here is my structure
CHILD COMPONENT
// HTML
<v-select
v-bind:items="selectItems"
v-model="selectedItemModel"
label="Category"
item-value="text"
></v-select>
<v-text-field
label="Enter Value"
type="number"
v-model="compValModel"
></v-text-field>
// REMOVE BUTTON for deleting this component in render.
<v-btn icon>
<v-icon>cancel</v-icon>
</v-btn>
// JS
props: {
selectItems: {
type: Array,
required: true
},
selectedItem: {
type: String
},
compVal: {
type: Number
},
}
data () {
return {
selectedItemModel: this.selectedItem,
compValModel: this.compVal
}
},
watch: {
selectedItemModel(value) {
this.$emit('selectedItemInput', value);
},
compValModel(value) {
this.$emit('compValInput', value);
}
}
PARENT COMPONENT
// HTML
<component
:selecItems="selectItems"
:selectedItem="selectOneItem"
:compVal="compOneVal"
#selectedItemInput="selectOneItem = $event"
#compValInput="compOneVal = $event"
></component>
// ADD BUTTON for adding the above component more.
<v-btn icon>
<v-icon>cancel</v-icon>
</v-btn>
My Case
When i click on that plus button, it should create new component. and when i click on that REMOVE button from that component, it should delete that component.
Right now i have followed this. The problem in this approach is, whenever a new component is created. the value of the exsisting dynamically created values got refereshed.
My Question
Whats the good way to duplicate the component dynamically?
Since we are going to create components dynamically, how to create data values also dynamically?