jQuery: "jQuery is a fast, small, and feature-rich JavaScript library. It makes
things like HTML document traversal and manipulation, event handling,
animation, and Ajax much simpler with an easy-to-use API that works across
a multitude of browsers. With a combination of versatility and
extensibility, jQuery has changed the way that millions of people write
JavaScript. " (From
https://jquery.com/)
Bootstrap: "Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web. Bootstrap makes front-end web development faster and easier. It's made
for folks of all skill levels, devices of all shapes, and projects of
all sizes. Millions of amazing sites across the web are being built with Bootstrap." (From
http://getbootstrap.com/)
For the detail of each one above, you can just click the links for detail.
I've used jQuery before, this is my first time to use Bootstrap and Form Validation. But this is a very good experience, it makes the UI development job easier and fun.
Below is the form I am building. In
the previous blog, I introduced how to use jQuery to build drag and drop sorting table, and how to add a new row or remove a row.
Here, I use Bootstrap form validation to validate the form.
Features for this form:
1, Each row can be drag and drop to the any place.
2. User can add a new row and remove an existing row.
3. Internal name, display name, Search weight columns are required for each row.
4. If there is a current image, the new image is not required.
5. If there is not a current image, the new image is required.
HTML code as below:
<form id="classconfigform">
<button type="button" class="btn btn-default btn-xs" id="addbutton">Add New Class</button>
<table id="classtable" class="table table-striped">
<thead>
<tr>
<th class="hidden-xs hidden-sm hidden-md hidden-lg">Order</th>
<th>Class Internal Name</th>
<th>Display Name</th>
<th>Enabled</th>
<th>Search Weight</th>
<th>Current Image</th>
<th>New Image</th>
<th>Delete class</th>
</tr>
</thead>
<tbody id="sortable">
<c:forEach items="${classes}" var="class">
<tr>
<td class="hidden-xs hidden-sm hidden-md hidden-lg ui-state-default">
<input type="hidden" id="displayOrder" name="displayOrder[]" class="form-control" value='<c:out value="${class.displayOrder}"/>'></input>
</td>
<td><input type="text" id="internalClassName" name="internalClassName[]" class="form-control" placeholder="Class Internal Name" value='<c:out value="${class.internalClassName}"/>'></td>
<td><input type="text" id="displayName" name="displayName[]" class="form-control" placeholder="Display Name" value='<c:out value="${class.displayName}"/>'></td>
<td><input type="checkbox" name="enabled[]" class="form-control" checked ></td>
<td><input type="text" id="searchWeight[]" name="searchWeight[]" class="form-control" value='<c:out value="${class.searchWeight}"/>'></td>
<td><input type="hidden" id="fileid" name="fileid[]" class="form-control" value='<c:out value="${class.fileStore.fileId}"/>'><img src="${pageContext.request.contextPath}/download/${class.fileStore.fileId}"></td>
<td><input type="file" id="uploadimage" name="uploadimage[]" class="form-control" value=''>
</td>
<td><button type="button" class="removebutton" title="Remove this class"><span class="glyphicon glyphicon-remove "></span></button> </td>
</tr>
</c:forEach>
<!-- The new row template containing -->
<tr class="hide" id="newClassRowTemplate">
<td class="hidden-xs hidden-sm hidden-md hidden-lg ui-state-default">
<input type="hidden" id="displayOrder" name="displayOrder[]" class="form-control" value='<c:out value="${class.displayOrder}"/>'>
</td>
<td><input type="text" id="internalClassName" name="internalClassName[]" class="form-control" placeholder="Class Internal Name"></td>
<td><input type="text" id="displayName" name="displayName[]" class="form-control" placeholder="Display Name"></td>
<td><input type="checkbox" name="enabled[]" class="form-control" checked ></td>
<td><input type="text" id="searchWeight[]" name="searchWeight[]" class="form-control"></td>
<td><input type="hidden" id="fileid" name="fileid[]" class="form-control"/> </td>
<td><input type="file" id="uploadimage" name="uploadimage[]" class="form-control" value=''>
</td>
<td><button type="button" class="removebutton" title="Remove this class"><span class="glyphicon glyphicon-remove "></span></button> </td>
</tr>
</tbody>
</table>
<button type="submit" id="saveClasses" class="btn btn-primary">Save Classes</button>
</form>
Javascript code as below:
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="${pageContext.request.contextPath}/resources/scripts/jquery.js" type="text/javascript"></script>
<script src="${pageContext.request.contextPath}/resources/scripts/jquery-ui.min.js" type="text/javascript"></script>
<script src="${pageContext.request.contextPath}/resources/scripts/bootstrap-3.3.2-dist/js/bootstrap.min.js" type="text/javascript"></script>
<!-- FormValidation CSS file -->
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/scripts/formvalidation-0.6.2/dist/css/formValidation.min.css">
<!-- FormValidation plugin and the class supports validating Bootstrap form -->
<script src="${pageContext.request.contextPath}/resources/scripts/formvalidation-0.6.2/dist/js/formValidation.min.js"></script>
<script src="${pageContext.request.contextPath}/resources/scripts/formvalidation-0.6.2/dist/js/framework/bootstrap.min.js"></script>
<script>
$(document).ready(function() {
$("#sortable").sortable();
$('#classconfigform').formValidation({
framework: 'bootstrap',
err: {
container: 'tooltip'
},
row: {
selector: 'td'
},
icon: {
valid: 'glyphicon glyphicon-ok',
invalid: 'glyphicon glyphicon-remove',
validating: 'glyphicon glyphicon-refresh'
},
fields: {
'internalClassName[]': {
validators: {
notEmpty: {
message: 'Internal class name is required'
}
}
},
'displayName[]': {
validators: {
notEmpty: {
message: 'Display name is required'
}
}
},
'searchWeight[]': {
validators: {
notEmpty: {
message: 'Search Weight is required'
}
}
},
'uploadimage[]': {
validators: {
callback: {
message: 'An image is required',
callback: function (value, validator, $field) {
var $fileid='';
var $classimg = $field.closest('tr');
$fileid = $classimg.find('#fileid').val();
if (($fileid==null||$fileid=='')&&(value==null ||value==''))
return false;
else
return true;
}
}//end of callback
}
}
}
})//end of validation
.on("click","#addbutton",function (){
var $template = $('#newClassRowTemplate'),
$clone = $template
.clone()
.removeClass('hide')
.removeAttr('id')
.insertBefore($template)
.find('input')
.each(function(){
$('#classconfigform').formValidation('addField',$(this) );
});
})
.on("click",".removebutton",function () {
if (confirm("Do you want to delete the class?")){
var $removeclass = $(this).closest('tr');
$removeclass.remove()
.find('input')
.each(function(){
$('#classconfigform').formValidation('removeField',$(this) );
});
}
});
});
</script>
Challenges in this development:
1. After add/remove a new row, how to add/remove it from the validation?
There is a simple example (
adding dynamic field) from form validation web site. But the form above is more complicated because this one has multiple rows, and each row has multiple fields.
2. Validation on the images.
(If there is a current image, the new image is not required.
If there is not a current image, the new image is required.)
This validation need to use a callback function.
The callback function examples can be found
here. Since the form above has more rows, so it makes this callback more challenge.