[Vuejs]-Generating input fields and saving entered values dynamically with VueJS & VuetifyJS

0👍

I’d argue your question is not related to Vuetify.
As in, you’d have the same exact problem using BootstrapVue or Vue with plain HTML inputs. In fact, it barely has anything to do with Vue, as you’d have the same problem in any other library using two way bindings on input elements.


First and foremost, :value and v-model are incompatible. You either use one or the other. :value sets the value when component is mounted, while v-model provides 2-way data binding. You clearly want v-model.

Then, you want to create a dynamic form from an arbitrary structure containing either collections of items or items.
This can be solved using a recursive component (which either renders an input if current node is not a collection or a v-for of recursive components if node is a collection). While that model does work, it reaches a high level of complexity which makes debugging a pain. For less than 3 levels of recursion it’s not worth the trouble, IHMO.

Here’s a practical way to solve your case:

Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
  el: '#app',
  data: () => ({
    refJSON: jsonData(),
    extraction: nullify(jsonData())
  }),
  methods: {
    getInputType(type) {
      return type.replace('Field', '')
    }
  }
})

function nullify(obj) {
  for (const prop in obj) {
    obj[prop] = typeof obj[prop] === 'object' ?
      nullify(obj[prop]) :
      null
  }
  return obj;
}

function jsonData() {
  return {
    "documentAttributes": {
      "documentNumber": "textField",
      "issueDate": "dateField",
      "issuingAuthority": "textField",
      "dynamicProperties": {
        "recordedCityOfIssue": "textField",
        "recordedStateOrProvinceOfIssue": "textField",
        "recordedPaperNumber": "textField",
        "recordedFamilyNumber": "textField",
        "recordedOrderNumber": "textField",
        "issueRecordNumber": "textField"
      }
    },
    "personalAttributes": {
      "socialNumber": "textField",
      "surname": "textField",
      "name": "textField",
      "paternalName": "textField",
      "maternalName": "textField",
      "placeOfBirth": "textField",
      "dateOfBirth": "dateField",
      "maritalStatus": "textField",
      "gender": "textField"
    }
  }
}
label {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
input {
  margin-left: .5rem;
}
  #app {
    display: flex;
    flex-direction: row-reverse;
    justify-content: space-between;
    flex-wrap: wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<div id="app">
  <div v-for="(a, b) in refJSON" :key="b">
    <div v-for="(c, d) in a">
      <label v-if="typeof c === 'string'">
        <pre>{{`${b}.${d}`}}</pre>
        <input  v-model="extraction[b][d]" :type="getInputType(c)" />
      </label>
      <div v-else>
        <label v-for="(e, f) in c">
          <pre>{{`${b}.${d}.${f}`}}</pre>
          <input v-model="extraction[b][d][f]" :type="getInputType(e)" />
        </label>
      </div>
    </div>
  </div>
  <pre v-html="extraction" />
</div>

The model structure is provided by the nullify function, from the JSON source). Obviously, you can tweak/improve it to provide default values based on type.

Leave a comment