Search code examples
javascriptvue.jsvuelidate

end date must be less than start date, Vuelidate and VueJs


I'm having problems doing a form validation. I have a start date and an end date. I need to confirm that the end date is greater than the start date. I'm using vuejs and vuelidate. I'm working with an Input.vue component, which has a standard input being reused in other forms. I also have a Form.vue component that has Input.vue inside. And this Form.vue component is called both in the modal of creating a new event and in the editing mode. My problem is in the validation of when the event is edited. If you select an end date less than the start date, it doesn't allow saving the information, that's ok. However, when trying to correct it by selecting a date greater than the start date, and clicking on save again, it seems that it is no longer calling validation. With this, the error that the end date must be greater than the initial one does not disappear. I did several tests and noticed that validation is not called after the error occurs when editing. I do not know why.

component Input.vue:

 <template>
  <b-form-group :label="label" :label-for="labelFor">
    <b-form-input
      :id="labelFor"
      v-model="value"
      :type="type"
      :placeholder="placeholder"
      :class="{ 'is-invalid': this.isInvalid }"
      required>
    </b-form-input>
    <slot></slot>
  </b-form-group>
</template>

<script>
export default {
  name: 'InputForm',
  props: [ 'label', 'labelFor', 'vModel', 'type', 'placeholder', 'isInvalid' ],  
  computed: {
    value: {
      get() {
        return this.vModel
      },
      set(value) {
        this.newValue = value
        return this.$emit('emitValue', this.newValue)
      }
    }
  },
}
</script>

component Form.vue:

<template>
  <b-form>
....
<InputForm
          class="col-8 pl-0"
          :label="'*Início'"
          :labelFor="'event_start_at_date'"
          :vModel="event.start_at_date"
          :type="'date'"
          @emitValue="($event) => event.start_at_date = $event"
        />
        <InputForm
          class="col-4 pr-0"
          v-if="!event.all_day"
          :labelFor="'event_start_at_time'"
          :vModel="event.start_at_time"
          :type="'text'"
          @emitValue="($event) => event.start_at_time = $event"
        />
      </b-col>
      <b-col cols="12" lg="6" class="d-flex align-items-end">        
        <InputForm
          class="col-8 pl-0"
          :label="'*Fim'"
          :labelFor="'event_end_at_date'"
          :type="'date'"
          :vModel="event.end_at_date"
          @emitValue="($event) => event.end_at_date = $event"
          :isInvalid="invalid.end_at_date.$error"
        >
          <b-form-invalid-feedback v-if="this.submitted" :state="invalid.end_at_date.$error == 0">Fim deve ser maior que início.</b-form-invalid-feedback>
        </InputForm>      
        <InputForm
          class="col-4 pr-0"
          v-if="!event.all_day"
          :labelFor="'event_end_at_time'"
          :vModel="event.end_at_time"
          :type="'text'"
          :isInvalid="invalid.end_at_date.$error"
          :class="{ 'error' : invalid.end_at_date.$error  }"
          @emitValue="($event) => event.end_at_time = $event"
        />
...
 </b-form>
</template>

export default {
  name: 'Form',
  props: [ 'event', 'event_shared', 'error', 'submitted' ],
  components: { Input },
computed: {
    invalid() {
      if(this.error.edit_event != undefined) {
        return this.error.edit_event
      } else if(this.error.event != undefined) {
        return this.error.event
      } else {        
        return false;
      }
    },
    stateInvalid() {
      if(this.error.edit_event != undefined) {
        return this.error.edit_event
      } else if(this.error.event != undefined) {
        return this.error.event
      } else {        
        return 0;
      }
    },
  },
watch: {
    event() {
      let newEvent = this.event
      this.$emit('emitObj', newEvent)
    },
  },
}
</script>

component ModalEditEvent.vue:

    ...
<VWarningErrorForm v-if="$v.$error" />
            
            <FormEvent
              :event="edit_event"
              :event_shared="get_shared_users_id"
              :error="this.$v"
              :submitted="this.submitted"
              @emitObj="($event) => edit_event = $event"
            />
...
<b-button 
          v-if="editEvent && !view_popover_save"
          variant="primary" type="submit" @click="save()">Gravar</b-button>
...
<script>
import moment from 'moment';
import { required } from "vuelidate/lib/validators";
import isAfterDate from '@/functions/isAfterDate'
...
validations: {
    edit_event: {
      name: {
        required
      },
      end_at_date: {
        required: function(value) {
          return isAfterDate(value, this.edit_event);
        }
      },
    },
  },
...
save() {
      this.submitted = true;
      this.$v.$touch();
      if(!this.$v.$error) {
....
      }      
    },

validation

import moment from 'moment';

function isAfterDate(value, vm) {
  let hour =  function(att) {    
    return `${att[0]}${att[1]}`;
  }
  let minute = function(att) {
    return `${att[3]}${att[4]}`;
  }
  
  let start = moment(vm.start_at_date).set({hour: hour(vm.start_at_time), minute: minute(vm.start_at_time) })._d
  let end = moment(vm.end_at_date).set({hour: hour(vm.end_at_time), minute: minute(vm.end_at_time) })._d

  return end >= start;
}

export default isAfterDate;

Solution

  • Solved my issue with Vue.set. I realized that I was trying to detect the change of a property that I added later myself. So I had to use Vue.set when adding the property, so I could detect the change later, and then correctly validate the form. Thanks to whoever tried to help me!