Skip to content

Extending Form Components

In FormCreate, you can easily convert custom Vue components into form components and use them just like built-in form components. This guide helps you understand how to generate custom form components and demonstrates their usage in actual business scenarios.

Note

Form components need to listen to modelValue changes and synchronously update internal values, while submitting internal value changes to the parent component through the update:modelValue event.

Encapsulating Components

Create a new Input.vue file and develop the functionality of the input box component.

vue
<template>
    <el-input :disabled="disabled" :modelValue="modelValue" @update:modelValue="handleChange" v-bind="$attrs">
        <!--Custom component slots-->
        <template #prepend>Http://</template>
    </el-input>
</template>
<script>
    import {defineComponent} from 'vue';
    export default defineComponent({
        name: 'FcInput',
        data() {
            return {};
        },
        emits: ['update:modelValue', 'change'],
        props: {
            //Get form data
            modelValue: String,
            //Support form disabled functionality
            disabled: Boolean,
            //FormCreate automatically injects some useful parameters
            //into formCreateInject when generating custom components
            formCreateInject: Object,
        },
        methods: {
            //Update form data
            handleChange(val) {
                this.$emit('update:modelValue', val);
                //Trigger change event
                this.$emit('change', val);
            }
        }
    });
</script>

Predefined Component Properties and Events

To make your custom component act as a form component in FormCreate, you need to ensure that the component implements the basic functionality of v-model. This means the component needs to receive and manage modelValue and disabled states, and trigger the update:modelValue event when values change.

Props Reception Inside the custom component, ensure the following properties are received through props:

js
// Vue component example
{
  props: {
    modelValue: String, // Bound form value
    disabled: Boolean   // Component disabled state      
  }
}

Trigger Events When the value inside the component changes, notify external value updates through the update:modelValue event:

js
this.$emit('update:modelValue', newValue);

Mounting Form Components

Before generating forms, you need to ensure that custom components are mounted through global or local methods.

Global Mounting

Global mounting applies to the entire Vue application. Through this method, components can be used anywhere in the application.

js
app.component('FcInput', FcInput);

Local Mounting

Local mounting applies to specific form instances. This method makes components available only in specific forms, using the formCreate.component() method for mounting.

js
formCreate.component('FcInput', FcInput);

Generating Form Components

To generate custom form components in forms, you need to define the field property in the generation rule, which is a required property for form components.

js
const rule = {
    type:'FcInput',
    value:'test',
    field:'testField',
    title:'Input Box'
}

Examples

Custom Counter Button Component

Simple Input Box Component

This is a basic custom input box component.

vue
<template>
  <input :value="modelValue" @input="updateValue" :disabled="disabled" />
</template>


<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
  modelValue: String,
  disabled: Boolean
});
const emit = defineEmits(['update:modelValue']);
const updateValue = (event) => {
  emit('update:modelValue', event.target.value);
};
</script>

Custom Selector Component

This is a custom selector component that lets users select a value from given options.

vue
<template>
    <select :value="modelValue" @change="onChange" :disabled="disabled">
        <option v-for="option in options" :key="option.value" :value="option.value">
            {{ option.label }}
        </option>
    </select>
</template>


<script setup>
    import { defineProps, defineEmits } from 'vue';
    const props = defineProps({
        modelValue: [String, Number],
        disabled: Boolean,
        options: {
            type: Array,
            required: true,
        },
    });
    const emit = defineEmits(['update:modelValue']);
    const onChange = (event) => {
        emit('update:modelValue', event.target.value);
    };
</script>

Input Box Component with Prefix and Suffix

This component demonstrates how to add prefix and suffix to an input box.

vue
<template>
    <div>
        <span>{{ prefix }}</span>
        <input :value="modelValue" @input="onInput" :disabled="disabled" />
        <span>{{ suffix }}</span>
    </div>
</template>


<script setup>
    import { defineProps, defineEmits } from 'vue';
    const props = defineProps({
        modelValue: String,
        disabled: Boolean,
        prefix: String,
        suffix: String,
    });
    const emit = defineEmits(['update:modelValue']);
    const onInput = (event) => {
        emit('update:modelValue', event.target.value);
    };
</script>

Custom Composite Input Component

This is a composite component that combines an input box and selector, suitable for scenarios such as selecting a country and entering a phone number.

vue
<template>
    <div>
        <select :value="selectedCountry" @change="onCountryChange" :disabled="disabled">
            <option v-for="country in countries" :key="country.value" :value="country.value">
                {{ country.label }}
            </option>
        </select>
        <input :value="modelValue" @input="onInput" :disabled="disabled" placeholder="Enter phone number" />
    </div>
</template>


<script setup>
    import { defineProps, defineEmits, ref } from 'vue';
    const props = defineProps({
        modelValue: String,
        disabled: Boolean,
        countries: {
            type: Array,
            required: true,
        },
    });
    const emit = defineEmits(['update:modelValue']);
    const selectedCountry = ref(props.countries[0].value);
    const onCountryChange = (event) => {
        selectedCountry.value = event.target.value;
        emit('update:modelValue', ''); // Clear phone number after selecting country
    };
    const onInput = (event) => {
        emit('update:modelValue', event.target.value);
    };
</script>

Selector Dependent on External Data Source

This component fetches data from a remote API and populates it into a dropdown selector. Users can select an option, and the selection result will affect other parts of the form.

vue
<template>
    <div>
        <label>
            Select country:
            <select :value="selectedCountry" @change="onCountryChange">
                <option v-for="country in countries" :key="country.id" :value="country.id">{{ country.name }}</option>
            </select>
        </label>


<label v-if="cities.length > 0">
            Select city:
            <select :value="selectedCity" @change="onCityChange">
                <option v-for="city in cities" :key="city.id" :value="city.id">{{ city.name }}</option>
            </select>
        </label>
    </div>
</template>


<script setup>
    import { ref, onMounted, watch } from 'vue';
    import axios from 'axios';
    // Receive props and define emits
    const props = defineProps({
        modelValue: {
            type: Object,
            default: () => ({ country: '', city: '' }),
        },
    });
    const emit = defineEmits(['update:modelValue']);
    // Define state
    const countries = ref([]);
    const cities = ref([]);
    const selectedCountry = ref(props.modelValue.country);
    const selectedCity = ref(props.modelValue.city);
    // Watch selectedCountry changes
    watch(selectedCountry, async (newCountry) => {
        // When country changes, trigger fetching city list
        await fetchCities(newCountry);
        // When country changes, reset city selection
        selectedCity.value = '';
        emit('update:modelValue', { country: newCountry, city: '' });
    });
    // Watch selectedCity changes
    watch(selectedCity, (newCity) => {
        emit('update:modelValue', { country: selectedCountry.value, city: newCity });
    });
    // Get country list
    onMounted(async () => {
        const response = await axios.get('/api/countries');
        countries.value = response.data;
        if (selectedCountry.value) {
            await fetchCities(selectedCountry.value);
        }
    });
    // Get city list
    const fetchCities = async (countryId) => {
        const response = await axios.get(`/api/countries/${countryId}/cities`);
        cities.value = response.data;
    };
    // Country selection event handler
    const onCountryChange = (event) => {
        selectedCountry.value = event.target.value;
    };
    // City selection event handler
    const onCityChange = (event) => {
        selectedCity.value = event.target.value;
    };
</script>

Through this documentation, you should be able to understand how to create and use custom form components in FormCreate. Custom components allow you to closely integrate business requirements with form logic, thereby building more complex and flexible form applications. Whether it's a simple input box or a complex interactive component, FormCreate can provide you with great development convenience.

FormCreate is an open-source project released under the MIT License. Free for personal and commercial use.