Skip to content
Snippets Groups Projects
ExtraForm.vue 13.5 KiB
Newer Older
    :class="['field', { 'disabled': field.disabled }]"
    :data-field_type="field.field_type"
    data-test="extra-form"
    name="extra-form"
    <div
      v-if="field && field.field_type === 'boolean'"
      :class="['ui checkbox', { 'disabled': field.disabled }]"
      <!-- JSON.parse is used in case of receiving a string 'true' or 'false'-->
        :checked="JSON.parse(field.value)"
        :name="field.name"
        @change="updateStore_extra_form"
      >
      <label :for="field.name">
        {{ displayLabels ? field.label : '' }}
      </label>
    </div>
    <template v-else>
      <label
        v-if="displayLabels"
        :for="field.name"
        :class="{ required: field.is_mandatory }"
      >
        {{ field.label }}
      </label>
      <input
        v-if="field && field.field_type === 'char'"
        :id="field.name"
        :value="field.value"
        type="text"
        :name="field.name"
        :required="field.is_mandatory"
        @blur="updateStore_extra_form"
      <textarea
        v-else-if="field && field.field_type === 'text'"
        :value="field.value"
        :name="field.name"
        :required="field.is_mandatory"
        rows="3"
        @blur="updateStore_extra_form"
      />

        v-else-if="field && field.field_type === 'integer'"
        type="number"
        :name="field.name"
Florent Lavelle's avatar
Florent Lavelle committed
        :required="field.is_mandatory"
        v-else-if="field && field.field_type === 'decimal'"
        :id="field.name"
        v-else-if="field && field.field_type === 'date'"
        :id="field.name"
Florent Lavelle's avatar
Florent Lavelle committed
        :required="field.is_mandatory"
      <Dropdown
        v-else-if="field && field.field_type === 'list'"
        :options="field.options"
        :selected="selected_extra_form_list"
        :selection.sync="selected_extra_form_list"
        :required="field.is_mandatory"
        :search="true"
        :clearable="true"
      />

      <div
        v-else-if="field && field.field_type === 'multi_choices_list'"
        class="checkbox_list"
      >
        <div
          v-for="option in field.options"
          :key="option.id || option"
            :id="option.id || option"
            :checked="field.value && field.value.includes(option.id || option)"
            :name="option.id || option"
          <label :for="option.id || option">
            {{ option.name || option }}
          </label>
        </div>
      </div>

      <Multiselect
        v-else-if="field && field.field_type === 'pre_recorded_list'"
        v-model="selectedPrerecordedValue"
        :options="selectedPrerecordedListValues[field.options[0]] || []"
        :options-limit="10"
        :allow-empty="!field.is_mandatory"
        track-by="label"
        label="label"
        :reset-after="false"
        select-label=""
        selected-label=""
        deselect-label=""
        :searchable="true"
        :placeholder="'Recherchez une valeur de la liste pré-définie ...'"
        :show-no-results="true"
        :loading="loadingPrerecordedListValues"
        :clear-on-select="false"
        :preserve-search="false"
        @search-change="search"
        @select="selectPrerecordedValue"
      >
        <template slot="clear">
          <div
            v-if="selectedPrerecordedValue"
            class="multiselect__clear"
            @click.prevent.stop="clearPrerecordedValue"
          >
            <i
              class="close icon"
              aria-hidden="true"
            />
          </div>
        </template>
        <span slot="noResult">
          Aucun résultat.
        </span>
        <span slot="noOptions">
          Saisissez les premiers caractères ...
        </span>
      </Multiselect>
    </template>
import { mapState, mapActions, mapMutations } from 'vuex';
import Multiselect from 'vue-multiselect';
import Dropdown from '@/components/Dropdown.vue';
Timothee P's avatar
Timothee P committed
  props: {
    field: {
      type: Object,
      default: null,
    useValueOnly: {
      type: Boolean,
      default: false,
Timothee P's avatar
Timothee P committed
    }
  },
Florent Lavelle's avatar
Florent Lavelle committed
  data() {
    return {
      prerecordedListSearchQuery: null,
      loadingPrerecordedListValues: false,
      selectedPrerecordedValue: null,
      selectedMultipleCheckbox: [],
    ...mapState('feature-type', [
      'selectedPrerecordedListValues'
    ]),
    ...mapState('feature', [
      'extra_forms',
    ]),
        return this.field.value || '';
      },
      set(newValue) {
        //* set the value selected in the dropdown
        if (this.useValueOnly) {
          this.$emit('update:value', newValue.id || newValue);
        } else {
          const newExtraForm = this.field;
          newExtraForm['value'] = newValue;
          this.$store.commit('feature/UPDATE_EXTRA_FORM', newExtraForm);
        }
      return this.$route.name === 'editer-signalement' || this.$route.name === 'ajouter-signalement' || this.$route.name === 'editer-attribut-signalement';
Florent Lavelle's avatar
Florent Lavelle committed
  watch: {
    /**
     * Watches for changes in the 'field.value' and updates the form state accordingly.
     * This watcher handles specific field types, ensuring their values are correctly initialized
     * and updated in scenarios like fast edition mode where certain values might not be auto-refreshed.
     *
     * @param {*} newValue - The new value of the field.
     * @param {*} oldValue - The previous value of the field before the change.
     */
    'field.value': function(newValue, oldValue) {
      if (this.field) {
        // Handle pre-recorded list fields specifically.
        if (this.field.field_type === 'pre_recorded_list') {
          // Update the form value if both new and old values are defined and different,
          // or if either value is undefined, indicating a change.
          if ((newValue && oldValue && (newValue.label !== oldValue.label || newValue !== oldValue))
              || !newValue || !oldValue) {
            this.initPrerecordedXform(); // Reinitialize the field to reflect the updated value.
          }
        } else if (this.field.field_type === 'multi_choices_list') {
          // For multi-choice lists, reinitialize the field if the array values have changed.
          // This is crucial in fast edition modes to prevent overriding the current value with a stale value.
          this.initMultipleCheckboxXform();
        this.error = null;
    prerecordedListSearchQuery(newValue) {
      this.loadingPrerecordedListValues = true;
      this.GET_SELECTED_PRERECORDED_LIST_VALUES({
        name: this.field.options[0],
        pattern: newValue
      })
        .then(() => {
          this.loadingPrerecordedListValues = false;
        })
        .catch(() => {
          this.loadingPrerecordedListValues = false;
        });
    }
      if (this.field.field_type === 'pre_recorded_list') {
        this.initPrerecordedXform();
      } else if (this.field.field_type === 'multi_choices_list') {
        this.initMultipleCheckboxXform();
    // autoset field to false if is a boolean, since user doesn't need to select it, when false value is expected
    if (this.field.field_type === 'boolean' && (this.field.value === undefined || this.field.value === null)) {
      this.updateStore_extra_form(false);
    ...mapActions('feature-type', [
      'GET_SELECTED_PRERECORDED_LIST_VALUES'
    ]),
    ...mapMutations('feature', [
      'UPDATE_EXTRA_FORM',
      'SET_EXTRA_FORMS',
    initMultipleCheckboxXform() {
      this.selectedMultipleCheckbox = typeof this.field.value === 'string' ? this.field.value.split(',') : this.field.value || [];
      const { options, value } = this.field;
      this.loadingPrerecordedListValues = true;
      this.GET_SELECTED_PRERECORDED_LIST_VALUES({
        name: options[0],
        pattern: ''
      })
        .then(() => {
          this.loadingPrerecordedListValues = false;
        })
        .catch(() => {
          this.loadingPrerecordedListValues = false;
        });
      if (value) {
        this.selectedPrerecordedValue = { label: value.label ? value.label : value };
      } else {
        this.selectedPrerecordedValue = null;
    /**
     * Updates the Vuex store or component state with the new value for a form field.
     * This function handles different types of form fields including boolean, multi-choice lists, and others.
     * 
     * @param {Event|*} val - The new value or an event object.
     */
    updateStore_extra_form(val) {
      // Check if the field object is defined.
      if (this.field) {
        // If the function is triggered by an event from a template input.
        if (val && val.target) {
          // For boolean fields (like checkboxes), use the 'checked' property.
          if (this.field.field_type === 'boolean') {
            // For other input types, use the 'value' property.
            newValue = val.target.value;
        } else if (this.field.field_type === 'multi_choices_list') {
          // For multi-choice lists, the value is stored in component state.
          newValue = this.selectedMultipleCheckbox;
        } else {
          // If the function is called directly with a value (not from an event).
          newValue = val;
        if (this.useValueOnly) {
          // If the component is used to update directly a returned value, emit an event with the new value.
          this.$emit('update:value', newValue);
          // Otherwise, update the Vuex store with the new value for the extra form field.
          this.UPDATE_EXTRA_FORM({ ...this.field, value: newValue });
Florent Lavelle's avatar
Florent Lavelle committed

    checkForm() {
      let isValid = true;
      if (this.field && this.field.is_mandatory && !this.field.value) {
Florent Lavelle's avatar
Florent Lavelle committed
        isValid = false;
        this.error = 'Ce champ est obligatoire';
      } else {
        this.error = null;
      }
      return isValid;
    search(text) {
      this.prerecordedListSearchQuery = text;
    },

    selectPrerecordedValue(e) {
      this.selectedPrerecordedValue = e;
      this.prerecordedListSearchQuery = null;
      this.updateStore_extra_form({ target: { value:  this.selectedPrerecordedValue.label } });
    
    clearPrerecordedValue() {
      this.selectedPrerecordedValue = null;
      this.prerecordedListSearchQuery = null;
      this.updateStore_extra_form({ target: { value:  null } });
    /**
     * Handles the selection and deselection of checkboxes in a form.
     * This function updates an array to track the selected checkboxes by their names.
     * It's typically called on the change event of each checkbox.
     * 
     * @param {Event} e - The event object from the checkbox input.
     */
      // Destructure the 'checked' status and 'name' of the checkbox from the event target.
      const { checked, name } = e.target;

      // If the checkbox is checked, add its name to the array of selected checkboxes.
      // Cloning the array to allow unsaved changes detection (it wasn't working with Array.push)
        this.selectedMultipleCheckbox = [...this.selectedMultipleCheckbox, name];
        // If the checkbox is unchecked, remove its name from the array.
        this.selectedMultipleCheckbox = this.selectedMultipleCheckbox.filter((el) => el !== name);
      }

      // Call a method to update the Vuex store or component state with the latest selection.
Florent Lavelle's avatar
Florent Lavelle committed
</script>
Florent Lavelle's avatar
Florent Lavelle committed

<style lang="less" scoped>
Florent Lavelle's avatar
Florent Lavelle committed
label.required:after {
	content: ' *';
	color: rgb(209, 0, 0);
}
.checkbox_list {
  display: flex;
  flex-direction: column;
  .ui.checkbox {
.multiselect__placeholder {
  position: absolute;
  width: calc(100% - 48px);
  overflow: hidden;
  text-overflow: ellipsis;
/* keep font-weight from overide of semantic classes */
.multiselect__placeholder, .multiselect__content, .multiselect__tags  {
  font-weight: initial !important;
}
/* keep placeholder eigth */
.multiselect .multiselect__placeholder {
  margin-bottom: 9px !important;
  padding-top: 1px;
}
/* keep placeholder height when opening dropdown without selection */
input.multiselect__input {
  padding: 3px 0 0 0 !important;
}
/* keep placeholder height when opening dropdown with already a value selected */
.multiselect__tags .multiselect__single {
  padding: 1px 0 0 0 !important;
  margin-bottom: 9px;
}
Florent Lavelle's avatar
Florent Lavelle committed
</style>