3
votes

How can I validate, using VeeValidate v3 and VueJs-DatePicker, if a date is before or after a certain date?

I am using: https://www.npmjs.com/package/vuejs-datepicker (latest) and https://baianat.github.io/vee-validate/ (Version 3, not 2)

VeeValidate removed its date validation capabilities, stating that the rules were fragile, didn't work great, etc.

So I am assuming I need to create custom rules but I don't really know where to start with this and could use help.

I am using the VueJS-DatePicker package as a datepicker and I am using a custom format via the "format" attribute.

My datepicker code

<datepicker
    v-model="inputVal"
    :class="{ 'has-errors': !!errors[0] }"
    :placeholder="placeholder"
    calendar-button
    calendar-button-icon="fal fa-calendar-alt"
    bootstrapStyling
    :typeable="true"
    :disabled-dates="disabledDates"
    :format="customFormatter"
></datepicker>

Using Moment to format the date

customFormatter(date) {
    return moment(date).format('MM/DD/YYYY');
},
2

2 Answers

1
votes
  1. Create your custom rule using extend

For example, this is my own custom rule to check if it is enough items in collection:

  • value below is a value from input (unfortunately, vee-validate works only with input)
  • min is a value after semicolon in rules prop of ValidationProvider
    extend('minAmountItems', {
        validate: (value, { min }) => {
            return value >= min;
        },
        params: ['min'],
        message: '{_field_} should contains at least {min} items'
    });
  1. Wrap you datepicker by ValidationProvider

For example, I wrapped my own component by ValidationProvider:

HTML

    <ValidationProvider
        ref="editableListProvider"
        tag="div"
        rules="minAmountItems:2"
        v-slot="{errors,invalid}"
        name="Collection"
    >
        <!-- LINE BELOW IS VERY IMPORTANT. -->
        <!-- THIS INPUT VALUE GOES TO VALIDATE FUNCTION OR CUSTOM RULE -->
        <input type="text" :value="items.length" disabled v-show="false" />
        <div class="column add-item-column">
            <button @click="addItem">Add item</button>
        </div>
        <div class="column alert alert-danger" v-show="invalid">{{ errors[0] }}</div>
        <div class="column" v-for="(item, i) in items" :key="i">
            <div class="row as-row row-no-padding">
                <slot name="data" :data="{item, index: i}"></slot>
                <div class="column column-clear">
                    <button class="button-outline button-icon" @click="removeItem(i)">
                        <i class="la la-close"></i>
                    </button>
                </div>
            </div>
        </div>
    </ValidationProvider>

JS

        // another part of my component

        methods: {
            addItem() {
                this.$emit('editableList:itemAdded');

                this.$nextTick(async () => {
                    // LINE BELOW IS IMPORTANT, BECAUSE AUTOMATIC VALIDATE WORKS 
                    // ONLY ONCHANGE EVENT OF INPUT
                    this.$refs.editableListProvider.validate();
                });
            },
            removeItem(index) {
                this.$emit('editableList:itemRemoved', { index });

                this.$nextTick(async () => {
                    // LINE BELOW IS IMPORTANT, BECAUSE AUTOMATIC VALIDATE WORKS 
                    // ONLY ONCHANGE EVENT OF INPUT
                    this.$refs.editableListProvider.validate();
                });
            }

            // other my methods
        }
0
votes

expanding on @Barabas answer to specifically include a) before a given date b) the momentJS library c) the strict date format 'MM/DD/YYYY'

import { extend } from "vee-validate";
import * as moment from 'moment';

const dateFormat = 'MM/DD/YYYY';

extend("before", {
  params: ["limit", "included"],
  validate: (value, { limit, included }) => {
    limit = moment(limit, dateFormat);
    value = moment(value, dateFormat);
    return included 
      ? value.isSameOrBefore(limit)
      : value.isBefore(limit);
  },
  message: (fieldName, placeholders) => {
    let limit = moment(placeholders.limit).format(dateFormat);
    return `The ${fieldName} field must come before ${limit}`;
  }});
}

and without moment (with the caveat that different localization setting in the users browser could result in funkiness when using strings for either a Vue data property or the limit parameter - I would strongly suggest using 'YYYY-MM-DD' format if using strings - this does not have to be what is shown to the user of course)

extend("before", {
  params: ["limit", "included", "displayTime"],
  validate: (value, { limit, included }) => {
    value = isNaN(value)
      ? Date.parse(value)
      : +value;
    limit = isNaN(limit)
      ? Date.parse(limit)
      : +limit;
    return included 
      ? value <= limit
      : value < limit;
  },
  message: (fieldName, placeholders) => {
    let limit = placeholders.limit instanceof Date
      ? placeholders.limit
      : new Date(placeholders.limit);
    limit = placeholders.displayTime
      ? limit.toLocaleString()
      : limit.toLocaleDateString();
    return `The ${fieldName} field must come before ${limit}`
  }});
}