Nr.funf
Nr.funf

Reputation: 21

How to bind dynamically added form input with vue.js

I want to render some forms and bind them to Vue. forms are listed on json Like this.

formslist:[
    "<input type='text' name='item1' v-model='item1'>",
    "<input type='text' name='item2' v-model='item2'>",
]

I could render them but couldn't bind. Template is this.

<div v-for="form in formslist">
    <div v-html="form"></div>
</div>

Later, I understood that html added by "v-html" can't use data bindings. But then I don't have no ideas how to do this. Would anyone teach me good solution?


Sorry for lack of words. I wrote code like this but "item1" and "item2" are not binded.

js:

new Vue({
  el: '#app',
  data() {
    return {
      formData: {},
      formslist:
        // this list is got by ajax as neccessary
        {
            "item1": "<input type='text' name='item1' v-model='formData.item1'>",
            "item2": "<input type='text' name='item2' v-model='formData.item2'>",
        }
    };
  },
  methods: {
    submit() {
      alert('formdata: ' +  JSON.stringify(this.formData));

      // submit data to backend
      // e.g. axios.post(...).then(...).catch(...);
    }
  }
})

html:

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
  <form name="myForm" @submit.prevent="submit">
    <div class="form-group">
      <label>Firstname</label>
      <input class="form-control" v-model="formData.firstName" type="text">
    </div>
    <div class="form-group">
      <label>Lastname</label>
      <input class="form-control" v-model="formData.lastName" type="text">
    </div>
    <div class="form-group">
      <label>Description</label>
      <textarea class="form-control" v-model="formData.description"></textarea>
    </div>

    <div class="form-group" v-for="(form, name) in formslist">
      <label>{{name}}</label>
      <div v-html="form"></div>
    </div>

    <button type="submit">
      Submit
    </button>
  </form>
</div>

Upvotes: 2

Views: 2762

Answers (1)

AWolf
AWolf

Reputation: 8990

Vue.js is model/data oriented - have a look at the docs to learn more about it.

So create a model where you're storing your form data and then use that model to submit the data or do anything you like with the data.

Please have a look at a basic example below or at this fiddle.

Update 01.07.2017

OK, now I understand what you're trying to do. Please find my updated code below or at the fiddle (same link).

You could also use a form generator like vue-form-generator. It's doing the same as my demo - just fewer coding required.

How does it work?

The form will be rendered with a template that is getting the data from a model that you can get from the server.

The type of the input can't be bound with :type because that's not supported with v-model - there was an error message when I tried that. So it's a bit more to write but it should be clear how this works. Add similar template tags for other types.

To further improve the model, you could also store the top-level of your form, so you can customize that display class as well. e.g. something like:

{
  formName: 'myForm',
  className: 'form-horizontal',
  items: [...] // same array as before
}

If you need to group some inputs or display them differently you can add more properties to the model and more logic to the template.

const formRenderModel = [{
  type: 'text',
  modelName: 'firstName',
  className: 'form-control',
  labelText: 'First name',
  required: true
}, {
  type: 'text',
  modelName: 'lastName',
  className: 'form-control',
  labelText: 'Last name',
  required: true
}, {
  type: 'text',
  modelName: 'description',
  className: 'form-control',
  labelText: 'Description',
  required: true,
  pattern: '.{10,}', // minimum 10 characters
  validationMessage: 'Minimum 10 characters required'
}];

new Vue({
  el: '#app',
  data() {
    return {
      formData: {}
    };
  },
  created() {
    // later axios.get('/form-render).then(...).catch(...);
    // now just a constant
    this.formRenderModel = formRenderModel;
  },
  methods: {
    submit() {
      console.log('formdata', this.formData);

      // submit data to backend
      // e.g. axios.post(...).then(...).catch(...);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
  <form name="myForm" @submit.prevent="submit">
    <div v-for="inputModel in formRenderModel">
      <label>{{inputModel.labelText}}</label>
      <template v-if="inputModel.type == 'text'">
        <input type="text" v-model="formData[inputModel.modelName]"     
          :class="inputModel.className" 
          :pattern="inputModel.pattern"  
          :title="inputModel.validationMessage"
          :required="inputModel.required" />
      </template>
      <!-- add more templates for radios, checkboxes, .. -->
    </div>
    <button type="submit">
      Submit
    </button>
  </form>
</div>

Upvotes: 1

Related Questions