1.1.13 • Published 4 years ago

v-generic-form v1.1.13

Weekly downloads
-
License
-
Repository
-
Last release
4 years ago

v-Generic-Form

TODO:

  • Allow user to choose schema layout via slot
  • Optional passing of form data when not using slots
  • Create Typescript validation function to provide better intellisense when defining schema
  • Individual passing of specific individual fields, e.g.
  • Add rules directive to children of generic form ( nice to have honestly )
  • Typescript rewrite overall
  • Allow global custom errors

Quickstart

Vue 2

yarn add v-generic-form-vue2

OR

npm i v-generic-form-vue2

import Vue from 'vue'
import vGenericForm from 'v-generic-form-vue2'
Vue.use(VGenericForm, {
    ...
})

Vue 3

yarn add v-generic-form

OR

npm i v-generic-form

import { createApp } from 'vue'
import vGenericForm from 'v-generic-form'

const el = document.getElementById('app');
const app = createApp();
app.use(vGenericForm, {
  ...
}).mount(el)

Basic usage

<template>
  <div>
    <v-generic-form :fields="fields" @submit="onSubmit" :onDataChange="onDataChange"  />
  </div>
</template>

<script>
export default {
  name: 'Form',
  computed: {
    fields() {
      return [
        {
          name: 'input',
          placeholder: 'input',
          label: 'input label',
          type: 'text',
        }
      ]
    }
  },
  methods: {
    onSubmit(data) {
      console.log(data)
    },
    onDataChange(data) {
      console.log(data)
    }
  }
};
</script>

Adding validation

vGenericForm uses validate.js behind the scenes for validation so refer to it for adding custom rules or finding all available validators.

Validation is as simple as passing a rules object to the field in your schema.

[
  {
    name: "input",
    defaultValue: 'some supported value',
    placeholder: "input",
    label: "input label",
    type: "text",
    rules: {
      required: true,
      exclusion: {
        within: ['Not Supported Value 1', 'Not Supported Value 2'],
        message: "^We don't support %{value} right now, sorry",
      },
    },
  },
];

Adding a custom validator

<script>
import validate from 'validate.js'
validate.validators.isTest = function (value, options, key, attributes) {
  console.log(value);
  console.log(`input`, attributes.atest) // same results as previous line
  console.log(options);
  console.log(key);
  console.log(attributes);
  const { message } = options
  if (value !== "test") {
    return message || 'must equal test'
  }
  // return null if success
  return null
};
export default {
  computed: {
    fields() {
      return [
        {
          name: "atest",
          placeholder: "input",
          rules: {
            isTest: {
              message: "This value does not equal test!",
            },
          },
          // disable showing input name in validation message
          fullMessages: false
        },
      ];
    },
  },
};

</script>

Using different variants

By default, vGenericForm comes with 2 inputs. Input, Select, RadioButton(coming soon), and Textarea. It will default to Input but to use a different variant, simply pass a variant as a string to the desired input you'd like to modify.

[{
  name: 'input',
  placeholder: 'input',
  label: 'input label',
  type: 'text',
  variant: 'Select',
  values: ['option1', 'option2']
}]

Using a external component with the form

<script>
import Custom from '@/components/custom'
export default {
  computed: {
    fields() {
      return [
        {
          name: "input",
          placeholder: "input",
          label: "input label",
          type: "text",
          component: Custom,
          rules: {
            required: true, // validation still works perfectly on these
          },
          customData: {
            customPropForCustomComponent: "123",
          },
        },
      ];
    },
  },
};

</script>


Custom.vue

<template>
  <div>
    <button @click="sendUpdatedValueToForm()">{{ value }}</button>
    {{ customData.customPropForCustomComponent }}
  </div>
</template>

<script>
export default {
  // default props available from generic form
  props: {
    value: {
      type: String,
      default: '',
    },
    inputType: {
      type: String,
      default: 'text',
    },
    name: {
      type: String,
      default: '',
    },
    values: {
      type Array,
      default: () => []
    },
    label: {
      type: String,
      default: '',
    },
    placeholder: {
      type: String,
      default: '',
    },
    errors: {
      type: [Array, String, Object],
      default: null,
    },
    customData: {
      type: Object,
      default: null,
    },
    getAllFields: {
      type: Array,
      default: () => {}
    },
  },
  data() {
    return {
      componentValue: "first value",
    };
  },
  methods: {
    sendUpdatedValueToForm() {
      this.componentValue = Math.random();
      $emit("setValue", {
        name,
        value: componentValue,
      });
    },
  },
};

</script>

Defining default components

import { createApp } from 'vue'
import MyInput from '@/components/myInput'
import vGenericForm from 'v-generic-form'

const el = document.getElementById('app');
const app = createApp();
app
  .use(vGenericForm, {
    components: {
      // define default inputs here
      MyInput, // you will be able to pass this as a variant now by passing variant: 'MyInput' to object scheme,
      submitText: 'Okay'
    },
  })
  .mount(el);

Styling

The generic form is structured like so:

div>
  <form
    :class="(options && options.formClass) || ''"
   >
     <div
        v-for="(field, i) in fields.filter(
          (f) => f.show === undefined || f.show !== false
        )"
        :key="i"
        :class="field.divClass"
    >

We can change the structure very easily by passing a options object

<v-generic-form :options="{
    formClass: 'flex justify-between'
}">

As you may imagine, you can control the widths of the divs each one of your fields are in by passing a divClass to your field schema:

[
  {
    name: "input",
    placeholder: "input",
    label: "input label",
    type: "text",
    divClass: "w-2/3",
    inputClasses: "bg-green-200", // you can also style the default inputs by passing this inputClasses key
  },
];

Changing submit

<v-generic-form @submit="onSubmit">
  <template v-slot:submit>
    <button type="submit">my custom submit button!</button>
  </template>
</v-generic-frorm>

Asynchronous validation

<template>
  <div>
    <v-generic-form ref="form" :fields="fields" @submit="onSubmit" :beforeSubmit="beforeSubmit" />
  </div>
</template>

<script>
import axios from 'axios'
export default {
  name: "Form",
  computed: {
    fields() {
      return [
        {
          name: "name",
          placeholder: "Your Name",
          label: "Name",
        },
        {
          name: "email",
          placeholder: "Your Email",
          label: "Email",
        },
      ];
    },
  },
  methods: {
    async beforeSubmit({email}) {
      const { data: { exists } } = await axios.post("https://myendpoint.com/checkEmails", { email });
      if (exists) {
        // prevent form from submitting by setting a custom error
        this.$refs.form.setCustomError({
          key: "email",
          value: "Email already exists.",
        });
      } else {
        // we can clear it if it doesn't exist
        this.$refs.form.setCustomError({
          key: "email",
          clear: true,
        });
      }
    },
  },
};

</script>

Defining your own layout

<template>
  <v-generic-form :schema="schema"  @submit="submit" ref="form">
    <template v-slot="{ errors, firstError, formData }">
        <generic-input v-model="formData.test1" />
        <span class="text-red-500">{{ firstError('test1') }}</span>
        <generic-input v-model="formData.test2" />
        <span class="text-red-500">{{ errors('test1') }}</span>
        <button class="custom-submit" type="submit">Submit</button>
    </template>
  </v-generic-form>
</template>

<script>
import GenericInput from "./GenericInput.vue";

export default {
  name: "CustomForm!",
  components: {
    GenericInput,
  },
  methods: {
    submit(data) {
      console.log(`data`, data);
    },
  },
  computed: {
    schema() {
      return {
        test1: {
          defaultValue: "test",
          rules: {
            required: true,
          },
        },
        test2: {
          rules: {
            required: true,
          },
        },
      };
    },
  },
};

</script>

<style scoped>
.custom-submit {
  background: blue;
  color: white;
  padding: 0.5rem 2rem;
  display: block;
  margin-top: 1rem;
  cursor: pointer;
}
</style>

Using your own formData object

<template>
  <v-generic-form :schema="schema"  @submit="submit" :customFormData="customFormData" ref="form">
    <template v-slot="{ errors, firstError, formData }">
        <generic-input v-model="formData.test1" />
        <span class="text-red-500">{{ firstError('test1') }}</span>
        <generic-input v-model="formData.test2" />
        <span class="text-red-500">{{ errors('test1') }}</span>
        <button class="custom-submit" type="submit">Submit</button>
    </template>
  </v-generic-form>
</template>

<script>
import GenericInput from "./GenericInput.vue";

export default {
  name: "CustomForm!",
  components: {
    GenericInput,
  },
  data() {
    return {
      customFormData: {
        test1: "",
        test2: "434",
      },
    };
  },
  methods: {
    submit(data) {
      console.log(`data`, data);
    },
  },
  computed: {
    schema() {
      return {
        test1: {
          defaultValue: "test",
          rules: {
            required: true,
          },
        },
        test2: {
          rules: {
            required: true,
          },
        },
      };
    },
  },
};
</script>
1.1.9

4 years ago

1.1.11

4 years ago

1.1.10

4 years ago

1.1.13

4 years ago

1.1.8

4 years ago

1.1.7

4 years ago

1.1.6

4 years ago

1.1.5

4 years ago

1.1.4

4 years ago

1.1.3

4 years ago

1.1.0

4 years ago

1.1.2

4 years ago

1.0.55

4 years ago

1.0.54

4 years ago

1.0.58

4 years ago

1.0.57

4 years ago

1.0.56

4 years ago

1.0.51

4 years ago

1.0.53

4 years ago

1.0.52

4 years ago

1.0.48

4 years ago

1.0.47

4 years ago

1.0.46

4 years ago

1.0.45

4 years ago

1.0.49

4 years ago

1.0.50

4 years ago

1.0.29

4 years ago

1.0.39

4 years ago

1.0.28

4 years ago

1.0.38

4 years ago

1.0.27

4 years ago

1.0.40

4 years ago

1.0.44

4 years ago

1.0.33

4 years ago

1.0.43

4 years ago

1.0.32

4 years ago

1.0.42

4 years ago

1.0.31

4 years ago

1.0.41

4 years ago

1.0.30

4 years ago

1.0.37

4 years ago

1.0.26

4 years ago

1.0.36

4 years ago

1.0.35

4 years ago

1.0.34

4 years ago

1.0.24

4 years ago