1

I followed the solution to create dynamic form elements here: Dynamically adding different components in Vue

Works great, my next conundrum is now I want to remove form elements if the user adds too many.

The way it works is the user creates a "set" which is defined as a group of inputs. So each set could be for a different person, or place, etc.

Here is my JSfiddle https://jsfiddle.net/61x784uv/

Html

<div id="component-pii-input" v-for="field in fields" v-bind:is="field.type" :key="field.id">
</div>
<button id='button-add-pii-component' v-on:click="addFormElement('pii-entry-field')">Add Set</button>

</div>

Javascript

Vue.component('pii-entry-field', {
  data: function () {
    return {
fields: [],
    count: 0,
    }
  },
     methods: {
      addFormElement: function(type) {
      this.fields.push({
        'type': type,
        id: this.count++
      });
    },
     },
  template: ` <div class='pii-field'><div>
     <component v-for="field in fields" v-bind:is="field.type":key="field.id"></component>
             </div>

            <button id='button-add-pii-input' v-on:click="addFormElement('pii-input-field')">Add Input</button>
            <hr>
            </div>`,
})

Vue.component('pii-input-field', {
  data: function () {
    return {

    }
  },

  template: ` <div>
            <select>
                <option disabled>Select Classification</option>
                <option>Name</option>
                <option>Address</option>
                <option>Email</option>
                <option>Phone</option>
                <option>Medical</option>
                <option>Financial</option>
            </select>

        <div>
            <input type="text" placeholder="Input">
        </div>
        <button v-on:click="removeFormElement">Remove Input</button></span>
        </div>`,
})

var app = new Vue({
  el: '#app',
  data: {
    fields: [],
    count: 0,
  },
    methods: {
      addFormElement: function(type) {
      this.fields.push({
        'type': type,
        id: this.count++
      });
    },

    }
});

2 Answers 2

6

Here is a working fiddle: https://jsfiddle.net/e12hbLcr/

Walkthrough:

  1. You need to give the component some kind of id to know what component should be removed. You can do this with props.

    <component v-for="field in fields" v-bind:is="field.type" :id="field.id" :key="field.id">

  2. Now create the function in the child-component and edit the button so it sends the id.

    <button v-on:click="removeFormElement(id)">Remove Input&lt/button>

Remember in Vue that props go down (parent -> child) and events up (child-parent). So now we need to tell the parent that this button was clicked and an id was sent.

removeFormElement(id) {
  console.log('sending message up to remove id', id)
  this.$emit('remove', id)      
}
  1. Tell the parent component to listen to that event and attach a function to that event.

    <component v-for="field in fields" v-bind:is="field.type" :id="field.id" @remove="removeFormElement" :key="field.id">

Note that the @ is same as v-on:

  1. Finally remove that item from the fields array.
removeFormElement(id) {
    console.log('removing form element', id)        
    const index = this.fields.findIndex(f => f.id === id)
    this.fields.splice(index,1)                
}
Sign up to request clarification or add additional context in comments.

4 Comments

Tried your fiddle, for some reason it won't remove the second or subsequent inputs.
Yeah, my bad. The id is the number of elements, which changes when I alter the array.
edit: saw your comment; Nice one dude, only one slight issue is when you create an input field and then delete it, then add a new one, you can no longer delete them anymore, I'm guessing this would be easily fixed
Sorry for the bug, updated the code. Now it finds the component with the id. (see the last function)
1

You should probably move these remove buttons into a <slot> of the component so you could pass in the scoped id.

But if you can't, you could $emit removal event on the $parent of the individual components, passing the id of the item to remove.

Vue.component('pii-entry-field', {
  data() {
    return {
      fields: [],
      count: 0,
    }
  },

  mounted() {
    this.$on('removeFormElement', this.removeFormElement);
  },

  methods: {
    addFormElement(type) {
      this.fields.push({
        'type': type,
        id: this.count++
      });
    },

    removeFormElement(id) {
      const index = this.fields.findIndex(f => f.id === id);

      this.fields.splice(index, 1);
    }
  },

  template: `
    <div class='pii-field'>
      <component v-for="field in fields" v-bind:is="field.type" :key="field.id"></component>

      <button id='button-add-pii-input' v-on:click="addFormElement('pii-input-field')">Add Input</button>
      <hr>
    </div>`,
})

Vue.component('pii-input-field', {
  data() {
    return {

    }
  },

  methods: {
    removeFormElement() {
      const id = this.$vnode.key;
      this.$parent.$emit('removeFormElement', id);
    }
  },

  template: `
    <div>
      <select>
        <option disabled>Select Classification</option>
        <option>Name</option>
        <option>Address</option>
        <option>Email</option>
        <option>Phone</option>
        <option>Medical</option>
        <option>Financial</option>
      </select>

      <div>
        <input type="text" placeholder="Input" />
      </div>
      <button v-on:click="removeFormElement">Remove Input</button>
    </div>`,
})

var app = new Vue({
  el: '#app',

  data() {
    return {
      fields: [],
      count: 0,
    }
  },

  methods: {
    addFormElement(type) {
      this.fields.push({
        'type': type,
        id: this.count++
      });
    },

  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <div id="component-pii-input" v-for="field in fields" v-bind:is="field.type" :key="field.id">

  </div>

  <button id="button-add-pii-component" v-on:click="addFormElement('pii-entry-field')" class="uk-button uk-button-primary uk-width-1-1 uk-margin-small">Add Set</button>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.