0
votes

Iam using vee-validate plugin for validation. In my form, there is a select field in the table. Rows will be added dynamically in the table. I don't want to select the same select(Description column) option again and again Image. Hence I want to throw an error like "Selected description already exists in a table" this using vee-validate. Kindly help me to solve this.

Here is my code:

<template>
<div>
    <b-card>
        <div class="panel-body" id="app">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <th style="width: 20px;">No.</th>
                        <th style="width: 330px;">Description</th>
                        <th style="width: 130px;" class="text-right">Charges</th>
                        <th style="width: 130px;">Total</th>
                        <th style="width: 130px;"></th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="(row, index) in rows" :key="row.qty">
                        <td>
                            {{ index +1 }}
                        </td>
                        <td>

                        <select class="form-control" v-model="row.billChgDesc" v-validate="'required|check'" :name="'billChgDesc' + index" data-vv-as="Description" @change="checkRepetation">
                            <option v-for="option in billChgDescOpt" v-bind:value="option.value" 
                                :key="option.value"> {{ option.text }} 
                            </option>
                        </select>
                        <span v-show=" errors.has('billChgDesc' + index)" class="is-danger">{{  errors.first('billChgDesc' + index) }}</span>
                        </td>

                                <td> 
                                  <input class="form-control text-right" type="text" v-model="row.charges"  data-type="currency" v-validate="'required'" :name="'charges' + index" data-vv-as="Charges" >
                                     <span v-show=" errors.has('charges' + index)" class="is-danger">{{  errors.first('charges' + index) }}</span>
                                <td>
                                    <input class="form-control text-right" :value="row.qty * row.charges" number readonly />
                                    <input type="hidden" :value="row.qty * row.charges * row.tax / 100"  number/>
                                </td>

                                <td>
                                    <button class="btn btn-primary btn-sm" @click="addRow(index)"><i class="fa fa-plus"></i></button>
                                    <button class="btn btn-danger btn-sm" @click="removeRow(index)"><i class="fa fa-minus"></i></button>
                                </td>
                            </tr>
                </tbody>
                <tfoot>
                  <tr>
                        <td colspan="3" class="text-right">TOTAL</td>
                        <td colspan="1" class="text-right"><input class="form-control text-right" v-model="delivery" number/></td>
                        <td></td>
                    </tr>
                </tfoot>
            </table>

        </div>
    </b-card>
</div>
</template>

<script>
import Vue from 'vue'
import accounting from 'accounting'

export default {

  data: function () {
    return {
        billChgDescOpt: [
            { value: '', text: 'Select' },
            { value: 'M', text: 'Maintenance Fee'},
            { value: 'W', text: 'Water Charges'},
            { value: 'P', text: 'Penalty Fee'},
            ],
        rows: [
            {qty: 5, billChgDesc: '', charges: 55.20, tax: 10},
            {qty: 19, billChgDesc: '', charges: 1255.20, tax: 20},
        ],
        grandtotal: 0,
        delivery: 40,
        selectArr:[]
    }

  },
   methods: {
        addRow: function (index) {
            try {
                this.rows.splice(index + 1, 0, {});
            } catch(e)
            {
                console.log(e);
            }
        },
        removeRow: function (index) {
            this.rows.splice(index, 1);
        },
        checkRepetation:function(){
             this.$validator.extend('check', {
                    getMessage: field => '* Slected ' + field + ' already exists',
                    validate: function(value){
                            selectArr.push(value);
                    }
    })
        }
    }
}
</script>

<style lang="scss" scoped>
.is-danger{
  color:  RED;
}
</style>

Thanks in advance.

1

1 Answers

0
votes

You're on the right track, but a couple changes need to be made. When you call this.$validator.extend, that only needs to be done once - when your component is created. It attaches the check method to the validator, so then every time you have the attribute v-validate="'required|check'" in your HTML, it will run that check method.

In your check validator, you need to answer the question "is this value already selected". The answer is to go through the this.rows and see if any of them have the same billChgDesc property. Because this is in Vue, by the time the validator gets run, the row in question already does have that value, so you want to check if MORE than one row have that value. So, something like this:

mounted() {
  var self = this;
  this.$validator.extend('check', {
    getMessage: field => '* Selected ' + field + ' already exists',
    validate: function(value){
      return (self.rows.filter(function(v){
        return v.billChgDesc == value;
      }).length <= 1);
    }
  });
}

This validator returns true if only one item has the given value. I'm using the built-in filter method of Array (see docs).

You can see an example of this all working here: https://jsfiddle.net/ryleyb/f9q50wx4/1/