Vuetify Dynamic Binding of Property - javascript

I've got a form which contains a dynamic set of input components (v-selects and v-text-fields) that a user has to set. However, I'm really struggling to understand how to retrieve the values from these input components once the user submits the form. My input setup looks like this:
<v-row>
<v-col
v-for="(item, index) in documentTags.tags"
:key="index"
cols="18"
sm="8"
md="6"
>
<v-select
v-if="item.InputType === 'Combobox'"
:v-model="documentTagResponses[index]"
:name="item.Name"
:items="item.TagValueOptions"
:rules="rules.ComboboxRule"
item-text="Value"
:label="item.Name"
required
>
</v-select>
<v-text-field
v-else-if="item.InputType === 'Textbox'"
:v-model="documentTagResponses[index]"
:name="item.Name"
:label="item.Name"
:rules="rules.TextboxRule"
required
>
</v-text-field>
</v-col>
</v-row>
And my documentTags definition looks something like this:
{
tags: [
{
TagDefinitionId: '1',
InputType: 'Combobox',
Name: 'Name_1',
TagValueOptions: [
{
Value: 'Option 1',
},
{
Value: 'Option 2',
},
{
Value: 'Option 3',
},
],
},
{
TagDefinitionId: '2',
InputType: 'Textbox',
Name: 'Name_2',
TagValueOptions: [],
},
],
}
I'm trying to bind the form responses from these inputs to an array called documentTagResponses.
So, something like this:
export default {
name: 'MyModal',
data: () => ({
documentTagResponses: [],
}),
methods: {
validate() {
const valid = this.$refs.form.validate();
if (valid) {
for (const key of Object.keys(this.documentTagResponses)) {
console.log(`${key} -> ${this.documentTagResponses[key]}`);
}
}
},
};
Then, when the user clicks the submit button, I call the validate function to ensure the components are valid and then do something with the values... Right now I'm simply trying to print the values of each component to the console. However, my documentTagResponses array is always empty. Does anyone know why I can't seem to access the values that are being set in these components?

I just tested your code in my project and the problem was :v-model
We don't need : in v-model
So the code can be like below
<v-row>
<v-col
v-for="(item, index) in documentTags.tags"
:key="index"
cols="18"
sm="8"
md="6"
>
<v-select
v-if="item.InputType === 'Combobox'"
v-model="documentTagResponses[index]"
:name="item.Name"
:items="item.TagValueOptions"
:rules="rules.ComboboxRule"
item-text="Value"
:label="item.Name"
required
>
</v-select>
<v-text-field
v-else-if="item.InputType === 'Textbox'"
v-model="documentTagResponses[index]"
:name="item.Name"
:label="item.Name"
:rules="rules.TextboxRule"
required
>
</v-text-field>
</v-col>
</v-row>

Related

Can't validate v-text-field [migrating from older version of Vuetify]

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.

How to get changed object field from vuetify form written in Vuex store

I have the following vue component and the values are loaded correctly from the store and displayed on the form. However if I change the value in a field it is not updated in the store. How can I make it such that whenever I change a value in the form it is also committed to the store?
<template>
<v-form v-model="valid" justify="center" class="mx-5" :disabled="show">
<v-row justify="center">
<H1 class="my-10">{{coin}}</H1>
</v-row>
<v-row>
<H3 class="my-5 mx-10">Add account</H3>
</v-row>
<v-row justify="space-around">
<v-text-field v-model="settings.key" :rules="[rules.required]" class="mx-12" label="API Key" />
</v-row>
</v-form>
</template>
<script>
import store from '../store'
export default {
name: "Settings",
store,
props: ["coin", "show"],
data: () => ({
valid: true,
rules: {
required: (value) => !!value || "Required.",
},
}),
computed: {
settings: {
get() { return store.getters.getSettingsBySymbol(this.coin) },
set(value) { store.commit("setSettingsBySymbol", value) },
}
},
}
</script>
==== EDIT =====
settings = {
coin: "hh",
key: "12345",
secret: "ajksjlkuT6ty",
}

How to delete row from datatable without interfering a computed function using a click event in vue.js

I am making a shopping cart using Vuetify's current CRUD Datatable UI Component (compatible with Vue.js2). I previously had a problem to calculate the total of a product by multiplying the product's quantity with its price. Fortunately, I was given a great solution by using a computed function detailsWithSubTotal() to calculate the total and replacing the datatable's :item value (previously "details") to :item="detailsWithSubTotal" as you can see in the following code:
HTML
<template>
<v-layout align-start>
<v-flex>
<v-container grid-list-sm class="pa-4 white">
<v-layout row wrap>
<v-flex xs12 sm8 md8 lg8 xl8>
<v-text-field
v-model="code"
label="Barcode"
#keyup.enter="searchBarcode()"
>
</v-text-field>
</v-flex>
<v-flex xs12 sm12 md12 lg12 xl12>
<v-data-table
:headers="headerDetails"
:items="detailsWithSubTotal"
hide-default-footer
class="elevation-1"
>
<template v-slot:item.delete="{ item }">
<v-icon small class="ml-3" #click="deleteDetail(detailsWithSubTotal, item)">
delete
</v-icon>
</template>
<template v-slot:no-data>
<h3>There are no current products added on details.</h3>
</template>
</v-data-table>
</v-flex>
</v-layout>
</v-container>
</v-flex>
</v-layout>
</template>
JAVASCRIPT
<script>
import axios from 'axios'
export default {
data() {
return {
headerDetails: [
{ text: 'Remove', value: 'delete', sortable: false },
{ text: 'Product', value: 'product', sortable: false },
{ text: 'Quantity', value: 'quantity', sortable: false },
{ text: 'Price', value: 'price', sortable: false },
{ text: 'Subtotal', value: 'subtotal', sortable: false }
],
details: [],
code: ''
}
},
computed: {
detailsWithSubTotal() {
return this.details.map((detail) => ({
...detail,
subtotal: detail.quantity * detail.price
}))
}
},
methods: {
searchBarcode() {
axios
.get('api/Products/SearchProductBarcode/' + this.code)
.then(function(response) {
this.addDetail(response.data)
})
.catch(function(error) {
console.log(error)
})
},
addDetail(data = []) {
this.details.push({
idproduct: data['idproduct'],
product: data['name'],
quantity: 10,
price: 150
})
},
deleteDetail(arr,item){
var i= arr.indexOf(item);
if (i!==-1){
arr.splice(i,1);
}
},
}
}
</script>
Though this helped to solve my previous problem, now I can't delete the added detail.
While keeping the array parameter in the #click event as #click="deleteDetail(details, item) and the datatable's :item value as :items="details", the delete button works with no problem, but then the subtotal calculation does not work as you can see in the following image:
But when replacing both array parameter to #click="deleteDetail(detailsWithSubTotal, item) and :item value to :items="detailsWithSubTotal", it calculates the subtotal but the delete button stops working.
How can I delete the added row on the datatable while not interfering with the detailsWithSubTotal() computed function?
You can try to add detail itself to a computed item as a link to an original item and use it to delete from the original details array:
detailsWithSubTotal() {
return this.details.map((detail) => ({
...detail,
subtotal: detail.quantity * detail.price,
source: detail
}))
}
...
#click="deleteDetail(details, item)
...
deleteDetail(arr,item){
var i= arr.indexOf(item.source);
if (i!==-1){
arr.splice(i,1);
}
}

Execute method on enter key VueJS?

I am using vuetify and trying to create a method to add chips from the dropdown. Now i got most of the logic down but as you can see, the method multipleSelection does not fire off on the enter key but works fine onClick.
Here is a demo for the codepen.
So the method multipleSelection should fire off on the enter key.
new Vue({
el: '#app',
data() {
return {
arr: [],
items: [{
text: 'Foo',
value: 'foo'
},
{
text: 'Bar',
value: 'bar'
},
{
text: 'biz',
value: 'buzz'
},
{
text: 'buzz',
value: 'buzz'
}
],
}
},
methods: {
multipleSelection(item) {
this.arr.push(item)
console.log(this.arr)
},
deleteChip(item) {
console.log('delete')
this.arr = this.arr.filter(x => x !== item);
console.log(this.arr)
}
},
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-container>
<v-combobox :items="items" label="Add Multiple Chips" multiple small-chips solo deletable-chips :value="arr">
<template v-slot:item="{ index, item }">
<v-list-tile-content #click.stop.prevent="multipleSelection(item)">
{{item.text}}
</v-list-tile-content>
</template>
<template v-slot:selection="{ index, item }">
<v-chip close dark color="info" #click:close="deleteChip(item)">
{{ item.text }}
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
Since multipleSelection() is not being called from #keypress on v-slot:item, likely it's not where the event is being captured.
Taking a look at events on Vue Dev Tools, can see input $emit by <VCombobox> is the first one after pressing Enter.
So the following will get it, but this seems to mess with the position in the list for some reason I don't understand.
<v-combobox
...
#input.capture="(item) => multipleSelection(item)"
>
Better to add a listener,
mounted() {
this.$refs.combobox.$on('input', (items) => {
const item = items[items.length -1]; // getting all selected, so take the last
this.multipleSelection(item)
})
},
Note, I tested this on a local project with Vuetify v1.5.14.
Looks like you need #keyup
<v-list-tile-content
#keyup.enter.prevent="multipleSelection(item)" #click.stop.prevent="multipleSelection(item)">{{item.text}}
</v-list-tile-content>
Not sure about keypress....Vue docs show #keyup
https://v2.vuejs.org/v2/guide/events.html#Key-Modifiers

vue / vuetify dynamically modify v-text-field properties

I've got a couple fields:
<v-row>
<v-col cols="12" class="d-flex">
<v-text-field clearable outlined required :rules="rules.codeRules" name="code" v-model="attribute.code" label="Code"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" class="d-flex">
<v-text-field clearable outlined required :rules="rules.nameRules" name="name" v-model="attribute.name" label="Name"></v-text-field>
</v-col>
</v-row>
In Vuetify's documents I see there's a couple properties named error which triggers an error state and error-messages with messages to be displayed.
When the form's submitted, if there's any errors on any of the fields I want to trigger these properties.
How can I do that? For instance, how can I manually set the field with the name of "code" into an error state by using the error property? how can I pass a personalized message to it? Do I need to specifically create a variable in the data() object in order to do what I wanna do? because if I have to do it that way it'd mean that I need to create an error and an error-messages properties in the data object for each and every field in my form!? or it can do by selecting exactly the field that I want to modify and somehow update its properties directly without the need of any variable from the model?
Thanks
Edit
Here's what I'm doing:
<v-row>
<v-col cols="12" class="d-flex">
<v-text-field clearable outlined required :error="formErrors.code.error" :error-message="formErrors.code.errorMessages" :rules="rules.codeRules" name="code" v-model="attribute.code" label="Code"></v-text-field>
</v-col>
</v-row>
My submit method is the following:
axios.post(postUrl, this.attribute, { Accept: "application/json" })
.then(response => {
if (response.data.success) {
Event.fire('message', {
type: 'success',
message: 'Attribute successfully saved'
});
this.$router.push('/attributes/list')
}
})
.catch(errors => {
// eslint-disable-next-line no-console
console.log(errors.response.data)
const responseErrors = errors.response.data
if (responseErrors) {
// eslint-disable-next-line no-console
console.log('here modafucka')
//const self = this
for (const key of Object.keys(responseErrors)) {
// eslint-disable-next-line no-console
console.log(responseErrors[key][0])
this.formErrors[key].error = true;
this.formErrors[key].errorMessages = responseErrors[key][0];
}
}
})
}
By setting this.formErrors[key].error = true; I'm able to put the field in an error state, but still the customized error message is not being displayed
First of all, you don't need both error and error-messages prop. If you just set error-messages, the input field will go in error state and the message will be shown
Do I need to specifically create a variable in the data() object in
order to do what I wanna do? because if I have to do it that way it'd
mean that I need to create an error and an error-messages properties
in the data object for each and every field in my form!
Yes, you'll need to set error-messages prop for each and every field. You already have separate variables for v-model and rules.
Ideally you would be running a for loop to generate the input fields(shown below), but a simple :error-messages="errorMessages.code" & :error-messages="errorMessages.name" will also work.
// Fields array
[
{
name: 'code',
rules: [ // list of rules ],
value: '' // Some value,
errorMessages: [] // Could be list or string
},
{
name: 'name',
rules: [ // list of rules ],
value: '' // Some value,
errorMessages: [] // Could be list or string
}
]
// Your form template
<v-row v-for="field in fields" :key="field.name">
<v-col cols="12" class="d-flex">
<v-text-field
clearable
outlined
required
:rules="field.rules"
:name="field.name"
v-model="field.value"
:label="field.name"
:error-messages="field.errorMessages"
>
</v-text-field>
</v-col>
</v-row>

Categories