How to create reusable form and input components using slots?

First, I am wondering whether I am on the right track with the patterns I am using for my application. Is it correct for me to create a reusable BaseForm component?

I simply want to re use the same submit handler for all my forms. The submit handler should use the action and method props to determine how to configure axios, and the request body will be dynamic based on the BaseInput and BaseSelect components that are slotted within the BaseForm component.

Here’s my sandbox example:

I am stuck on the BaseForm.vue handleSubmit method.

You don’t say if you are building a dynamic form builder application, or you just want the functionality in another application. If the main function of the app is not as a dynamic form builder, you might want to take a simpler approach and just build the forms you need separately.

Regardless, an interesting idea that motivated me to create a proof of concept for myself, that possibly might help out. I learned a few things.

I have built several Vue client applications that submit forms to backend APIs. Generally the form inputs are bound to data using v-model. Normally the form backing object is known to me ahead of time.

In this case, in order to build a form dynamically, I thought that you might want to pass a list of properties to the form component at run time and build the form backing object dynamically at runtime.

Here is the form component that I built, DynamicFormObject.vue:

<template>
  <div class="dynamic-form-object">
    <h2>Dynamic Form Object</h2>
    <div class="row">

      <div class="col-sm-4 form-container">
        <form @submit.prevent="submitUserForm">
          <div class="form-group">
            <label>Name</label>
            <input type="text" class="form-control" v-model.trim="user.name">
          </div>
          <div class="form-group">
            <label>Email</label>
            <input type="text" class="form-control"
              v-model.trim="user.email">
          </div>
          <div class="form-group">
            <button class="btn btn-primary" type="submit">Save</button>
          </div>
        </form>
      </div>

    </div>
  </div>
</template>
<script>
  import { submitUserForm } from '@/globalvars.js'

  export default {
    data() {
      return {
        user: {},
      }
    },
    props: {
      userProperties: {
        type: Object,
        required: true
      }
    },
    methods: {
      addUserProperties() {
        this.user = Object.assign({}, this.userProperties);
      },
      submitUserForm
    },
    created() {
      this.addUserProperties();
    }
  }
</script>

and here is my App.vue:

<template>
  <div id="app" class="container">
    <dynamic-form-object :userProperties="userProperties" />
  </div>
</template>
<script>
  import DynamicFormObject from '@/components/vue-help-forum/DynamicFormObject'

  export default {
    name: 'App',
    components: {
      DynamicFormObject
    },
    data() {
      return {
        userProperties: {
          id: 0,
          name: '',
          email: ''
        }
      }
    }
  }
</script>

I didn’t code ‘submitUserForm’, but based on my previous projects, it would look something like:

methods: {
    submitUserForm() {
                axios({
                    method: this.user.id ? 'put' : 'post',
                    url: this.user.id ? userRestUrl + this.user.id : userRestUrl,
                    data: JSON.stringify(this.user),
                })
                .then(() => {
                    // Redirect back to Index view
                    this.$router.push({ name: 'userIndex' });
                })
                .catch( error  => console.log(error) );
    }
}
1 Like

Dynamic form is completely foolish I must say.

Have you ever seen a form that looks beautiful for the end user?

So easy for a programmer to say but i dare you to programatically produce a form thats built by any top 100 sites!

Example try to recreate this… you cant.

Forms canbe looped for each input or what ever but for end result you need to make it look nice

1 Like

Thanks all for the replies. I did eventually decide against using this pattern. I still get to have reusable form input components, but having a reusable parent form component with a reusable form submit handler is not the way to go. Even though it would be nice to keep it DRY, I think the solution would be overengineered (if even possible).

Here’s my final result: romantic-almeida-wpxbw - CodeSandbox

You can still have a global submit button.

I have one and makes it easy to modify it in 1 spot vs every form…

Basically just a regular button but emits an event when clicked up to parent to submit form.

<script>
export default {
  name: 'FormSubmitBtn',
  props: {
   text: {
      type: String,
      default: 'Save',
      required: false
    },
    loading: {
      type: Boolean,
      default: false,
      required: false
    },
  }
}
</script>
<template>
  <v-btn
    :loading:loading
   ... anything you want to pass from props if you want to change text / or color / or whatever you can think of
    class="mySubmitBtn"
    @click="this.$emit('submitForm')"
  >
    {{ text }}
  </v-btn>
</template>
1 Like