Multiple vuetify birthday pickers in a v-for range loop - vue.js

Vuetify has a birthday picker. However i need this birthday picker in a for loop. How do i get this to work, i just started with vue and vuetify and can not get my head a round the $refs in combination of an index concept. And i think thats what is needed here. The script is working with errors in the $refs. The idea for the picker is first to select a year, than a month and then the day
<template>
<v-container>
<div v-for="n in 3">
<v-menu
ref="menu"
v-model="menu[n]"
:close-on-content-click="false"
transition="scale-transition"
offset-y
min-width="290px">
<template v-slot:activator="{ on }">
<v-text-field
v-model="date[n]"
label="Birthday date"
readonly
v-on="on"
></v-text-field>
</template>
<v-date-picker
ref="picker"
v-model="date[n]"
:max="new Date().toISOString().substr(0, 10)"
min="1950-01-01"
#change="save"
></v-date-picker>
</v-menu>
</div>
</v-container>
</template>
<script>
export default {
data: () => ({
date: [],
menu: [],
}),
watch: {
menu (val) {
val && setTimeout(() => (this.$refs.picker.activePicker = 'YEAR'))
},
},
methods: {
save (date) {
this.$refs.menu.save(date)
},
},
}
</script>

I have the same need/issue with this component. I'm searching for the answer as well so I can only make suggestions based on my experience with this issue.
I've tried your same approach here but with little success. I'm currently creating a separate BirthDatePicker component and trying to use the v-model example from this article Using v-model on Nested Vue Components to get each birth-date modeled to it's corresponding value within my 'people' array of objects.
Rough example:
people: [{name: '', dob: '', etc...},{name: '', dob: '', etc...}]

Related

"this is null" when change in v-select [NuxtJs]

this is my first post! Hope you can help.
So I'm on some new project to migrate to nuxtJs.
I have some trouble with my v-select and v-model, here's the code:
_id.vue:
<template>
<v-container fluid white>
<v-card>
<v-card-title class="headline">
TEST:
</v-card-title>
<v-card-text>
<p>info :</p>
<p>{{this.host.typeS}}</p>
<v-select
v-model="this.host.typeS"
:items="this.fields.typeS"
item-value="value"
item-text="value"
dense outlined>
</v-select>
</v-card-text>
</v-card>
</v-container>
</template>
script
export default {
name: 'EditPage',
data() {
return {
tab: null,
fields: [],
host: {},
tabHeaders: ["tab1", "tab2", "tab3", "tab4", "tab5", "tab6"]
}
},
async fetch() {
this.fields = await fetch("APIfields")
.then(res => res.json());
if (this.$nuxt._route.params.id) {
this.host = await fetch("APIhost" + this.$nuxt._route.params.id).then(res => res.json());
} else {
this.host = {};
}
}
}
responses
APIfields:
{"typeS": [{"value": "p","order": 100}, {"value":"v","order": 100}],
"otherThings": [{"value":"se", "order": 100},{"value":"sa", "order": 100}]}
APIhost/id:
{"typeS": "p",
"otherThings": "se"}
When everything run, My select is initiated with the value "p" and when clicked I see all the values this field can have.
When I try selecting "v" in the v-select, I'm redirected to my error layout, and the console say "Type error: this is null".
Without the v-model, I don't have any error, but the v-select have no selected value.
The goal is to initialise a form based on an existing host, change the data inside the form and then submit it to the database. The form should also be used to create new hosts (empty form)
The same error appear in my textfields and checkboxes.
Do you have any idea or track I can follow?
you don't need this when working on vue templates
<template>
<v-container fluid white>
<v-card>
<v-card-title class="headline">
TEST:
</v-card-title>
<v-card-text>
<p>info :</p>
<p>{{host.typeS}}</p>
<v-select
v-model="host.typeS"
:items="fields.typeS"
item-value="value"
item-text="value"
dense outlined>
</v-select>
</v-card-text>
</v-card>
</v-container>
</template>
note that :items takes an array but you are using fields.typeS so it's better to set field in data as
fields: {}
since it's an object

Value of v-text-field returns null when text area has a value

I'm trying to get the value from vuetify's v-text-field. The problem I am having is that I thought by typing something in, the field will automatically assign whatever is typed to vue-text-field's value. However this doesn't seem to be the case. Below is the code directly from vuetify with some additional code attempting to extract the value:
<template>
<v-card
class="overflow-hidden"
color="purple lighten-1"
dark
>
<v-toolbar
flat
color="purple"
>
<v-icon>mdi-account</v-icon>
<v-toolbar-title class="font-weight-light">
{{setterTitle}}
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn
color="purple darken-3"
fab
small
#click="isEditing = !isEditing"
>
<v-icon v-if="isEditing">
mdi-close
</v-icon>
<v-icon v-else>
mdi-pencil
</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-text-field ref="rowCount"
:disabled="!isEditing"
:value="2"
autofocus
color="white"
label="Number of Rows"
></v-text-field>
<v-autocomplete
:disabled="!isEditing"
:items="states"
:filter="customFilter"
color="white"
item-text="name"
label="Number of Columns"
></v-autocomplete>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
:disabled="!isEditing"
color="success"
#click="save"
>
Save
</v-btn>
</v-card-actions>
<v-snackbar
v-model="hasSaved"
:timeout="2000"
absolute
bottom
left
>
Your profile has been updated
</v-snackbar>
</v-card>
</template>
<script>
export default {
props: {
setterTitle: String
},
data () {
return {
hasSaved: false,
isEditing: null,
model: null,
states: [
{ name: 'Single', abbr: 'S', id: 1 },
{ name: 'Double', abbr: 'D', id: 2 },
{ name: 'Triple', abbr: 'T', id: 3 },
{ name: 'Custom', abbr: 'C', id: 0 },
// { name: 'New York', abbr: 'NY', id: 5 },
],
}
},
methods: {
customFilter (item, queryText, itemText) {
const textOne = item.name.toLowerCase()
const textTwo = item.abbr.toLowerCase()
const searchText = queryText.toLowerCase()
console.log(textOne, textTwo,itemText)
return textOne.indexOf(searchText) > -1 ||
textTwo.indexOf(searchText) > -1
},
save () {
this.isEditing = !this.isEditing
this.hasSaved = true
console.log(this.$refs.rowCount.value)
},
},
}
</script>
As you can see, in the v-text-field tag I added a ref. I then tried to use that reference to extract the value from the save() method. Provided that I also have :value="2" assigned, this.$refs.rowCount.value always return 2. However if I don't have the value property set as 2 then this.$refs.rowCount.value returns null even though I've typed something in the v-text-field. I'm guessing I am misunderstanding something here. Maybe when I type to the field, it doesn't automatically assign what is typed to the value? Thank you for your help.
I've attached the api for v-text-field https://vuetifyjs.com/en/api/v-text-field/#component-pages
v-text-field is designed as common custom input control in Vue - it has a value prop and a input event which means it can by used with v-model - see the docs how that works
As a user, you do this:
<template>
<v-text-field v-model="text" />
</template>
<script>
export default {
data: function() {
return {
text: '' // initial value
}
}
}
</script>
To your question - value is a prop. All props in Vue are one way only - parent can send data to the component (and change that data in the future) but child component cannot change value of the prop it receives. That's why when you type something into the textbox the v-text-field must use an input event to tell the parent component that something has changed and that parent should update "it's own source" of value prop...
v-model is Vue's "syntactic sugar" how to work with these two more easily...

Stable, unchanged numeration of rows in Vuetify Data Table

I'm learning Vue and Vuetify at the moment and I've faced the problem of indexing rows in data tables.
The only way I can assign the number of a row is relying on indexOf comparing to the raw array of data.
But the problem in my case is that when I sort data in the table index breaks accordingly.
I would like to have it stable and when you sort data it recalculates.
Is there any way to achieve it in Vue?
In Angular it's much easier with tables. There is a build-in functionality
Or maybe you know how to reach the filtered or sorted data (array) where Vue holds it.
<v-data-table
:headers="headers"
:items="orders"
class="elevation-2 mt-4"
:loading="loading"
loading-text="Loading... Please wait"
>
<template v-slot:item.index="{ item }">
{{ orders.indexOf(item) + 1}}
</template>
</v-data-table>
Thanks in advance!
The way I've managed to get it working (thanks to #elushnikova) is like this:
<v-data-table
:headers="headers"
:items="orders"
class="elevation-2 mt-4"
:loading="loading"
loading-text="Loading... Please wait"
>
<template v-slot:item="{item, index}">
<tr>
<td>{{index + 1}}</td>
<td>{{item.client_name}}</td>
<td>{{item.client_id}}</td>
<td>{{item.order_total}}</td>
</tr>
</template>
</v-data-table>
It doesn't break on sorting. But I believe later I'll have another problem with it when I have pagination.
But let's solve problems as they come :)
I found this in this link
<template #item.index="{ item }">
{{ protocolData.indexOf(item) }}
</template>
and its worked for me.
Closest way to get the result is by using a computed property as show in codepen
https://codepen.io/manojkmishra/pen/OJyYzVo
Template Part:
<div id="app">
<v-app>
<h3>Orders</h3>
<v-data-table
:headers="headers"
:items="ordersWithIndex"/>
</v-app>
</div>
Script Part:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
headers: [{ text: 'Num', value: 'index' },
{ text: 'OrderName', value: 'Name' },
{ text: 'OrderValue', value: 'value' }
],
orders: [ {Name: 'order1', value: 100 },
{ Name: 'order2', value: 200 },
{ Name: 'order3', value: 300 },
{ Name: 'order4', value: 400 }
],
}
},
computed: {
ordersWithIndex()
{ return this.orders.map(
(items, index) => ({
...items,
index: index + 1
}))
}
}
})
This can be achieved in vuetify using item. slot, you need to pass a header for index along with other headers (eg. SNO).
now just use this in v-data-table.
<template v-slot:item.SNO = "{ index }">
{{ index + 1 }}
</template>

Vuetify insert action button in data-table and get row data

Environment:
vue#^2.6.10:
vuetify#^2.1.0
I want to use v-data-table to show search results and add evaluate button in each row in the v-data-table.
Unfortunately I have two issues:
Evaluate buttons are not shown
I don't know how to get row data of pushed button
What do I need to change?
Template
<v-data-table
:headers="headers"
:items="search_result"
>
<template slot="items" slot-scope="row">
<td>{{row.item.no}}</td>
<td>{{row.item.result}}</td>
<td>
<v-btn class="mx-2" fab dark small color="pink">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</td>
</template>
</v-data-table>
Script
data () {
return {
headers: [
{ text: 'no', value: 'no' },
{ text: 'result', value: 'result' },
{ text: 'good', value: 'good'},
],
// in real case initial search_result = [], and methods: search function inject below data
search_result: [{no: 0, result: 'aaa'}, {no:2, result: 'bbb'],
}
},
slot name used to "replace the default rendering of a row" is item, not items
Add wrapping <tr> into slot template
Just add #click="onButtonClick(row.item) to v-btn and create method onButtonClick
<v-data-table :headers="headers" :items="search_result">
<template v-slot:item="row">
<tr>
<td>{{row.item.no}}</td>
<td>{{row.item.result}}</td>
<td>
<v-btn class="mx-2" fab dark small color="pink" #click="onButtonClick(row.item)">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</td>
</tr>
</template>
</v-data-table>
methods: {
onButtonClick(item) {
console.log('click on ' + item.no)
}
}
Note..
...solution above is replacing default row rendering with your own so expect some of the v-data-table features to not work (didn't try but I expect row selection, grouping, in place editing etc. will be broken). If that's problem for you, here is alternative solution:
Add one more column to your headers definition: { text: "", value: "controls", sortable: false }
Do not override item slot (row rendering). Override item.controls slot instead. Notice "controls" is the same as in column definition - we are overriding just rendering of "controls" column
Everything else is same
<v-data-table :headers="headers" :items="search_result">
<template v-slot:item.controls="props">
<v-btn class="mx-2" fab dark small color="pink" #click="onButtonClick(props.item)">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</template>
</v-data-table>
In my case the solution of Michal was throwing the following exception
The solution was using slot and slot-scope
<template>
<v-data-table
:headers="headers"
:items="items"
class="elevation-1"
>
<template slot="item.delete" slot-scope="props">
<v-btn class="mx-2" icon #click="() => delete(props.item)">
<v-icon dark>mdi-delete</v-icon>
</v-btn>
</template>
</v-data-table>
</template>
<script>
export default {
data() {
return {
headers: [
// Dynamic headers
{
text: 'Name',
value: 'name'
},
{
text: '',
// Row to replace the original template
value: 'delete'
},
],
items: [
{
id: 1,
name: 'A'
},
{
id: 2,
name: 'B'
}
]
};
},
methods: {
delete(item) {
this.items = this.items.filter((d) => d.id !== item.id);
},
},
};
</script>

Problem when creating a menu by iteration

I'm new to vue and vuetify. I need to create a submenu and for that I am using v-menu. Its construction is by iteration, where I need each sub menu to assign it a method. But it turns out that the way I'm doing it generates an error
[Vue warn]: Error in v-on handler: 'TypeError: handler.apply is not a function'
. What am I doing wrong?
https://codepen.io/maschfederico/pen/vMWBPV?editors=1011
<div id="app">
<v-app id="inspire">
<div class="text-xs-center">
<v-menu>
<template #activator="{ on: menu }">
<v-tooltip bottom>
<template #activator="{ on: tooltip }">
<v-btn
color="primary"
dark
v-on="{ ...tooltip, ...menu }"
>Dropdown w/ Tooltip</v-btn>
</template>
<span>Im A ToolTip</span>
</v-tooltip>
</template>
<v-list>
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="item.f"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>
new Vue({
el: '#app',
data: () => ({
items: [
{ title: 'Click Me1',f:'login'},
{ title: 'Click Me2',f:'login' },
{ title: 'Click Me3',f:'login' },
{ title: 'Click Me4' ,f:'login' }
]
}),
methods: {
login(){console.log('login')}
}
})
Try to pass the method name to another one and handle it inside the last one like :
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="handle(item.f)"
>
inside the methods :
methods: {
handle(f){
this[f]();
},
login(){console.log('login')}
}
check this codepen
You are passing the method's name - a string - instead of a function. The click event listener generated by vue is trying to call a function using apply, this is why you are getting that error.
One solution would be to pass directly the function when the Vue instance is created (before that, the method might not be available, so passing it directly to the data { title: 'Click Me1', f: this.login } would not work).
For example, you could keep having method names in the data, and replace them with the actual methods at create:
new Vue({
el: '#app',
data: () => ({
items: [
{ title: 'Click Me1', f: 'login' }
]
}),
created () {
this.items.forEach(item => {
item.f = this[item.f]
})
},
methods: {
login (){
console.log('login')
}
}
})