How To Create Simple Drag And Drop Custom Form Builder

In this article, we are going to learn how to create a simple drag And drop custom form builder using  HTML,JQuery.

Step: 1
First, we need to include external js file and other UI files, and for design add style.

<html lang="en" ng-app="DragDropApp">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js" type="text/javascript"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js" type="text/javascript"></script>
  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js" type="text/javascript"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular-animate.js" type="text/javascript"></script>
  <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/374704/sortable.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

    
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" /> 
   <title>Drag drop form builder</title>
   <style type="text/css">
       body {
    padding-top: 20px;
    color: #75736f;
    overflow: hidden;
}

::-webkit-scrollbar {
    display: none;
}


a {
    color: #75736f;
}

a:focus,
a:hover,
a:visited,
a:active,
a:link {
    text-decoration: none !important;
    color: #75736f;
}

ul.neo-nav {
    border: 2px solid #75736f;
    display: inline-block;
    -moz-padding-start: 0px;
    -webkit-padding-start: 0px;
    padding-start: 0px;
    border-radius: 5px;
}

ul.neo-nav li {
    display: inline-block;
    list-style: none;
}

ul.neo-nav li a {
    color: #75736f;
    padding: 10px 30px;
    font-weight: bold;
    display: block;
}

ul.neo-nav li.active a {
    color: white;
}

ul.neo-nav li:not(:first-child) {
    border-left: 2px solid #75736f;
}

ul.neo-nav li.active {
    background-color: #75736f;
    color: white;
}

ul.neo-nav li a:hover,
ul.neo-nav li a:visited,
ul.neo-nav li a:active,
ul.neo-nav li a:link {
    text-decoration: none;
}

#main-content {
    background-color: whitesmoke;
    border-left: 1px solid #ddd;
    padding-top: 20px;
    padding-bottom: 20px;
    z-index: 1;
    overflow-y: scroll;
}

#sideBar {
    padding-top: 20px;
    padding-left: 0px;
    padding-right: 0px;
    z-index: 2;
}

.tab-container {
    border-bottom: 1px solid gray;
    padding-bottom: 20px;
}

#addFieldTab {
    padding-top: 20px;
    padding-left: 20px;
}

#fieldSettingTab {
    padding: 20px;
}

#stdFields {}

#formBuilderContent {
    background-color: white;
    padding: 20px;
}

.dragElement-wrapper {
    width: 50%;
    display: inline-block;
    margin-bottom: 20px;
}

.dragElement-wrapper .drag-element {
    display: block;
    width: 90%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
    cursor: pointer;
}

.dragElement-wrapper .drag-element i {
    margin-right: 5px;
}

[draggable] {
    -moz-user-select: none;
    -khtml-user-select: none;
    -webkit-user-select: none;
    user-select: none;
    /* Required to make elements draggable in old WebKit */

    -khtml-user-drag: element;
    -webkit-user-drag: element;
}

#chkBoxZone {
    margin-top: 15px;
}

.input-as-label {
    border: none;
    box-shadow: none;
    display: inline-block;
    max-width: 100%;
    margin-bottom: 5px;
    font-weight: 700;
    background-color: transparent;
}

.active-field {
    background-color: #f1fafc;
}

.form-group {
    padding: 10px;
    position: relative;
}

.form-group i.remove-ico {
    position: absolute;
    right: 5px;
    top: 5px;
    cursor: pointer;
}

.pale {
    opacity: 50%;
}

.sortable-formbuilder,
.sortable-stdFields {
    padding-left: 0px;
    -moz-padding-start: 0px;
    -webkit-padding-start: 0px;
    -khtml-padding-start: 0px;
    -o-padding-start: 0px;
    padding-start: 0px;
    list-style: none;
    min-height: 40px;
}

.drop-to-add {
    height: 40px;
}

a[data-toggle="collapse"] i.fa-plus-square-o {
    display: none;
}

a[data-toggle="collapse"] i.fa-minus-square-o {
    display: inline-block;
}

a[data-toggle="collapse"].collapsed i.fa-plus-square-o {
    display: inline-block;
}

a[data-toggle="collapse"].collapsed i.fa-minus-square-o {
    display: none;
}

.orange-txt {
    color: orange;
}
#sidebar-tab-content{
    overflow-y: scroll;
}
   </style>
</head>

Step 2: Add the following code to the body tag.

<body ng-controller="DragDropCtrl">

<div class="container-fluid">
    <div class="row">
        <div class="col-md-3" id="sideBar">
            <div class="tab-container text-center">
                <ul class="neo-nav clearfix" role="tablist">
                    <li role="presentation" class="active"><a href="#addFieldTab" id="addFieldTab_lnk" aria-controls="home" role="tab" data-toggle="tab">Add a Field</a></li><!--

                    --><li role="presentation"><a href="#fieldSettingTab" id="fieldSettingTab_lnk" aria-controls="profile" role="tab" data-toggle="tab">Field Settings</a></li>
                </ul>
            </div>

            <div class="tab-content" id="sidebar-tab-content">
                <div role="tabpanel" class="tab-pane active" id="addFieldTab">
                    <p>
                        <a role="button" data-toggle="collapse" href="#stdFields">
                            <i class="fa fa-lg fa-plus-square-o"></i><i class="fa fa-lg fa-minus-square-o"></i> STANDARD FIELDS
                        </a>
                    </p>

                    <div class="collapse in" id="stdFields">
                        <ul  ng-model="dragElements" class="sortable-stdFields">
                            <li draggable="true" class="dragElement-wrapper" ng-repeat="ele in dragElements" element-draggable data-index="{{$index}}">
                                <div class="drag-element" ng-click="addElement(ele)" >
                                    <i class="fa fa-cogs"></i> {{ele.Name}}
                                </div>
                            </li>
                        </ul>

                    </div>

                </div>
                <div role="tabpanel" class="tab-pane" id="fieldSettingTab">

                    <div ng-repeat="set in current_field.Settings" ng-switch on="set.Type">
                        <div >

                            <div class="form-group " ng-switch-when="text">
                                <label for="{{set.Name.replace(' ','_')}}">{{set.Name}}</label>
                                <input ng-change="current_field.ChangeFieldSetting(set.Value, set.Name)" type="text" ng-model="set.Value"  class="form-control" id="{{set.Name.replace(' ','_')}}" value="{{set.Value}}" placeholder="{{set.Name}}">
                            </div>


                            <div class="form-group" ng-switch-when="string">
                                <label >{{set.Name}}</label>
                                <br>
                                <span >{{set.Value}}</span>
                            </div>


                            <div class="form-group" ng-switch-when="label">
                                <label class="pale">{{set.Name}}</label>
                            </div>

                            <div class="form-group " ng-switch-when="dropdown" >
                                <label >{{set.Name}}</label>
                                <select class="form-control" ng-model="set.Value" >
                                    <option ng-repeat="op in set.PossibleValue">{{op}}</option>
                                </select>
                            </div>

                            <div class="form-group" ng-switch-when="radio">
                                <div ng-repeat="val in set.PossibleValue" class="radio">
                                    <label>
                                        <input type="radio" name="optionsRadios"  value="{{val.Checked}}" ng-checked="val.Checked">
                                        {{val.Text}}
                                    </label>
                                </div>
                            </div>

                            <div class="form-group" ng-switch-when="dropdown_increment">
                                <label class="control-label">
                                    Choices
                                </label>
                                <div ng-repeat="val in set.PossibleValue" class="radio" class="form-control">
                                    <i class="fa fa-sort fa-lg">

                                    </i>
                                    &nbsp;
                                    <i class="fa fa-circle-o fa-lg">

                                    </i>
                                    <input type="text"  value="val.Text" ng-model="val.Text">
                                </div>

                            </div>



                            <div ng-switch-when="checkBoxZone">
                                <a role="button" data-toggle="collapse" href="#chkBoxZone">
                                    <i class="fa fa-lg fa-plus-square-o"></i><i class="fa fa-lg fa-minus-square-o"></i> {{set.Name}}
                                </a>
                                <div class="collapse in" id="chkBoxZone">
                                    <div class="form-group" ng-repeat="op in set.Options">
                                        <label class="checkbox-inline" >
                                            <input type="checkbox" value="{{op.Value}}" ng-model="op.Value"> {{op.Name}}
                                        </label>
                                    </div>

                                </div>
                            </div>
                            <p ng-switch-default>
                                Unknown
                            </p>
                        </div>
                    </div>
                </div>
            </div>

        </div>
        <div class="col-md-9 text-center" id="main-content">
            

            <div class="tab-content">
                <div role="tabpanel" class="tab-pane active text-left" id="formBuilderContent">
     

                    <div class="container-fluid" id="dropZone">
                        <ul class="row sortable-formbuilder" element-drop  ui-sortable="formbuilderSortableOpts" ng-model="formFields" id="sortable-formbuilder-ul">
                            <li  ng-repeat="field in formFields" element-drop data-index="{{$index}}" ng-switch on="field.Type" ng-class="field.GetFieldSetting('Column Span').Value == 1 ? 'col-md-6 sortable-field' : 'col-md-12 sortable-field' " ng-click="activeField(field)" data-index="{{$index}}">

                                <div class="form-group "  ng-switch-when="text" ng-class="field.Active ? 'active-field' : '' " >

                                    <input type="text" class="input-as-label" ng-model="field.Name" value="{{field.Name + (field.GetFieldSetting('Required') ? '*': '')}}" ng-change="field.ChangeFieldSetting(field.Name,'Field Label')"/>
                                    <span ng-if="field.GetFieldSetting('Required').Value" class="orange-txt">*</span>

                                    <!--<label ng-if="!field.Active" for="{{field.Name.replace(' ','_') + field.id}}">{{field.Name}}<span ng-if="field.GetFieldSetting('Required')">*</span></label> -->

                                    <input type="text" class="form-control" id="{{field.Name.replace(' ','_') + field.id}}" value="{{field.Value}}" placeholder="{{field.Name}}">
                                    <i class ="fa fa-lg fa-minus-square-o remove-ico" ng-click="removeElement($index)" ng-if="field.Active"></i>
                                </div>

                                <div class="form-group k" ng-switch-when="date" ng-class="field.Active ? 'active-field' : '' ">
                                    <input type="text" class="input-as-label" ng-model="field.Name" value="{{field.Name + (field.GetFieldSetting('Required').Value ? '*': '')}}" ng-change="field.ChangeFieldSetting(field.Name,'Field Label')"/>
                                    <span ng-if="field.GetFieldSetting('Required').Value" class="orange-txt">*</span>
                                    <div class="has-feedback">
                                        <input type="text" class="form-control"  placeholder="{{field.Name}}">
                                        <span class="glyphicon glyphicon-calendar form-control-feedback custom-feedback" aria-hidden="true"></span>
                                        <span id="inputSuccess2Status" class="sr-only">(success)</span>
                                    </div>

                                    <i class ="fa fa-lg fa-minus-square-o remove-ico" ng-if="field.Active"  ng-click="removeElement($index)"></i>
                                </div>

                                <div class="form-group " ng-switch-when="dropdown" ng-class="field.Active ? 'active-field' : '' ">
                                    <input type="text" class="input-as-label" ng-model="field.Name" value="{{field.Name + (field.GetFieldSetting('Required').Value ? '*': '')}}" ng-change="field.ChangeFieldSetting(field.Name,'Field Label')"/>
                                    <span ng-if="field.GetFieldSetting('Required').Value" class="orange-txt">*</span>
                                    <select class="form-control" >
                                        <option ng-repeat="val in field.GetFieldSetting('Choice').PossibleValue">
                                            {{val.Text}}
                                        </option>
                                    </select>
                                    <i class ="fa fa-lg fa-minus-square-o remove-ico" ng-if="field.Active"  ng-click="removeElement($index)"></i>
                                </div>

                                <div class="form-group " ng-switch-when="textarea" ng-class="field.Active ? 'active-field' : '' ">
                                    <input type="text" class="input-as-label" ng-model="field.Name" value="{{field.Name + (field.GetFieldSetting('Required').Value ? '*': '')}}" ng-change="field.ChangeFieldSetting(field.Name,'Field Label')"/>
                                    <span ng-if="field.GetFieldSetting('Required').Value" class="orange-txt">*</span>
                                    <textarea class="form-control" id="{{field.Name.replace(' ','_')  + field.id}}" rows="4"></textarea>
                                    <i class ="fa fa-lg fa-minus-square-o remove-ico" ng-if="field.Active"  ng-click="removeElement($index)"></i>
                                </div>
                                <div ng-switch-default>
                                </div>
                            </li>
                            <li class="drop-to-add col-md-12" element-drop>

                            </li>
                        </ul>
                    </div>

                </div>
            </div>
        </div>
    </div>
</div>


</body>

 

Step 3: We need to add the Following Script code for the setting of the custom field.

<script type="text/javascript">
    

    /**
 * 
 */
(function() {
    var guid = 1;

    var app = angular.module('DragDropApp', ['ui.sortable']);

    app.controller('DragDropCtrl', function($scope) {
        $scope.dragElements = [{
            'Name': "Single Text",
            'Type': "text",
            'Settings': [{
                'Name': 'Field Label',
                'Value': 'Single Text',
                'Type': 'text'
            }, {
                'Name': 'Short Label',
                'Value': 'Single Text',
                'Type': 'text'
            }, {
                'Name': 'Internal Name',
                'Value': 'xSingle_Text',
                'Type': 'text'
            }, {
                'Name': 'Field Type',
                'Value': 'Single Text',
                'Type': 'string'
            }, {
                'Name': 'Single Line Text Options',
                'Value': '',
                'Type': 'label'
            }, {
                'Name': 'Max Input Length',
                'Value': '50',
                'Type': 'text'
            }, {
                'Name': 'Url Template',
                'Value': '',
                'Type': 'text'
            }, {
                'Name': 'Column Span',
                'Value': '1',
                'Type': 'dropdown',
                'PossibleValue': ['1', '2']
            }, {
                'Name': 'General Options',
                'Type': 'checkBoxZone',
                'Options': [{
                    'Name': 'Required',
                    'Value': false
                }, {
                    'Name': 'Show on list',
                    'Value': false
                }, {
                    'Name': 'Unique',
                    'Value': false
                }, {
                    'Name': 'Index',
                    'Value': false
                }]
            }

            ]
        }, {
            'Name': "Date",
            'Type': "date",
            'Settings': [{
                'Name': 'Field Label',
                'Value': 'Field Label',
                'Type': 'text'
            }, {
                'Name': 'Short Label',
                'Value': 'Short Label',
                'Type': 'text'
            }, {
                'Name': 'Internal Name',
                'Value': 'Internal Name',
                'Type': 'text'
            }, {
                'Name': 'Field Type',
                'Value': 'Date',
                'Type': 'string'
            }, {
                'Name': 'Display Type',
                'Value': '',
                'Type': 'radio',
                'PossibleValue': [
                    {
                        'Text' : 'DateTimeInstance',
                        'Checked' : true
                    },
                    {
                        'Text' : 'DateTimeLocal',
                        'Checked' : false
                    },
                    {
                        'Text' : 'DateLocal',
                        'Checked' : false
                    },
                    {
                        'Text' : 'Time',
                        'Checked' : false
                    },
                    ]
            }, {
                'Name': 'Column Span',
                'Value': '1',
                'Type': 'dropdown',
                'PossibleValue': ['1', '2']
            }, {
                'Name': 'General Options',
                'Type': 'checkBoxZone',
                'Options': [{
                    'Name': 'Required',
                    'Value': false
                }, {
                    'Name': 'Show on list',
                    'Value': false
                }, {
                    'Name': 'Unique',
                    'Value': false
                }, {
                    'Name': 'Index',
                    'Value': false
                }]
            }

            ]
        }, {
            'Name': "Singe Selection",
            "Type": "dropdown",
            'Settings': [{
                'Name': 'Field Label',
                'Value': 'Field Label',
                'Type': 'text'
            }, {
                'Name': 'Short Label',
                'Value': '',
                'Type': 'text'
            }, {
                'Name': 'Internal Name',
                'Value': 'Short Label',
                'Type': 'text'
            }, {
                'Name': 'Field Type',
                'Value': 'Single Selection',
                'Type': 'string'
            }, {
                'Name': 'Display Type',
                'Value': '',
                'Type': 'radio',
                'PossibleValue': [
                    {
                        'Text' : 'Dropdown',
                        'Checked' : true
                    },
                    {
                        'Text' : 'Radio List',
                        'Checked' : false
                    }
                ]
            }, {
                'Name': 'Choice',
                'Type': 'dropdown_increment',
                'PossibleValue': [
                    {
                        'Text':'Choice 1',

                    }
                    , {
                        'Text': 'Choice 2'
                    }
                    ]
            }, {
                'Name': 'Column Span',
                'Value': '1',
                'Type': 'dropdown',
                'PossibleValue': ['1', '2']
            }, {
                'Name': 'General Options',
                'Type': 'checkBoxZone',
                'Options': [{
                    'Name': 'Required',
                    'Value': false
                }, {
                    'Name': 'Show on list',
                    'Value': false
                }, {
                    'Name': 'Unique',
                    'Value': false
                }, {
                    'Name': 'Index',
                    'Value': false
                }]
            }

            ]
        }, {
            'Name': "Pagaraph Text",
            "Type": "textarea",
            'Settings': [{
                'Name': 'Field Label',
                'Value': '',
                'Type': 'text'
            }, {
                'Name': 'Short Label',
                'Value': '',
                'Type': 'text'
            }, {
                'Name': 'Internal Name',
                'Value': '',
                'Type': 'text'
            }, {
                'Name': 'Field Type',
                'Value': 'Paragraph Text',
                'Type': 'string'
            }, {
                'Name': 'Column Span',
                'Value': '1',
                'Type': 'dropdown',
                'PossibleValue': ['1', '2']
            }, {
                'Name': 'General Options',
                'Type': 'checkBoxZone',
                'Options': [{
                    'Name': 'Required',
                    'Value': false
                }, {
                    'Name': 'Enable Rich Text',
                    'Value': false
                }, {
                    'Name': 'Active',
                    'Value': true
                }, {
                    'Name': 'Hidden',
                    'Value': false
                }]
            }

            ]
        }];

        $scope.formFields = [];

        $scope.current_field = {};

        var createNewField = function() {
            return {
                'id': ++guid,
                'Name': '',
                'Settings': [],
                'Active': true,
                'ChangeFieldSetting': function(Value, SettingName) {
                    switch (SettingName) {
                        case 'Field Label':
                        case 'Short Label':
                        case 'Internal Name':
                            $scope.current_field.Name = Value;
                            $scope.current_field.Settings[0].Value = $scope.current_field.Name;
                            $scope.current_field.Settings[1].Value = $scope.current_field.Name;
                            $scope.current_field.Settings[2].Value = 'x' + $scope.current_field.Name.replace(/\s/g, '_');
                            break;
                        default:
                            break;
                    }
                },
                'GetFieldSetting': function(settingName) {
                    var result = {};
                    var settings = this.Settings;
                    $.each(settings, function(index, set) {
                        if (set.Name == settingName) {
                            result = set;
                            return;
                        }
                    });
                    if (!Object.keys(result).length) {
                        //Continue to search settings in the checkbox zone
                        $.each(settings[settings.length - 1].Options, function(index, set) {
                            if (set.Name == settingName) {
                                result = set;
                                return;
                            }
                        });
                    }
                    return result;

                }
            };
        }

        $scope.changeFieldName = function(Value) {
            $scope.current_field.Name = Value;
            $scope.current_field.Settings[0].Value = $scope.current_field.Name;
            $scope.current_field.Settings[1].Value = $scope.current_field.Name;
            $scope.current_field.Settings[2].Value = 'x' + $scope.current_field.Name.replace(/\s/g, '_');
        }

        $scope.removeElement = function(idx){
            if($scope.formFields[idx].Active) {
                $('#addFieldTab_lnk').tab('show');
                $scope.current_field = {};

            }
            $scope.formFields.splice(idx, 1);
        };

        $scope.addElement = function(ele, idx) {
            $scope.current_field.Active = false;

            $scope.current_field = createNewField();
            //Merge setting from template object
            angular.merge($scope.current_field, ele);

            if (typeof idx == 'undefined') {
                $scope.formFields.push($scope.current_field);
            } else {
                $scope.formFields.splice(idx, 0, $scope.current_field);
                $('#fieldSettingTab_lnk').tab('show');
            }

        };

        $scope.activeField = function(f) {
            $scope.current_field.Active = false;
            $scope.current_field = f;
            f.Active = true;
            $('#fieldSettingTab_lnk').tab('show');
        };

        $scope.formbuilderSortableOpts = {
            'ui-floating': true,

        };
    });

    app.directive('elementDraggable', ['$document', function($document) {
        return {
            link: function(scope, element, attr) {
                element.on('dragstart', function(event) {

                    event.originalEvent.dataTransfer.setData('templateIdx', $(element).data('index'));
                });
            }
        };
    }]);

    app.directive('elementDrop', ['$document', function($document) {
        return {
            link: function(scope, element, attr) {

                element.on('dragover', function(event) {
                    event.preventDefault();
                });

                $('.drop').on('dragenter', function(event) {
                    event.preventDefault();
                })
                element.on('drop', function(event) {
                    event.stopPropagation();
                    var self = $(this);
                    scope.$apply(function() {
                        var idx = event.originalEvent.dataTransfer.getData('templateIdx');
                        var insertIdx = self.data('index')
                        scope.addElement(scope.dragElements[idx], insertIdx);
                    });
                });
            }
        };
    }]);

})();

$(function() {
    // Code here
    var dh = $(document).height();
    $('#sidebar-tab-content').height(dh - 115);
    $('#main-content').height(dh - 10);
});
</script>
</html>

Please review  following video:

 

 

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories