In the first part, we saw how to create a basic multi-colum drag-drop interface for form fields.

In this second and final part, we will be using the same concepts - instead using Twitter Bootstrap for UI and controls.

Form Builder part 2

Links:

Form Builder Part 2 Demo

Source@GitHub


First we will be defining the controls that can be used for customization. In the first part, these fields were rendered using JavaScript, while here we keep these simple for the demo and render within the HTML. We use Bootstrap Tabs to have three different sets of controls. Each control DIV has a class ctrl-, where would be the unique identifier for the type of control.

<div class="tabbable">
<!-- List of controls rendered into Bootstrap Tabs -->
<ul class="nav nav-tabs">
	<li class="active">
		<a href="#simple" data-toggle="tab">Simple input</a>
	</li>
	<li>
		<a href="#multiple" data-toggle="tab">Radio/Checkbox/List</a>
	</li>
	<li>
		<a href="#btns" data-toggle="tab" >Buttons</a>
	</li>
</ul>
<div class="row-fluid">
<div id="listOfFields" class="span3 well tab-content">
  <div class="tab-pane active" id="simple">
	<div class='selectorField draggableField'>
		<label class="control-label">Text Input</label>
		<input type="text" placeholder="Text here..." class="ctrl-textbox"></input>
	</div>
	......

Each control is contained within a DIV with class “selectorField”. These DIVs are made draggable with the line:

$(".selectorField").draggable({ helper: "clone",stack: "div",cursor: "move", cancel: null });

Next we define the droppables with the class “droppedFields”. For this demo we have 2 droppable columns for inputs with span5 and one droppable action bar for buttons with span10. This is shown below:

  <div class="row-fluid">
    <div id="selected-column-1" class="span5 well droppedFields"></div>
    <div id="selected-column-2" class="span5 well droppedFields"></div>
  </div>
  <!-- Action bar - Suited for buttons on form -->
  <div class="row-fluid">
    <div id="selected-action-column" class="span10 well action-bar droppedFields" style="min-height:80px;"></div>
  </div>

To allow repositioning within droppables, we apply Sortable with “connectWith” option on the dropped fields

$( ".droppedFields" ).sortable({
    cancel: null, // Cancel the default events on the controls
    connectWith: ".droppedFields"
}).disableSelection();

Next, to make the dropped controls customizable - we attach a click handler on the dropped control.We use handlerbar templates (with some kind of template-inheritance) and Bootstrap Modal dialog.

<script id="control-customize-template" type="text/x-handlebars-template">
  <div class="modal-header">
    <h3>{{header}}</h3>
  </div>
  <div class="modal-body">
    <form id="theForm" class="form-horizontal">
      <input type="hidden" value="{{type}}" name="type"></input>
      <input type="hidden" value="{{forCtrl}}" name="forCtrl"></input>
      <p><label class="control-label">Label</label> <input type="text" name="label" value=""></input></p>
      <p><label class="control-label">Name</label> <input type="text" value="" name="name"></input></p>
      {{{content}}}
    </form>
  </div>
  <div class="modal-footer">
    <button class="btn btn-primary" data-dismiss="modal" onclick='save_customize_changes()'>Save changes</button>
    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <button class="btn btn-danger" data-dismiss="modal" aria-hidden="true" onclick='delete_ctrl()'>Delete</button>
  </div>
</script>

<script id="textbox-template" type="text/x-handlebars-template">
  <p><label class="control-label">Placeholder</label> <input type="text" name="placeholder" value=""></input></p>
</script>

The methods in objects load_values.common and save_changes.common are implemented to load the values into the modal form and save changes to control from it. The “common” methods call the specific methods for the specific control, if any.


/* Common method to save changes to a control - This also calls the specific methods */

save_changes.common = function(values) {
  var div_ctrl = $("#"+values.forCtrl);
  div_ctrl.find('.control-label').text(values.label);
  var specific_save_method = save_changes[values.type];
  if(typeof(specific_save_method)!='undefined') {
    specific_save_method(values);
  }
}

/* Common method for all controls with Label and Name */
load_values.common = function(ctrl_type, ctrl_id) {
  var form = $("#theForm");
  var div_ctrl = $("#"+ctrl_id);

  form.find("[name=label]").val(div_ctrl.find('.control-label').text())
  var specific_load_method = load_values[ctrl_type];
  if(typeof(specific_load_method)!='undefined') {
    specific_load_method(ctrl_type, ctrl_id);
  }
}

Template inheritance, control abstraction/customization could have been written in a better way - I encourage readers to explore better designs for production code

For a preview, we simply generate and render HTML to a new window. In this process we remove the classes used for customization

If you found this useful, please share or leave a comment below.

UPDATE: Kevin Debeil added some features to the bootstrap form builder demo:

Dynamically adding upto 4 columns and Required fields.

Thanks Kevin!