【问题标题】:How to get the data from a dynamically generated form in VUE?如何从 VUE 中动态生成的表单中获取数据?
【发布时间】:2021-05-16 13:25:14
【问题描述】:

你好社区

我有一个包含子组件的父组件,其中具有不同控件的表单从 JSON 对象(通过 Axios 的 Get 请求获得)动态呈现。

我的目标是从父组件读取/循环在子组件中找到的所有表单字段值。我将如何使用 Vue JS 来做到这一点?

这是代码的一部分,如果有人提出改进建议,他们将受到好评。例如,更好地组织我的代码,使其更有条理和干净,或者使用 Vue 的最佳编程实践。谢谢。

父组件

<template>
  <div id="app">
    <h1>{{msg}}</h1>
    <b-container>
      <b-card>
        <b-card-title>Formulario Dinámico</b-card-title>
        <b-card-body>
           <FormControls :fields="fields"></FormControls>
        </b-card-body>
        <b-card-footer>
          <button class="btn btn-primary" @click="send">Enviar</button>
        </b-card-footer>
      </b-card>
    </b-container>
  </div>
</template>

<script>
import FormControls from "./FormControls.vue";
import ComponentTest from "./ComponentTest.vue";
import axios from 'axios';

export default {
  name: 'app',
  components: {
    FormControls,
    ComponentTest
  },
  created() { 
    axios.get('./src/form.json').then(response => this.fields = response.data); 
  },
  data () {
    return {
      msg: 'Bienvenido',
      fields: [] // Array que almacenará el json proveniente de la petición get
    }
  },
  methods: {
    send: () => {
      alert('Enviar Formulario');
    }
  }
}

</script>

JSON

[
    {
      "name": "fechaRegistro",
      "label": "Fecha de Registro",
      "type": "date",
      "placeholder": "Ingresa Fecha"
    },
    {
      "name": "nombreDeUsuario",
      "label": "Nombre de Usuario",
      "type": "text",
      "placeholder": "Ingresa Usuario"
    },
    {
      "name": "passwordUsuario",
      "label": "Password",
      "type": "password",
      "placeholder": "Contraseña"
    },
    {
      "name": "adjuntarArchivo",
      "label": "Adjuntar",
      "type": "file"
    },
    {
      "name": "roles",
      "label": "Roles",
      "type": "select",
      "sortedByKey": false,
      "options": [{
          "name": "admin",
          "label": "Administrador"
        },
        {
          "name": "user",
          "label": "Usuario"
        },
        {
          "name": "guest",
          "label": "Invitado"
        }
      ]
    },
    {
      "name": "description",
      "label": "Descripción",
      "type": "textarea"
    },
    {
      "name": "multiSelect",
      "label": "Selección Multiple",
      "type": "multiselect",
      "options": [{
          "name": "op1",
          "label": "Opcion1"
        },
        {
          "name": "op2",
          "label": "Opcion2"
        },
        {
          "name": "op3",
          "label": "Opcion3"
        }
      ]
    }
  ]

子组件

<template>
  <div>
  <form>
    <ul>
      <li v-for="field in fields" :key="field">
        <label :for="field.name">{{field.label}}</label>

        <input v-if="isInput(field.type)" 
        :id="field.name" :type="field.type" :placeholder="field.placeholder" >

        <select v-else-if="field.type === 'select'" :name="field.name">
          <option v-for="opt in field.options" :key="opt" :value="opt.name">
          {{opt.label}}
          </option>
        </select>

        <textarea v-else-if="field.type === 'textarea'" :id="field.name" />
        
        <div v-else-if="field.type === 'multiselect'" class="multi-select">
          <multiselect v-model="values" tag-placeholder="Agregar etiqueta" :placeholder="field.placeholder" label="label" track-by="name" :options="field.options" :multiple="true" :taggable="true" @tag="agregarEtiqueta"></multiselect>
        </div>

      </li>
    </ul>
  </form>
  </div>
</template>

<script>
export default {
  props: ['fields'],
  name: 'FormControls',
  data () {
    return {
      titulo: 'Formulario Dinámico',
      // Aqui va lo del MultiSelect
      values: [],
      options: []
    }
  },
  methods: {
    isInput(type) {
      return ['text', 'password', 'checkbox', 'file', 'date'].includes(type);
    },
    // metodo multiselect
    agregarEtiqueta (newTag) {
      const tag = {
        name: newTag,
        label: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
      }
      this.options.push(tag)
      this.value.push(tag)
    },
    devolverControl(){

    }
  }
}
</script>

【问题讨论】:

  • 子组件循环遍历自己的字段可能更有意义。它可以发射一个带有任何结果的对象。
  • 你可以看看 Vue 公式库,它非常灵活,可以从 Json 模式生成表单。

标签: javascript html json vue.js templates


【解决方案1】:

您不应该干预来自父母的孩子的数据。那将是一个紧密的耦合。尝试“哑组件”样式,您的inputs 只是显示组件并接受道具并向父级发送数据。这样您就可以以任何您想要的方式验证父级中的数据。

更复杂一点的解决方案是在“哑组件”中创建错误处理程序,并且只有在它们自身检测到错误时才返回“错误”。另一种风格可能是在您的 fields 数据集中创建验证函数,并仅传递函数本身(更多的是 React 而不是 Vue 方法 - 完全有效且有效JS,只是不是真正的 Vue 风格)。

在下面的 sn-p 中,我只是在字段项中添加了 valueerror,因此它们完全存在于父组件中。

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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    • 1970-01-01
    相关资源
    最近更新 更多