[Vuejs]-How to get the data from a dynamically generated form in VUE?

0👍

You shouldn’t be meddling with children’s data from the parent. That would be a tight coupling. Try the "dumb component" style, where your inputs are only display components & accept props & emit data to the parent. This way you can validate the data in the parent, in any way you want.

A bit more complex solution would be to create the error-handlers inside your "dumb components", and only emit back "error" if they detect errors in themselves. Another style could be to create the validation functions in your fields dataset & just pass down the functions themselves (more of a React than a Vue approach – totally working & valid JS, just not really Vue-style).

In the snippet below I just added value & error to the field items, so they are fully present in the parent component.

Vue.component("InputPassword", {
  props: ['field'],
  computed: {
    value: {
      get() {
        return this.field.value
      },
      set(value) {
        this.$emit("update:value", {
          value
        })
      }
    },
  },
  template: `
    <div>
      <label>{{ field.name }}: <input type="password" v-model="value" /></label>
      <span
        v-if="field.error"
      >
        THIS FIELD HAS ERRORS
      </span>
    </div>
  `
})

Vue.component("InputText", {
  props: ['field'],
  computed: {
    value: {
      get() {
        return this.field.value
      },
      set(value) {
        this.$emit("update:value", {
          value
        })
      }
    },
  },
  template: `
    <div>
      <label>{{ field.name }}: <input type="text" v-model="value" /></label>
      <span
        v-if="field.error"
      >
        THIS FIELD HAS ERRORS
      </span>
    </div>
  `
})

new Vue({
  el: "#app",
  data() {
    return {
      fields: [{
          "name": "nombreDeUsuario",
          "label": "Nombre de Usuario",
          "type": "text",
          "placeholder": "Ingresa Usuario",
          "value": null,
          "error": null,
        },
        {
          "name": "passwordUsuario",
          "label": "Password",
          "type": "password",
          "placeholder": "Contraseña",
          "value": null,
          "error": null,
        },
      ]
    }
  },
  computed: {
    isFormValid() {
      return this.fields.every(({
        error
      }) => error == false) ? "YES" : "NO"
    }
  },
  methods: {
    handleValidateForm() {
      this.fields = this.fields.map(field => {
        return {
          ...field,
          error: !(!!field.value) // valid if not empty
        }
      })
    },
  },
  template: `
    <div>
      <form>
        <component
          v-for="field in fields"
          v-bind:is="'input-' + field.type"
          v-bind="{
            field
          }"
          v-on="{
            'update:value': ({ value }) => {
              field.value = value
            },
          }"
        ></component>
      </form>
      <div
        v-for="(field, i) in fields"
        :key="'display-field-' + i"
      >
        field name: {{ field.name }}<br />
        field value: {{ field.value }}
      </div>
      <hr />
      <button
        @click="handleValidateForm"
      >
        VALIDATE
      </button>
      IS FORM VALID? {{ isFormValid }}
    </div>
  `
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<div id="app"></div>

Leave a comment