logaretm / vee-validate

✅ Painless Vue forms
https://vee-validate.logaretm.com/v4
MIT License
10.75k stars 1.26k forks source link

Submit doesn't work for show hide field. #3336

Closed shankhadevpadam closed 3 years ago

shankhadevpadam commented 3 years ago

Versions

Unable to submit the form.

I have a Vue component where some fields will show/hide according to choose radio option. The form is working perfectly when filling in all the data but it will be stuck if I hide the fields. When I set the airportPickUpStatus to arranged it will be stuck.

<template>
  <div class="vld-parent">
    <loading
      :active="isLoading"
      :height="64"
      :width="64"
      :color="'#0089BC'"
    ></loading>

    <Header :departure="departure"></Header>

    <div class="wizard" ref="wizard">
      <wizard-tab index="2"></wizard-tab>

      <div class="row" v-if="formValidationErrors">
        <div class="col-md-12">
          <div
            class="alert alert-danger alert-dismissible fade show"
            role="alert"
          >
            <p class="mb-0" v-html="formValidationErrors"></p>
            <button
              type="button"
              class="close"
              data-dismiss="alert"
              aria-label="Close"
              @click="formValidationErrors = ''"
            >
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
        </div>
      </div>

      <Form @submit="onSubmit" :validation-schema="schema" v-slot="{ errors }">
        <div class="wizard__content">
          <div id="wz-stepTwo" class="wizard__content__step">
            <div v-if="airportPickUpStatus === 'pickup'">
              <div class="form-group row">
                <div class="col-md-4">
                  <label>Arrival Date</label>
                  <date-picker
                    v-model="pickUp.arrivalDate"
                    valueType="format"
                    placeholder="Arrival Date"
                    :inputClass="{
                      'form-control': true,
                      'is-invalid': errors.arrivalDate,
                    }"
                  >
                  </date-picker>
                  <Field
                    name="arrivalDate"
                    type="text"
                    class="form-control"
                    :class="{ 'is-invalid': errors.arrivalDate }"
                    v-model="pickUp.arrivalDate"
                    v-show="false"
                  />
                  <ErrorMessage
                    as="div"
                    name="arrivalDate"
                    class="invalid-feedback"
                  />
                </div>
                <div class="col-md-4">
                  <label>Arrival Time</label>
                  <date-picker
                    v-model="pickUp.arrivalTime"
                    type="time"
                    format="hh:mm A"
                    valueType="format"
                    placeholder="Arrival Time"
                    :inputClass="{
                      'form-control': true,
                      'is-invalid': errors.arrivalDate,
                    }"
                  >
                  </date-picker>
                  <Field
                    name="arrivalTime"
                    type="text"
                    class="form-control"
                    :class="{ 'is-invalid': errors.arrivalTime }"
                    v-model="pickUp.arrivalTime"
                    v-show="false"
                  />
                  <ErrorMessage
                    as="div"
                    name="arrivalTime"
                    class="invalid-feedback"
                  />
                </div>
                <div class="col-md-4">
                  <label>Flight Number</label>
                  <Field
                    name="flightNumber"
                    type="text"
                    placeholder="Flight Number"
                    class="form-control"
                    :class="{ 'is-invalid': errors.flightNumber }"
                    v-model="pickUp.flightNumber"
                  />
                  <ErrorMessage
                    as="div"
                    name="flightNumber"
                    class="invalid-feedback"
                  />
                </div>
              </div>
              <hr />
              <div class="form-group row">
                <div class="col-md-6">
                  <p>
                    <strong
                      >Upload your recent photograph so that we can recognize
                      you.</strong
                    >
                  </p>
                  <div class="custom-file">
                    <Field
                      name="photograph"
                      type="file"
                      accept="image/*"
                      class="custom-file-input"
                      :class="{ 'is-invalid': errors.photograph }"
                      v-model="pickUp.photograph"
                      @change="handleFileUpload"
                    />
                    <label
                      class="custom-file-label"
                      for="inputGroupFile03"
                      v-text="pickUp.photograph.name || 'Choose file'"
                    ></label>
                    <ErrorMessage
                      as="div"
                      name="photograph"
                      class="invalid-feedback"
                    />
                  </div>
                </div>
              </div>
              <hr />
            </div>
            <div class="form-group">
              <p class="mb-0">
                <label class="radio">
                  <Field
                    name="airportPickUp"
                    type="radio"
                    value="pickup"
                    class="radio-input"
                    v-model="airportPickUpStatus"
                    @change="resetPickUpData"
                  />
                  <span class="radio-label">
                    Yes, pick me up from the airport upon arrival.</span
                  >
                </label>
              </p>
              <p class="mb-0">
                <label class="radio">
                  <Field
                    name="airportPickUp"
                    type="radio"
                    value="arranged"
                    class="radio-input"
                    v-model="airportPickUpStatus"
                    @change="resetPickUpData"
                  />
                  <span class="radio-label"> No, it is already arranged.</span>
                </label>
              </p>
            </div>
            <div class="form-group">
              <label class="checkbox">
                <input
                  type="checkbox"
                  class="checkbox-input"
                  v-model="intFlightBookStatus"
                />
                <span class="checkbox-label">
                  Select this option if your international flights are not
                  booked yet.</span
                >
              </label>
            </div>

            <div v-if="intFlightBookStatus">
              <hr />
              <p>
                <strong>Group members arriving on different flights?</strong>
              </p>
              <div
                v-for="(item, index) in groupArriving"
                :key="index"
                class="form-group row"
              >
                <div class="col-md-4">
                  <label>Arrival Date</label>
                  <date-picker
                    v-model="item.arrivalDate"
                    valueType="format"
                    placeholder="Arrival Date"
                    :inputClass="{
                      'form-control': true,
                      'is-invalid':
                        errors[`groupArriving[${index}].arrivalDate`],
                    }"
                  >
                  </date-picker>
                  <Field
                    :name="`groupArriving[${index}].arrivalDate`"
                    type="text"
                    v-model="item.arrivalDate"
                    :class="{
                      'is-invalid':
                        errors[`groupArriving[${index}].arrivalDate`],
                    }"
                    v-show="false"
                  />
                  <ErrorMessage
                    as="div"
                    :name="`groupArriving[${index}].arrivalDate`"
                    class="invalid-feedback"
                  />
                </div>
                <div class="col-md-4">
                  <label>Arrival Time</label>
                  <date-picker
                    v-model="item.arrivalTime"
                    type="time"
                    format="hh:mm A"
                    valueType="format"
                    placeholder="Arrival Time"
                    :inputClass="{
                      'form-control': true,
                      'is-invalid':
                        errors[`groupArriving[${index}].arrivalTime`],
                    }"
                  >
                  </date-picker>
                  <Field
                    :name="`groupArriving[${index}].arrivalTime`"
                    type="text"
                    :class="{
                      'is-invalid':
                        errors[`groupArriving[${index}].arrivalTime`],
                    }"
                    v-model="item.arrivalTime"
                    v-show="false"
                  />
                  <ErrorMessage
                    as="div"
                    :name="`groupArriving[${index}].arrivalTime`"
                    class="invalid-feedback"
                  />
                </div>
                <div class="col-md-4">
                  <label>Flight Number</label>
                  <Field
                    :name="`groupArriving[${index}].flightNumber`"
                    type="text"
                    class="form-control"
                    placeholder="Flight Number"
                    :class="{
                      'is-invalid':
                        errors[`groupArriving[${index}].flightNumber`],
                    }"
                    v-model="item.flightNumber"
                  />
                  <ErrorMessage
                    as="div"
                    :name="`groupArriving[${index}].flightNumber`"
                    class="invalid-feedback"
                  />
                </div>
              </div>

              <button
                type="button"
                class="btn btn-primary btn-sm"
                @click="addFlight"
              >
                Add flight details
              </button>
            </div>

            <hr />

            <div v-if="airportPickUpStatus === 'pickup'">
              <p>
                Magical Nepal representative will pick you on
                <span
                  class="text-colored"
                  v-text="formatDate(pickUp.arrivalDate)"
                ></span>
                at
                <span class="text-colored" v-text="pickUp.arrivalTime"></span>
                arriving with flight
                <span class="text-colored" v-text="pickUp.flightNumber"></span>.
                Look at your name card
                <span class="text-colored" v-text="booker"></span>
                outside our arrival terminal.
              </p>
            </div>
            <p>
              If any issues contact {{ helpText }}
            </p>

            <button class="btn btn-primary btn-wizard" type="submit">
              Go to final step
            </button>
          </div>
          <!-- END Step 2 -->
        </div>
      </Form>
    </div>
  </div>
</template>

<script>
import Header from "../components/steps/Header.vue";
import WizardTab from "../components/steps/WizardTab.vue";
import { Form, Field, ErrorMessage } from "vee-validate";
import * as Yup from "yup";
import DatePicker from "vue2-datepicker";
import "vue2-datepicker/index.css";
import Loading from "vue3-loading-overlay";
import "vue3-loading-overlay/dist/vue3-loading-overlay.css";
import { format } from "date-fns";
import Api from "../api/api";

export default {
  name: "StepTwo",

  components: {
    Header,
    WizardTab,
    Form,
    Field,
    ErrorMessage,
    DatePicker,
    Loading,
  },

  data() {
    const FILE_SIZE = 2048 * 1024;
    const SUPPORTED_FORMATS = [
      "image/jpg",
      "image/jpeg",
      "image/gif",
      "image/png",
    ];

    const schema = Yup.object().shape({
      arrivalDate: Yup.string().required("Arrival date is required."),
      arrivalTime: Yup.string().required("Arrival time is required."),
      flightNumber: Yup.string().required("Flight number is required."),
      photograph: Yup.mixed()
        .test(
          "fileSize",
          "File too large",
          (value) => value && value.size <= FILE_SIZE
        )
        .test(
          "fileFormat",
          "Unsupported format",
          (value) => value && SUPPORTED_FORMATS.includes(value.type)
        ),
      groupArriving: Yup.array().of(
        Yup.object().shape({
          arrivalDate: Yup.string().required("Arrival date is required."),
          arrivalTime: Yup.string().required("Arrival time is required."),
          flightNumber: Yup.string().required("Flight number is required."),
        })
      ),
    });

    return {
      schema,
      departure: {
        package: {},
      },
      isLoading: false,
      airportPickUpStatus: "pickup",
      intFlightBookStatus: false,
      booker: "",
      helpText: "",
      pickUp: {
        arrivalDate: "",
        arrivalTime: "",
        flightNumber: "",
        photograph: "",
      },
      groupArriving: [
        {
          arrivalDate: "",
          arrivalTime: "",
          flightNumber: "",
        },
      ],
      formValidationErrors: "",
    };
  },

  mounted() {
    this.getDeparture();
    this.getBooker();
    this.getHelpContact();
  },

  methods: {
    async getDeparture() {
      try {
        const params = new URLSearchParams(window.location.search);

        const response = (
          await Api.get(`/packages/departures/${params.get("did")}`)
        ).data.data;

        this.departure = response;
      } catch (error) {
        console.log(error.response);
      }
    },

    async getBooker() {
      try {
        const params = new URLSearchParams(window.location.search);

        const response = (
          await Api.get(`/booking/booker?token=${params.get("token")}`)
        ).data.data;

        this.booker = response.name;
      } catch (error) {
        console.log(error.response);
      }
    },

    async getHelpContact() {
      try {
        const response = (
          await Api.get(`/booking/help-contact?name=help_contact`)
        ).data.data;

        this.helpText = response.value;
      } catch (error) {
        console.log(error.response);
      }
    },

    handleFileUpload(e) {
      this.pickUp.photograph = e.target.files[0];
    },

    addFlight() {
      this.groupArriving.push({
        arrivalDate: "",
        arrivalTime: "",
        flightNumber: "",
      });
    },

    resetPickUpData() {
      this.pickUp = {
        arrivalDate: "",
        arrivalTime: "",
        flightNumber: "",
        photograph: "",
      };
    },

    formatDate(date) {
      if (!date) return;

      return format(new Date(date), "do MMMM yyyy");
    },

    async onSubmit() {
      this.isLoading = true;
      this.formValidationErrors = "";

      const url = new URL(window.location);
      const params = new URLSearchParams(window.location.search);

      let formData = new FormData();
      formData.append("airport_pickup_status", this.airportPickUpStatus);
      formData.append("arrival_date", this.pickUp.arrivalDate);
      formData.append("arrival_time", this.pickUp.arrivalTime);
      formData.append("flight_number", this.pickUp.flightNumber);
      formData.append("photograph", this.pickUp.photograph);
      formData.append("int_flight_status", this.intFlightBookStatus);
      formData.append("group_dates", JSON.stringify(this.groupArriving));
      formData.append("departure_id", params.get("did"));
      formData.append("token", params.get("token"));

      try {
        const response = await Api.post("/booking/dates", formData, {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        });

        params.set("step", 3);
        params.set("token", response.data.data.token);
        params.set("num_of_trekkers", response.data.data.number_of_trekkers);

        window.location.href =
          url.origin + url.pathname + "?" + params.toString();
      } catch (error) {
        let errorMsg = "";

        for (const [key, value] of Object.entries(error.response.data.errors)) {
          value.forEach((element) => {
            errorMsg = errorMsg + element + "<br>";
          });
        }

        this.formValidationErrors = errorMsg;

        this.$refs.wizard.scrollIntoView({ behavior: "smooth" });

        this.isLoading = false;
      }
    },
  },
};
</script>

<style scoped>
.mx-datepicker {
  width: 100%;
  display: block;
}
</style>
logaretm commented 3 years ago

This is intended in 4.2 since there was a schema strictness issue that was fixed.

Your schema defines all fields as required, so that's why it is not submitting the form, you have two options to resolve this:

You can debug this by printing the errors of the form whenever the submit seems "stuck".

shankhadevpadam commented 3 years ago

@logaretm same problem even used conditionally. I made the airportPickUpStatus as a boolean in my code.

const schema = Yup.object().shape({
      airportPickUpStatus: Yup.boolean(),
      arrivalDate: Yup.string().when('airportPickUpStatus', {
        is: true,
        then: Yup.string().required("Arrival date is required."),
      }),
});
logaretm commented 3 years ago

Could you please create a demo for this on codesandbox? try to keep it as minimal as possible as I can't go through your large snippet.

shankhadevpadam commented 3 years ago

@logaretm I have created a codesandbox for my issue https://codesandbox.io/s/vee-yup-1smlj

Now my problem is when I choose pickup and fill the form it submitted but when I choose arranged it doesn't submit. I need to submit my form if I choose arranged.

So it means if I choose pickup field should be validated but not when I choose arranged.

logaretm commented 3 years ago

Well you didn't specify any conditions in your schema in your example, computed schema resolves it:

https://codesandbox.io/s/vee-yup-forked-qqujv?file=/src/App.vue

shankhadevpadam commented 3 years ago

@logaretm thanks for the solution.