I have a vue application where I am trying to save an array which consists of a date field and has another array inside with time and time has startTime and untilTime as values.
What i want to achieve is that that when I click on a button the fields connected to time.startTime and untilTime should be added on button click. But I am getting this error:
vue.runtime.esm.js?2b0e:619 [Vue warn]: Avoid using non-primitive value as key, use string/number value instead.
found in
---> <DatePickerComponent> at src/components/DatePickerComponent.vue
<VCard>
<VThemeProvider>
<VDialog>
<VsRow>
<VCard>
<NextMeetingCardComponent> at src/components/NextMeetingCardComponent.vue
<DashboardComponent> at src/views/DashboardComponent.vue
<VMain>
<VApp>
<App> at src/App.vue
<Root>
I am a bit new to vue and javascript could someone point out my error maybe?
<template>
<div>
<v-row>
<v-col cols="12" sm="12">
<v-menu v-for="item in dates" :key="item"
ref="menu"
v-model="menu"
:close-on-content-click="false"
:return-value.sync="dates"
transition="scale-transition"
offset-y
min-width="auto"
>
<template v-slot:activator="{ on, attrs }" >
<v-text-field
v-model="item.date"
label="Picker in menu"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<v-date-picker
v-model="dates"
no-title
scrollable
>
<v-spacer/>
<v-btn
text
color="primary"
#click="menu = false"
>
Cancel
</v-btn>
<v-btn
text
color="primary"
#click="$refs.menu.save(dates)"
>
OK
</v-btn>
</v-date-picker>
</v-menu>
<v-btn v-on:click="addTimeFields()">Add Time</v-btn>
</v-col>
</v-row>
<v-row v-for="i in dates" :key="i">
<v-col
cols="6"
sm="6"
>
<v-text-field
v-model="i.time.startTime"
label="Starttime"
type="time"
></v-text-field>
</v-col>
<v-col
cols="6"
sm="6"
>
<v-text-field
v-model="i.time.untilTime"
label="Untiltime"
type="time"
></v-text-field>
</v-col>
</v-row>
</div>
</template>
<script>
export default {
name: "DatePickerComponent",
data: () => ({
dates: [{date: new Date().toISOString().substr(0,10), time: [{
startTime: "",
untilTime: ""
}]}],
timeInputFields:[],
selectedDate: [],
menu: false,
modal: false,
menu2: false,
}),
methods:{
addTimeFields(){
this.dates[0].time.push({
startTime: "",
untilTime: "",
})
}
}
};
</script>
Output Vue:
Vue output
I think there are a couple of issues.
time is not an array, it is an object
You don't have 2 dates in the array, so probably want the 0 based index.
#2 is where the error is coming from.
addTimeFields(){
this.dates[0].time.push({
startTime: "",
untilTime: "",
})
}
Since your first entry already has start and until times set, to update you just need to to:
this.dates[0].time = {
startTime: "",
untilTime: "",
}
or
this.dates[0].time.startTime = "",
this.dates[0].time.untilTime = "",
EDIT one
I created a code pen for your code, there are quite a few errors, probably around the setup, but this screenshot shows what happens if you add two dates into the array. You get a list of them at the top, and then a list of the times. This makes me think you probably are just wanting one?
Please see this pen, https://codepen.io/OblivionSY/pen/rNyOLpw?editors=1011 it is not amazing, and doesn't fully work (date picker has an error)
Some general notes though:
make sure your :key value are primitive (string, int etc) and unique
try and split up the functionality into components. If you have multiple dates, then have one component to deal with editing a single date and the respective times.
feel free to edit the pen if you want me to take another look.
Related
I have a vue form inside a dialog. Where the user can select a date and multplie starting times and ending times. So far I can save one object consisting of one date and multiple times. But when I want to add another object it takes the new date but changes the time values for every object.
for example if I add first an object with 05.09.2021 start:15:00 end: 16:00 and then add another object with date: 06.09.2021 start: 16:00 end: 17:00. The start and end is changed to the latest value of all objects, but I want each of them to be individually. I tried submitting a form, but sadly I could not make it work because it is reloading the page which I do not want, if i use .prevent on submit my error with time changing for every object still consists. Could someone take a look at my code and point me my mistake out`?
HTML:
<v-row>
<v-col cols="12" sm="12">
<v-menu
ref="menu3"
v-model="menu3"
:close-on-content-click="false"
:return-value.sync="dates"
transition="scale-transition"
offset-y
min-width="auto"
>
<template v-slot:activator="{ on, attrs }" >
<v-text-field
v-model="dates"
label="Picker in menu"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<v-date-picker
v-model="dates"
no-title
scrollable
>
<v-spacer/>
<v-btn
text
color="primary"
#click="menu3 = false"
>
Cancel
</v-btn>
<v-btn
text
color="primary"
#click="$refs.menu3.save(dates) "
v-on:click=" menu3 = false"
>
OK
</v-btn>
</v-date-picker>
</v-menu>
<v-btn v-on:click="addTimeFields()">Add Time</v-btn>
</v-col>
</v-row>
<v-row v-for="(find, index) in selectedTime" >
<v-col
cols="6"
sm="6">
<v-text-field
v-model="find.startTime"
label="Starttime"
type="time"
></v-text-field>
</v-col>
<v-col
cols="6"
sm="6">
<v-text-field
v-model="find.endTime"
label="Untiltime"
type="time"></v-text-field>
</v-col>
</v-row>
</form>
Javascript:
<script>
import MeetingsTableComponent from "#/components/MeetingsTableComponent";
import DatePickerComponent from "#/components/DatePickerComponent";
export default {
name: "NextMeetingCardComponent",
data: () => ({
selectedTime: [],
dates: new Date().toISOString().substr(0,10),
datesFinal: [],
dialog: false,
menu: false,
modal: false,
menu2: false,
menu3: false
}),
methods:{
addTimeFields(){
this.selectedTime.push({
startTime:"",
endTime: "",
})
},
saveDateAndTIme(e){
this.datesFinal.push({
date: this.dates,
times : [this.selectedTime]
}
)
this.$refs.form.submit()
},
clearArrays(){
while (this.selectedTime.length){
this.selectedTime.pop()
}
}
}
};
</script>
I tried clearing the selectedTimes array which is pushed to datesFinal after pushing it but then every value is deleted.
I think there's an error in saveDateAndTIme(): times contains a nested array of the this.selectedTime array, but that should be unnested (i.e., set times to this.selectedTime itself).
After pushing this.selectedTime into datesFinal, you could clear the form by setting this.selectedTime to an empty array.
export default {
methods: {
saveDateAndTIme(e) {
this.datesFinal.push({
date: this.dates,
times: this.selectedTime, 👈
})
this.selectedTime = [] 👈
},
}
}
demo
I have a bit of a problem. I am trying to loop through an object in order to create textboxes dynamically, rather than manually writing out the fields.
editedItem: {
day: "",
"9.00 - 10.00": "",
"10.00 - 11.00": "",
"11.00 - 12.00": "",
"12.00 - 13.00": "",
"13.00 - 14.00": "",
"14.00 - 15.00": "",
},
Then in the template.
<v-col cols="12" sm="6" md="4" v-for="item in editedItem" :key="item">
<v-text-field v-model="item"></v-text-field>
</v-col>
That's not working. I have also tried to loop through the keys (editedItem.keys), but I can't seem to assign just "item" to the v-model.
You can think of the template behaving like this:
for (let i in editedItem) {
let item = editedItem[i];
// On #input
item = $event;
}
It's editing a copy. You can resolve the issue by referencing the v-model by index.
<v-col cols="12" sm="6" md="4" v-for="(item, index) in editedItem" :key="item">
<v-text-field v-model="editedItem[index]"></v-text-field>
</v-col>
item is value of your editedItem.keys(days,...), for objects iteration (v-for), second param is the actual key.
v-for="(value, key) in object".
So, change your code like this, it will work
<v-col cols="12" sm="6" md="4" v-for="(item, key) in editedItem" :key="item">
<v-text-field v-model="editedItem[key]" />
</v-col>
I've been migrating an old project from last year since Entity Framework 5 was released. Since I'm also migrating the frontend from the previous version of Vuetify I worked with last year (from 2.2.11 to 2.4.0), I've encountered some issues that I've had to look up online and fix, but I got this sudden issue that I can't quite lay a finger on and haven't been able to find a similar issue with an answer.
I'm trying to validate a v-text-field inside a v-card to not save a record if the v-text-field length is less than 3 or greater than 50 characters. Despite that I've followed up the same code I used last year to validate my v-text-field, I get the following errors on chrome console:
The last 2 errors from this image pop up when you click save when it should show the validation message:
The code used in the component is the following.
For the Validation message that should pop below the description v-text-field in red:
VUE:
<template v-slot:top>
<v-toolbar flat>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">New</v-btn>
</template>
<v-card>
<v-container>
<v-row>
<v-col cols="12" sm="12" md="12">
<v-text-field v-model="name" label="Name"></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="12">
<v-text-field v-model="description" label="Description"></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="12" v-show="valida">
<div class="red--text" v-for="v in validaMessage" :key="v" v-text="v">
</div>
</v-col>
</v-row>
</v-container>
</v-card>
</v-dialog>
</v-toolbar>
</template>
JAVASCRIPT:
<script>
import axios from 'axios'
export default {
data(){
return {
categories:[],
dialog: false,
dialogDelete: false,
headers: [
{ text: 'Options', value: 'actions', sortable: false, class:"primary--text" },
{ text: 'Name', value: 'name', class:"primary--text" },
{ text: 'Description', value: 'description', sortable: false, class:"primary--text" },
{ text: 'Status', value: 'status', sortable: false, class:"primary--text" },
],
search: '',
desserts: [],
editedIndex: -1,
editedItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
id: '',
name: '',
description: '',
valida: 0,
validaMessage:[]
}
},
methods: {
save () {
if (this.valida()){
return;
}
if (this.editedIndex > -1) {
//Code to update
}
else {
let me=this;
axios.post('api/Categories/Create',{
'name': me.name,
'description': me.description
}).then(function(response){
me.close();
me.list();
me.clean();
}).catch(function(error){
console.log(error);
});
}
},
valida(){
this.valida=0;
this.validaMessage=[];
if (this.name.length < 3 || this.name.length > 50){
this.validaMessage.push("The name must have between 3 and 50 characters.")
}
if (this.validaMessage.length){
this.valida=1;
}
return this.valida;
}
},
}
</script>
This is how it used to show like in my older version of the project:
I don't know if something that was changed in the Vuetify update version from 2.2.11 to 2.4.0 is interfering with the ability to implement this method inside the component to be able to show or hide the validation message. I'm trying to resolve this to avoid having to recur to 3rd party validation services like vee-validate or vuelidate. What could it be? Thank you for taking your time in reading this!
To sum up, these were the reasons for the errors:
There were a data property and a method with the same name - valida
v-btn component had a deleted method initialize provided to #click event.
And another tip as a bonus:
You have a v-col that spans to 12 columns: <v-col cols="12" sm="12" md="12">. Since it should span 12 columns on every screen size, there's really no need to define the columns span for small and medium breakpoints. So in this case it really should be only <v-col cols="12"> - will save you a little file size and loading time.
As the title states, Im looking to add a google autocomplete inside of a vuetify dialog box within vue.js. I have successfully added autocomplete to regular inputs on the page by using the mounted() function and attaching there(code below). However it seems that since the dialog box doesnt render until you click a button to launch it, using this.$refs and attaching an autocomplete to those inputs wont work. Ive also tried creating a function to attach after the dialog launches and it still does not work. Any insight appreciated.
The part that works:
mounted() {
console.log(this.$refs)
//creates google maps automcomplete on the address inputs. Gets the addresses and saves them as well as getting the lat and long from each and saving them
for (let ref in this.$refs) {
const options = {
componentRestrictions: { country: 'us' },
fields: ['geometry', 'name', 'formatted_address'],
strictBounds: false,
types: ['address'],
}
const autocomplete = new google.maps.places.Autocomplete(
this.$refs[ref],
options
)
autocomplete.addListener('place_changed', () => {
// selected place object, bring up info such as address & geo coords
const place = autocomplete.getPlace()
// gets the address and lat/long from the found address and saves them to be used later when actually creating the order
if (ref == 'pickupAddress') {
this.pickupAddress = `${place.formatted_address}`
this.pickupPair = place.geometry.location
} else {
this.dropoffAddress = `${place.formatted_address}`
this.dropoffPair = place.geometry.location
}
})
}
this.$store.dispatch('getOpenOrders')
}
The dialog box
<v-row justify="center">
<v-dialog v-model="dialog" persistent max-width="600px">
<template v-slot:activator="{ on, attrs }">
<v-btn
color="rgb(216, 52, 125)"
dark
v-bind="attrs"
v-on="on"
style="margin-top: 10px"
#click="addRefs"
>
Add a leg to an existing order
</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">Add a leg</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field
label="Order ID*"
required
v-model="legId"
hint="Order ID of the order you want to add a leg to"
persistent-hint
></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
label="Leg Letter (a-z)*"
required
v-model="legLetter"
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
label="Leg Pickup Address*"
ref="legPickupAddress"
required
v-model="legPickup"
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
label="Leg Dropoff Address*"
ref="legDropoffAddress"
required
v-model="legDropoff"
></v-text-field>
</v-col>
</v-row>
</v-container>
<small>*indicates required field</small>
<v-alert
class="v-alert"
v-if="noLegFoundAlert"
type="error"
dismissible
#click="noLegFoundAlert = !noLegFoundAlert"
>
No matching leg found, please enter another id or create a leg!
</v-alert>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text #click="dialog = false"> Close </v-btn>
<v-btn text #click="addLeg"> Save </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
and finally the addRefs function which DOES NOT WORK
async addRefs() {
const options = {
componentRestrictions: { country: 'us' },
fields: ['geometry', 'name', 'formatted_address'],
strictBounds: false,
types: ['address'],
}
var input = this.$refs['legPickupAddres'] //DOES NOT WORK DOESNT EXIST APPARENTLY!!!!!!
const autocomplete1 = new google.maps.places.Autocomplete(input, options)
autocomplete1.addListener('place_changed', () => {
// selected place object, bring up info such as address & geo coords
const place = autocomplete.getPlace()
// gets the address and lat/long from the found address and saves them to be used later when actually creating the order
this.legPickup = `${place.formatted_address}`
})
}
Hey im trying to iterate through an array of urls to feed it inside of my src attribute. I'm not sure about the correct syntax to use. Can you help me?
I have my array called DataArray made of images url, and i want to insert inside of v-img in v-row. i tried this :src=DataArray[n] but it's not the correct way.
<template>
<v-container v-if="Dataloaded" class="dark">
<v-row no-gutters>
<template v-for="n in 6">
<v-col :key="n">
<v-card dark class="mx-auto" max-width="344">
<v-img :src=DataArray[n] height="200px"></v-img>
<v-card-title>Title</v-card-title>
<v-card-subtitle>Anime-Descriptions</v-card-subtitle>
<v-card-actions>
<v-btn text>View</v-btn>
<v-btn color="purple" text>Explore</v-btn>
<v-spacer></v-spacer>
<v-btn icon #click="show = !show">
<v-icon>{{ show ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
</v-btn>
</v-card-actions>
<v-expand-transition>
<div v-show="show">
<v-divider></v-divider>
<v-card-text>I'm a thing. But, like most politicians, he promised more than he could deliver. You won't have time for sleeping, soldier, not with all the bed making you'll be doing. Then we'll go with that data file! Hey, you add a one and two zeros to that or we walk! You're going to do his laundry? I've got to find a way to escape.</v-card-text>
</div>
</v-expand-transition>
</v-card>
<!-- spacing -->
<br :key="1" />
<br :key="2" />
</v-col>
<v-responsive v-if="n === 3" :key="`width-${n}`" width="100%"></v-responsive>
</template>
</v-row>
</v-container>
</template>
<script>
import db from "#/Firebase/firebaseInit";
export default {
name: "Grid",
data() {
return {
DataArray: [],
Dataloaded: false,
show: false
};
},
created() {
let ArtData = db.collection("artworks").doc("yWnmwHTxMhvobLi21FNw");
ArtData.get()
.then(doc => {
if (doc.exists) {
// This variable contains all the necessary artworks for display
let DataArray = doc.data().Array;
console.log("Document data!:", DataArray);
this.Dataloaded = true;
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function(error) {
console.log("Error getting document:", error);
});
}
};
</script>
Change this:
let DataArray = doc.data().Array;
to:
this.DataArray = doc.data().Array;
And use this to show the image:
<v-img :src="DataArray[n-1]" height="50px"></v-img>
n-1 because the v-for you're using is 1-based.