Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

amjadkhan896's avatar

Laravel and Vue js Dynamic Form (custom fields coming from the database)

Hi All, I made custom form in laravel means fields have type like "text, text area, etc". The fields are saved in to the database. When i load the form in to the VUE Component . I cant set the v-model attribute. due to which a lot of issue are occuring. Jquery can solve my problem. But I don't want to use Jquery.

Can anybody help me in this regard. How i can add the fields into the data object also how i can assign the v-model attribute on runtime. Any help will be appreciated.

Currently below is my custom Form Code. How i can Assign v-model at run time and also how i can make the same property in data Form Object

                        <div v-for="(field,index) in this.allData.formFields"
                             class="form-rows col-lg-4 col-md-4 col-sm-12">

                            <div v-if="field.type == 'select'">

                                <select :name="field.code.replace('-', '_')"    @change="bindData(field.code.replace('-', '_'))"
                                        class="form-control"
                                        :id="field.code.replace('-', '_')">
                                    <option value="">Please Select {{ field.name }}</option>

                                    <option :value="attributOption.id"
                                            v-for="attributOption in field.attribut_options">
                                        {{attributOption.option_name}}


                                    </option>
                                </select>

                            </div>

My Data Object is data(){ form: new Form({ title: '', description: '', phone_no: '', price: '', keywords: '', country: '', city: '', area: '', building: '', fileIds: [], category: this.allData.category, body_condition: '',

            }),

}

0 likes
16 replies
Borisu's avatar

Your code just seems confused. All you need to do when using vue components is bind the server data to your component:

<custom-component :data="{{ $serverDataFromPhp }}"></custom-component>

After that you can use v-model or any other directive.

For the form side of the question: how exactly do you save the form schema information in the database?

amjadkhan896's avatar

@Borisu I am doing as you said. My "this.allData" containing all the page and component data. So in "this.allData.formFields" are the custom Fields. That i want to load on the page.

Borisu's avatar

@amjadkhan896

Ok, can you post a sample of the formFields array?

By the way you shouldn't call this.allData.formFields, instead in your component you have something like this:

props: ['allData'],

data() {
    return {
        formFields: this.allData.formFields
    }
}

The you just use formFields in your template

amjadkhan896's avatar

@borisu When i post the form. I cant get the dynamic fields in the submit method, though when i console the Form object in mounted or anywhere It shows all the form Fields. But onSubmit i Can't get the formFields.

Borisu's avatar

I'll need the whole component code to try it locally on my test setup and see exactly what the problem is. Can you provide that?

amjadkhan896's avatar

@Borisu

        <div class="fullwidth add-form-panel  elant-inner-cards padding30" id="AdForm">


            <div class="fullwidth center-title-div">
                <h3>{{this.allData.levelOne}} <span> <i class="fa fa-angle-right" aria-hidden="true"></i> </span>
                    {{this.allData.levelTwo}}/{{this.allData.levelThree}}</h3>

                <!--   <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit,</p>
                   <p>sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>-->
            </div>


            <div class="ads-details-page-form">
                <div class="page-loading" v-show="loading">
                     <div class="loading-panel">
                        <div class="loading-icon">
                             <img src="/images/loading.svg" alt=""/>
                        </div>
                    </div>
                      
                </div>

                <form class="margin-top30 fullwidth" id="AddAutoForm" @submit.prevent="AddAutos"
                      @keydown="form.errors.clear()">
                    <div class="row">

                        <input type="hidden" name="category" v-model="form.category"/>
                        <input type="hidden" name="fileIds" id="fileIds" v-model="form.fileIds"/>

                        <div class="form-rows col-lg-12 col-md-12 col-sm-12">
                            <div class="content"><input type="text" name="title" v-model="form.title"
                                                        class="form-password bold form-control"
                                                        placeholder="Title"/>
                                <span class="help is-danger" v-if="form.errors.has('title')"
                                      v-text="form.errors.get('title')"></span>
                            </div>
                        </div>

                        <div class="form-rows col-lg-12 col-md-12 col-sm-12">


                            <div v-show="percentCompleted>0">
                                <b-progress :value="percentCompleted" variant="success" striped :animated="animate"
                                            class="mb-2"></b-progress>
                            </div>
                            <div class="fullwidth elanat-upload-image" v-if="photosList.length>0">
                                <ul>
                                    <li class="attachment-holder" v-cloak v-for="(photo, index) in photosList">
                                        <span class="label label-primary"><img
                                                :src="rootUrl+'/media/images/motors/'+photo.name" :alt="photo.name"
                                                :title="photo.name"></span>
                                        <span class="" style="background: red; cursor: pointer;"
                                              @click.prevent="removeAttachment(index,photo.fileID)"><button
                                                class="remove-btn"><i class="fa fa-trash-o" aria-hidden="true"></i></button></span>
                                    </li>
                                </ul>
                                <hr>
                            </div>

                            <div class="upload-box">
                                <input type="file" multiple="multiple" id="attachments" class="upload_image-button"
                                       @change="uploadFileChange">
                                <label for="attachments">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="17"
                                         viewBox="0 0 20 17">
                                        <path d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"></path>
                                    </svg>
                                    <span>Add Photo</span></label>
                            </div>

                        </div>

                        <div class="form-rows col-lg-12 col-md-12 col-sm-12">
                            <div class="content">
                                <b-form-textarea id="textarea1" name="description" v-model="form.description"
                                                 class="bold" placeholder="Describe your car" :rows="3"
                                                 :max-rows="6"></b-form-textarea>
                                <span class="help is-danger" v-if="form.errors.has('description')"
                                      v-text="form.errors.get('description')"></span>
                            </div>
                        </div>

                        <div class="form-rows col-lg-6 col-md-6 col-sm-12">
                            <select name="country" v-model="form.country" class="form form-control"
                                    @change="loadCities(form.country)">
                                <option value="">Please Select Country</option>
                                <option :value="cntry.id" v-for="cntry in this.allData.countries">
                                    {{cntry.country_name}}



                                </option>
                            </select>
                            <span class="help is-danger" v-if="form.errors.has('country')"
                                  v-text="form.errors.get('country')"></span>
                        </div>

                        <div class="form-rows col-lg-6 col-md-6 col-sm-12">
                            <select name="city" v-model="form.city" placeholder="City" class="form form-control"
                                    @change="loadAreas(form.city)">
                                <option value="">Please Select City</option>
                                <option :value="ct.id" v-for="ct in citiesList">{{ct.city_name}}</option>
                            </select>
                            <span class="help is-danger" v-if="form.errors.has('city')"
                                  v-text="form.errors.get('city')"></span>
                        </div>


                        <div class="form-rows col-lg-6 col-md-6 col-sm-12">
                            <div class="content"><input type="text" name="phone" v-model="form.phone"
                                                        class="form-password bold form-control"
                                                        placeholder="Phone No."/>
                                <span class="help is-danger" v-if="form.errors.has('phone')"
                                      v-text="form.errors.get('phone')"></span></div>
                        </div>

                        <div class="form-rows col-lg-6 col-md-6 col-sm-12">
                            <div class="content">
                                <input type="text" name="price" v-model="form.price"
                                                        class="form-password bold form-control"
                                                        placeholder="Price"/>
                                <span class="help is-danger" v-if="form.errors.has('price')"
                                      v-text="form.errors.get('price')"></span>
                            </div>
                        </div>


                    </div>


                    <div v-if="this.allData.formFields.length>0" class="row">


                        <div v-for="(field,index) in this.allData.formFields"
                             class="form-rows col-lg-4 col-md-4 col-sm-12">

                            <template v-if="field.type == 'select'">


                                      <select :name="field.code.replace(/-/g, '_')" v-model="form[field.code.replace(/-/g, '_')]"
                                        class="form-control"
                                        :id="field.code.replace(/-/g, '_')">
                                    <option value="">Please Select {{ field.name }}</option>

                                    <option :value="attributOption.id"
                                            v-for="attributOption in field.attribut_options">
                                        {{attributOption.option_name}}


                                    </option>
                                </select>
                                <span class="help is-danger" v-if="form.errors.has(field.code.replace(/-/g, '_'))"
                                      v-text="form.errors.get(field.code.replace(/-/g, '_'))"></span>

                            </template>
                            <template v-else-if="field.type == 'multiple-select'" >

                                <select :name="field.code.replace(/-/g, '_')" v-model="form[field.code.replace(/-/g, '_')]"
                                        class="form-control"
                                        :id="field.code.replace(/-/g, '_')" multiple>

                                    <option :value="attributOption.id"
                                            v-for="attributOption in field.attribut_options">
                                        {{attributOption.option_name}}


                                    </option>
                                </select>
                                <span class="help is-danger" v-if="form.errors.has(field.code.replace(/-/g, '_'))"
                                      v-text="form.errors.get(field.code.replace(/-/g, '_'))"></span>

                            </template>
                            <template v-else-if="field.type=='text'">


                                <input type="text" v-model="form[field.code.replace(/-/g, '_')]"
                                       :name="field.code.replace(/-/g, '_')"
                                       class="form-control "
                                       :placeholder="field.name"
                                       :id="field.code.replace(/-/g, '_')">

                                <span class="help is-danger" v-if="form.errors.has(field.code.replace(/-/g, '_'))"
                                      v-text="form.errors.get(field.code.replace(/-/g, '_'))"></span>
                            </template>

                            <template v-else-if="field.type=='date'">
                                <input type="text" v-model="form[field.code.replace(/-/g, '_')]"
                                       :name="field.code.replace(/-/g, '_')"
                                       class="form-control "
                                       :placeholder="field.name"
                                       :id="field.code.replace(/-/g, '_')">
                                <span class="help is-danger" v-if="form.errors.has(field.code.replace(/-/g, '_'))"
                                      v-text="form.errors.get(field.code.replace(/-/g, '_'))"></span>
                            </template>


                        </div>


                    </div>


                    <!-- Load Form Fields-->


                    <!-- End Of Form Fields-->

                    <div class="row">
                        <div class="form-rows col-lg-12  col-sm-12">
                            <div class="content"><input type="text" name="keywords" v-model="form.keywords"
                                                        class="form-password bold form-control"
                                                        placeholder="Keywords"/>
                                <span class="help is-danger" v-if="form.errors.has('keywords')"
                                      v-text="form.errors.get('keywords')"></span></div>
                        </div>


                        <div class="form-rows col-lg-12 col-md-12 col-sm-12">
                            <select name="area" v-model="form.area" placeholder="Area" class="form form-control"
                                    @change="loadMap(form.area,form.city,form.country)">
                                <option value="">Please Select Area</option>
                                <option :value="area.id" v-for="area in areasList">{{area.area_name}}</option>
                            </select>
                            <span class="help is-danger" v-if="form.errors.has('area')"
                                  v-text="form.errors.get('area')"></span>

                        </div>

                       <!-- <div class="form-rows col-lg-6 col-md-6 col-sm-12">
                            <select name="building" v-model="form.building" placeholder="Building"
                                    class="form form-control"
                                    @change="loadMap(form.building,form.area,form.city,form.country)">
                                <option value="">Please Select Building</option>
                                <option :value="blding.id" v-for="blding in buildingsList">
                                    {{blding.building_name}}
                                </option>
                            </select>
                            <span class="help is-danger" v-if="form.errors.has('building')"
                                  v-text="form.errors.get('building')"></span>

                        </div>-->


                        <div class="form-rows col-lg-12 col-md-12 col-sm-12">
                            <div class="fullwidth">

                                <gmap-map :center="center" :zoom="mapZoom" style="width: 100%; height: 300px">
                                    <gmap-marker :key="index" v-for="(m, index) in markers" :position="m.position"
                                                 :options="{disableDefaultUI:true}" :animation="mapAnimation"
                                                 :clickable="true" :draggable="true"
                                                 @click="center=m.position"></gmap-marker>
                                </gmap-map>

                            </div>
                        </div>

                        <div class="col-md-3 col-sm-3 col-xs-6">
                            <button type="submit" class="btn btn-secondary elanat-search-btn"
                                    :disabled="form.errors.any()">Submit



                            </button>
                        </div>


                    </div>


                </form>

            </div>


        </div>

    </div>
</div>

import * as VueGoogleMaps from 'vue2-google-maps';
import { Base64 } from 'js-base64';
Vue.use(VueGoogleMaps, {
    load: {
        key: 'AIzaSyC78BuMgfiX24gnRCD2mYqx4CqywWDgkrg',
        v: 'v=3.30',
        // libraries: 'places', //// If you need to use place input
    }
});



export default {
    props: ['allData'],

    components: {},

    data: function () {


        return {

            attachments: [],
            photosList: [],
            data: new FormData(),
            animate: true,

            // Each file will need to be sent as FormData element

            percentCompleted: 0,


            loading: false,
            rootUrl: this.allData.rootUrl,

            citiesList: '',
            areasList: '',
            buildingsList: '',
            mapZoom: 6,
            mapAnimation: 0,
            fileIdsList: [],


            form: new Form({
                title: '',
                description: '',
                phone: '',
                price: '',
                keywords: '',
                country: '',
                city: '',
                area: '',
                building: '',
                fileIds: [],
                category: this.allData.category,

                // custom Fields: '',
                kilometers:'',
                body_condition:'',
                mechanical_condition:'',
                year:'',
                vin_number:'',
                body_type:'',
                color:'',
                transmission_type:'',
                regional_specs:'',
                no_of_cylinders:'',
                doors:'',
                horsepower:'',
                warranty:'',
                fuel_type:'',
                facilities:[],



            }),


            center: {lat: 23.640278, lng: 54.308167},
            markers: [{
                position: {lat: 0, lng: 0}
            }],

            /*   allCountries: this.allData.allCountries,
             CarName: null,
             body_condition: [
             { value: "1", label: "Perfect inside and out" },
             { value: "2", label: "No accidents, very few faults" },
             { value: "3", label: "A bit of wear & tear, all repaired" },
             { value: "4", label: "Normal wear & tear, a few issues" },
             { value: "5", label: "Lots of wear & tear to the body" },
             ],*/


        }
    },
    created(){
        if (this.allData.formFields.length > 0){
            // let form = new Form;

            for (let i = 0; i < this.allData.formFields.length; i++) {
                var str = this.allData.formFields[i].code;

                 //alert(str.replace(/-/g, '_'));
                if(this.allData.formFields[i].type=='multiple-select'){
                    Vue.set(this.form, str.replace(/-/g, '_'), []);
                    // this.form.push(str.replace(/-/g, '_'));

                }else{
                    Vue.set(this.form, str.replace(/-/g, '_'), '');
                }


            }

            //  console.log(this.$el);
        }
    },

    mounted(){
       // console.log(this.original);

        console.log(this.allData);

       //  console.log(this.form);

    },

    methods: {

        getAttachmentSize() {

            this.upload_size = 0; // Reset to beginningƒ
            this.attachments.map((item) => {
                this.upload_size += parseInt(item.size);
            });

            this.upload_size = Number((this.upload_size).toFixed(1));
            this.$forceUpdate();
        },
        prepareFields() {

            if (this.attachments.length > 0) {
                for (var i = 0; i < this.attachments.length; i++) {
                    let attachment = this.attachments[i];
                    this.data.append('attachments[]', attachment);
                }
            }
        },
        removeAttachment(index, recordId) {
            // alert(this.allData.deleteImageUrl);
            // alert(motorId);
            swal({
                // title: "",
                text: "Are you sure you Want to delete this Ad?",
                icon: "error",
                buttons: true,
                dangerMode: true,
            })
                .then(function (willDelete) {
                    if (willDelete) {

                        var url = this.allData.deleteImageUrl;

                        axios.post(url + '?imgId=' + recordId).then(function (response) {
                                this.photosList.splice(index, 1);
                                let imgArr= this.fileIdsList,
                                    position = imgArr.indexOf(recordId);
                                if (~position) imgArr.splice(position, 1);

                                swal(response.data.message, {
                                    icon: "success",
                                });

                            }.bind(this),
                            function (response) {
                                //  this.loading = false;
                            }.bind(this));

                    }
                }.bind(this));

        },
        // This function will be called every time you add a file
        uploadFileChange(e) {
            var files = e.target.files || e.dataTransfer.files;
            if (!files.length)
                return;
            for (var i = files.length - 1; i >= 0; i--) {
                this.attachments.push(files[i]);
            }
            // Reset the form to avoid copying these files multiple times into this.attachments
           // document.getElementById("attachments").value = [];
            this.uploadPhotos();
        },
        uploadPhotos() {
            this.prepareFields();
            var config = {
                headers: {'Content-Type': 'multipart/form-data'},
                onUploadProgress: function (progressEvent) {
                    this.percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
                    this.$forceUpdate();
                }.bind(this)
            };
            // Make HTTP request to store announcement
            axios.post(this.allData.addMotorPhotosUrl, this.data, config)
                .then(function (response) {
                    // console.log(response.data.files);

                    if (response.status = 200) {

                        for (var i = response.data.files.length - 1; i >= 0; i--) {
                            this.photosList.push(response.data.files[i]);
                            this.fileIdsList.push(response.data.files[i].fileID);

                        }
                        this.form.fileIds = this.fileIdsList;
                        this.percentCompleted = 0;

                        this.resetData();
                    } else {
                        // console.log('Unsuccessful Upload');
                        this.errors = response.data.errors;
                    }
                }.bind(this)) // Make sure we bind Vue Component object to this funtion so we get a handle of it in order to call its other methods
                .catch(function (error) {
                    console.log(error);
                });
        },
        // We want to clear the FormData object on every upload so we can re-calculate new files again.
        // Keep in mind that we can delete files as well so in the future we will need to keep track of that as well
        resetData() {
            this.data = new FormData(); // Reset it completely
            this.attachments = [];
        },

        AddAutos(){

            if (!this.form.facilities){this.form.facilities = []}

            this.loading = true;

            this.form.post(this.allData.AddAutosRoute).then(function (response) {
                console.log(response);
                    this.loading = false;
                   // this.submitted = true;
                   // this.successMessage = response.message;
                    var url = this.allData.successUrl.replace(':id', Base64.encode(response.id));
                    window.location.href = url+'?msg='+Base64.encode(response.message);

                }.bind(this),
                function (response) {
                    this.loading = false;
                }.bind(this));

        },
        loadCities(countryId){

            this.loading = true;
            this.citiesList = '';
            this.buildingsList = '';
            this.form.city = '';
            this.form.area = '';
            this.form.building = '';
            this.areasList = '';
            axios.get(this.allData.getCitiesByCountryId + '?country_id=' + countryId).then(function (response) {
                    this.loading = false;
                    this.citiesList = response.data;
                    //  this.levelOneActive = levelOneId;
                }.bind(this),
                function (response) {
                    this.loading = false;
                }.bind(this));
        },
        loadAreas(cityId){
            this.areasList = '';
            this.buildingsList = '';
            this.form.area = '';
            this.form.building = '';
            this.loading = true;
            axios.get(this.allData.getAreasByCity + '?cityId=' + cityId).then(function (response) {
                    this.loading = false;
                    this.areasList = response.data;
                    //  this.levelOneActive = levelOneId;
                }.bind(this),
                function (response) {
                    this.loading = false;
                }.bind(this));
        },

     /*   loadBuildings(areaId){
            this.loading = true;
            this.form.building = '';
            this.buildingsList = '';
            axios.get(this.allData.getBuildingsByArea + '?areaId=' + areaId).then(function (response) {
                    this.loading = false;
                    this.buildingsList = response.data;
                    //  this.levelOneActive = levelOneId;
                }.bind(this),
                function (response) {
                    this.loading = false;
                }.bind(this));
        },*/
        loadMap(area, city, country){
            // alert(building+'---'+area+'---'+city+'---'+country);
            this.loading = true;
            axios.get(this.allData.getLatLang  + '?area=' + area + '&city=' + city + '&country=' + country).then(function (response) {
                    this.loading = false;

                    this.center.lat = response.data.latitude;
                    this.center.lng = response.data.longitude;
                    this.markers[0].position.lat = response.data.latitude;
                    this.markers[0].position.lng = response.data.longitude;
                    this.mapZoom = 14;
                    this.mapAnimation = 3;

                }.bind(this),
                function (response) {
                    this.loading = false;
                }.bind(this));
        },


    },


}
Borisu's avatar

@amjadkhan896

Ok I'll take a deeper look, but it seems the whole thing is not working since you didn't build your components correctly. You're basically trying to use Vue like jQuery which won't work. I'll write when I have some suggestions.

Borisu's avatar

@amjadkhan896

I tried it on my machine. You should try something like the following:

<div v-for="(field, index) in allData.formFields">
    <select v-if="field.type == 'select'">
        <option v-model="form[field.code]" v-for="option in field.attribute_options">{{ option }}</option>
    </select>
    <select v-else-if="field.type == 'select-multiple'" multiple>
        <option v-model="form[field.code]" v-for="option in field.attribute_options">{{ option }}</option>
    </select>
</div>

And the JavaScript

export default {
    props: ['allData'],
    data: function() {
                    return {
                        form: {
                            title: '',
                            description: '',
                            phone: '',
                            price: '',
                            ...
                    }
        }
    }
}

This works. Just fix the rest of your field etc.

And a word of advice, don't pack everything in the root Vue instance. Make components and start using simple objects (like in my example for the form object) instead of creating classes. Hope this helps.

amjadkhan896's avatar

@Borisu Thanks, You simplified the code. I will definitely try your code. But Regarding the Jquery Use of Vue. Can you please tell me where and in which section I am using Vue as a Jquery? Because I am trying my best toignore Jquery.

And regarding the form Object, I was following the jaffery otwell form tutorial episode 21 as i think.

In My Code there is a custom fields "comment".

data: function() {

form :new Form({ // custom Fields kilometers:'', body_condition:'', mechanical_condition:'', year:'', vin_number:'', body_type:'', color:'', transmission_type:'', regional_specs:'', no_of_cylinders:'', doors:'', horsepower:'', warranty:'', fuel_type:'', facilities:[], )} }

Does your code will add these fields to the form on run time ? Because these will be loaded on run time.

Actually this is a one complete form with a lot of inputs. Thats Why i did not make additional components.

amjadkhan896's avatar

@Borisu I tried your code. Actually i am using the jaffery Form.js. I tried your code But its creating a lot of problems. Like the Form.js has method is not working like getting the errors. making the form empty when submitting and holding the values when it has errors. Did you tried the Form.js and Error.Js ?

Borisu's avatar

@amjadkhan896

Hi, the first part of your question regarding jQuery: When you directly transform the field code instead of using computed properties and so on.

Second part: No my code is just a simple example to show you how to do it, it's not a complete solution. No I didn't use Form and Error, because as I pointed out earlier, you should first make it work with simple objects and then upgrade to more complex classes. Besides your form inputs come from the allData prop and not from the form class. The form class is just there to hold the values you choose.

Do you have the Vue-Debugbar installed?

amjadkhan896's avatar

Aaah yes for the first part i agree. Yes i checked If i use Your code. I have to work a lot. That's Why i preferred the Form.js and Error.js of episode 21.

I am using chrome Vue-tools.

bipin's avatar

@amjadkhan you can use both jquery and vue script define vue script above of all script it will help

amjadkhan896's avatar

@bipin I think its not a good approach. If you are using Vue Then don't use Jquery. But if you are using Jquery. Then Why you are using VUE. Jquery is Simple. It can give you all the functionality.

Borisu's avatar

They are both good and can be used together, but you just don't need jQuery in this case.

1 like

Please or to participate in this conversation.