Search code examples
vue.jsvee-validate

Vee validate - repeat form - checkbox


I have a simple form with a few inputs to capture contact details. the form can be repeated using the add button provided. the user must select a primary contact if multiple contacts being added. I have a custom validator for this. My problem arises when a couple of rows being added and unselect all primary contact checkboxes then select one. - at this point, I want all errors that are previously shown to be disappeared. for some reason, two way data binding doesn’t seem to work or I must be doing something wrong. assuming I have more validations on other fields, how can I get ride of validation errors related to primary contact checkboxes?

<template>
  <div id="app">
    <b-col md="12">
      <ValidationObserver ref="contactValidation">
        <b-row
          v-for="(item, index) in contactDtos"
          :id="item.id"
          :key="item.id"
          ref="row"
        >
          <b-col md="3">
            <!-- First Name -->
            <b-form-group>
              <ValidationProvider
                #default="{ errors }"
                name="First name"
                vid="FirstName"
              >
                <b-form-input
                  id="'firstName' + item.id"
                  v-model="item.firstName"
                  :state="errors.length > 0 ? false : null"
                  placeholder="First Name"
                />
                <small class="text-danger">{{ errors[0] }}</small>
              </ValidationProvider>
            </b-form-group>
          </b-col>
          <b-col md="3">
            <!-- Last Name -->
            <b-form-group>
              <ValidationProvider
                #default="{ errors }"
                name="Last name"
                vid="LastName"
              >
                <b-form-input
                  id="'lastName' + item.id"
                  v-model="item.lastName"
                  :state="errors.length > 0 ? false : null"
                  placeholder="Last Name"
                />
                <small class="text-danger">{{ errors[0] }}</small>
              </ValidationProvider>
            </b-form-group>
          </b-col>
          <b-col md="3" class="mt-2">
            <!-- Primary contact -->
            <b-form-group>
              <ValidationProvider
                #default="{ errors }"
                :rules="{ chk_primary_contact: [contactDtos, item.id] }"
                name="Primary contact"
                vid="PrimaryContact"
              >
                <b-form-checkbox
                  v-model="item.primaryContact"
                  :checked="item.primaryContact"
                  :state="errors.length > 0 ? false : null"
                >
                  Primary contact
                </b-form-checkbox>
                <small class="text-danger">{{ errors[0] }}</small>
              </ValidationProvider>
            </b-form-group>
          </b-col>
          <b-col md="2" class="mt-2">
            <b-row>
              <b-col v-if="index + 1 == contactDtos.length" cols="6">
                <b-button
                  :v-if="false"
                  variant="outline-primary"
                  class="rounded-circle"
                  @click="contactFormRepeat"
                >
                  Add
                </b-button>
              </b-col>
              <b-col
                v-if="index + 1 == contactDtos.length && contactDtos.length > 1"
                cols="6"
              >
                <b-button
                  variant="outline-danger"
                  class="rounded-circle"
                  @click="removeContactForm(index)"
                >
                  Delete
                </b-button>
              </b-col>
            </b-row>
          </b-col>
          <b-col cols="12">
            <hr />
          </b-col>
        </b-row>
      </ValidationObserver>
    </b-col>
  </div>
</template>

<script>
import { extend, ValidationProvider, ValidationObserver } from "vee-validate";
import {
  BRow,
  BCol,
  BFormGroup,
  BFormInput,
  BFormCheckbox,
  BButton,
  // BFormInvalidFeedback,
} from "bootstrap-vue";

export const validatorPrimaryContact = (chkValue, [contactDtos, id]) => {
  console.log(id);
  if (contactDtos === undefined || id === undefined) return true;

  if (chkValue) {
    contactDtos.forEach((x) => {
      if (x.id !== id) {
        // remove if multiple check boxes are checked.
        x.primaryContact = false;
      }
    });
    const contactCount = contactDtos.filter((x) => x.primaryContact === true);
    if (contactCount.length === 1) {
      return true;
    }
    return false;
  } else {
    const contactCount = contactDtos.filter((x) => x.primaryContact === true);
    if (contactCount.length === 0) {
      return false;
    } else {
      return true;
    }
  }
};

extend("chk_primary_contact", {
  validate: validatorPrimaryContact,
  message: "Please select a primary contact",
});

export default {
  name: "App",
  components: {
    ValidationProvider,
    ValidationObserver,
    BRow,
    BCol,
    BFormGroup,
    BFormInput,
    BFormCheckbox,
    BButton,
  },
  data() {
    return {
      contactDtos: [
        {
          id: 1,
          firstName: "",
          LastName: "",
          primaryContact: true,
        },
      ],
    };
  },
  methods: {
    contactFormRepeat() {
      const { length } = this.contactDtos;
      this.contactDtos.push({
        id: length + 1,
        firstName: "",
        lastName: "",
        contactEmail: "",
        contactPhone: "",
        primaryContact: false,
      });
    },
    removeContactForm(index) {
      this.contactDtos.splice(index, 1);
      if (this.contactDtos.length === 1) {
        this.contactDtos[0].primaryContact = true;
      }
    },
  },
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Please find the sandbox here.

Any help would be highly appreciated!


Solution

  • Did you find a solution for this yet? Since other check boxes other than the one you have clicked are not triggered, how about manually removing errors yourself?

    Give checkbox validation provider a ref and when checkbox is triggered remove other checkbox errors. Something like this...

    template:

    <ValidationProvider
                    :ref="`PrimaryContact${index}`"
                    #default="{ errors }"
                    :rules="{ chk_primary_contact: [contactDtos, item.id] }"
                    name="Primary contact"
                    vid="PrimaryContact"
                  >
                    <b-form-checkbox
                      v-model="item.primaryContact"
                      :checked="item.primaryContact"
                      :state="errors.length > 0 ? false : null"
                      @change="onChange(index)"
                    >
                      Primary contact
                    </b-form-checkbox>
                    <small class="text-danger">{{ errors[0] }}</small>
                  </ValidationProvider>
    

    `methods:

    onChange(ref) {
          console.info(ref);
          for (let i = 0; i < this.contactDtos.length; i++) {
            console.info(`i == ${i}`);
            if (i !== ref) {
              this.$refs[`PrimaryContact${i}`][0].errors = [];
            }
          }
        },