A first knockout custom binding to display twitter bootstrap alerts

Just had a go at my first knockout custom binding. I wanted to be able to bind an observableArray to a div and for that div to display as a twitter bootstrap alert with each array item on a separate line.

If you want to get TB working with knockout quickly then you may want to check out this library.
http://billpull.github.io/knockout-bootstrap/
I decided not to use it for my alerts because I wanted my errors to be in a single dismissable alert. It would be a pain for the user to have to dismiss all the alerts separately. Also, I’m trying to learn knockout so writing my own is all part of the fun. 🙂

So with that out of the way this is what I came up with.
First in my html I have this very simple markup.

<div data-bind="alertList: errors, priority: 'error'"></div>

I’m using my new binding called alertList to bind an array to this div. The priority option will allow me to choose an error, warning, info or success alert.

This is what the JSON looks like coming back from the server. The main things to note are that it’s an array of errors and each error has a message attribute.

[{"message":"Password may not be null","messageTemplate":"{javax.validation.constraints.NotNull.message}","path":"User.createUser.arg0.password","invalidValue":null},
{"message":"Language may not be null","messageTemplate":"{javax.validation.constraints.NotNull.message}","path":"User.createUser.arg0.supportedLanguage","invalidValue":null},
{"message":"Username may not be null","messageTemplate":"{javax.validation.constraints.NotNull.message}","path":"User.createUser.arg0.username","invalidValue":null}]

In the view model we’ll typically have an ajax request to get a rest resource. If that results in an error then we can populate the observable errors array with the response from the server. The custom binding will then magically render these in the div with the correct styling. This is a stripped down example just to give the idea.

define(["jquery", "knockout", "mapping"], function($, ko, mapping)
{
"use strict";
var FormVM = function FormVM()
{
  var self = this;
  self.errors = ko.observableArray();
  self.submit = function()
  {
    $.ajax( {
      url: "test",
      type: "post",
      error: function(data)
      {
        self.errors(data.responseJSON);
      }
    } );
  };
};
return FormVM;
});

Finally we just need the custom binding to glue it all together.

define(["jquery", "knockout"], function($, ko)
{
"use strict";
/**
* Binds an observable array to a dismissable alert box
*/
ko.bindingHandlers.alertList =
{
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext)
{
  var data = valueAccessor();
  var priority = allBindingsAccessor().priority;

  var alertClass = "alert-danger";
  if(priority === "info")
  {
    alertClass = "alert-info";
  }else if(priority === "warning")
  {
    alertClass = "alert-warning";
  }else if(priority === "success")
  {
    alertClass = "alert-success";
  }

  element.style.display = "none";
  element.innerHTML = "<button type=\"button\" data-hide=\"alert\" class=\"close\">x</button>" +
  "<h4 class=\"alert-heading\">Error</h4>";
  element.className = "alert " + alertClass + " alert-dismissable";

  var ul = document.createElement("ul");
  ul.className = "list-unstyled";
  ul.innerHTML = "<li><span data-bind='text: message'></span></li>";
  element.appendChild(ul);

  ko.applyBindingsToNode(ul, { foreach: data });
  return { controlsDescendantBindings: true };
},

update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext)
{
  var value = ko.utils.unwrapObservable(valueAccessor());
  if(value.length>0)
  {
    $(element).slideDown();
  } else {
    $(element).fadeOut("fast");
  }
}
};
});

The init function does all of the initial construction. We take the basic div and add in the bootstrap classes we need. We also add a ul element which we use to create a list of errors from the message property of the array items. We also check the priority option to determine the class used to display the alert (e.g. error or warning etc).

The update function just adds in some jQuery animations to make the alert slideDown when you give it some elements and then fade out if you remove the elements.

I also added the following bit of jQuery to make the dismiss button work. The default behavior for a bootstrap dismissable alert is to remove the element from the page but we want to only hide it so we can later display again if we add more errors to the observable. We add a click event to any element with the data-hide attribute to fade out when clicked.

$(document).on("click", "[data-hide]", function()
{
	$(this).closest("." + $(this).attr("data-hide")).fadeOut("fast");
});

Alert Binding

Source is also available as a gist.

Initialising DataTables with Twitter Bootstrap and Spring MVC Ajax

In the previous post I introduced handling Ajax requests from DataTables using Spring MVC.
Spring MVC with restful DataTables

This is only one side of the story though as I left out how I was doing the initialisation on the client side. I’m using DataTables with twitter bootstrap so my initialisation is doing two things, one setting up communication with the back end and two adding twitter bootstrap styling.

Continuing with the customer grid example this is my grid.

<table class="table table-striped table-bordered" id="customers">
  <thead>
    <tr> <th>ID</th> <th>Name</th> </tr>
  </thead>
  <tbody></tbody>
</table>
<script type="text/javascript">
	$(document).ready(function() {
	    $('#customers').dataTable( {
	    	"sAjaxSource": "/Root/customers/get",
	    	"bProcessing": true,
	    	"bServerSide": true,
	    	"sPaginationType": "bootstrap",
	    	"oLanguage": {"sLengthMenu": "_MENU_ records per page",
	    				  "sInfo": "Displaying _START_ to _END_ of _TOTAL_ records"},
	        "aoColumns":[
				{"mDataProp":"id"},
				{"mDataProp":"name"}
				]
	    } );
	} );	
</script>

The first three properties set up the Ajax processing, pagination type gives us bootstrap style paging buttons and oLanguage allows us to change the standard labels. When it came to mapping the columns I had to use the aoColumns property and setting the mDataProp value. In most of the examples online you’ll see a PHP script returning the JSON as an array but since we’re using Jackson we’re returning an object. The difference is this.

Array

{
  "aaData": [
    [
      "1",
      "Ben Thurley"
    ],
    [
      "2",
      "Another Person"
    ]
  ]
}

Object

{
  "aaData": [
    {
      "id" : "1",
      "name" : "Ben Thurley"
    },
    {
      "id" : "2",
      "name" : "Another Person"
    }
  ]
}

If you’re returning an array then you give DataTables the index of the property that matches the column, as we’re returning objects we have to give the name of the property that matches the column. We do this by setting the mDataProp property.

As an added bonus DataTables sends back these mDataProp values on the Ajax request which allows us to convert the sort column index into a meaningful column name.

The rest of the twitter bootstrap styling steps can be found on the DataTables site.
http://datatables.net/blog/Twitter_Bootstrap_2

It’s somewhat work in progress but I have also worked this into a JSP tag file so I don’t need to keep writing the same stuff in my JSP’s. It should in theory make it easy to change the tale plugin if required at a later date. I just added the following two tag files into WebRoot/WEB-INF/tags/table.

table.tag

<%@ tag language="java" %>
<%@ attribute name="id" required="true" %>
<%@ attribute name="datasource" required="true" rtexprvalue="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<c:set var="org_languagetool_tags_table_outputmode" value="TABLE" scope="request" />
	<table class="table table-striped table-bordered" id="${id}">
	  <thead>
	    <tr><jsp:doBody /></tr>
	  </thead>
	  <tbody></tbody>
	</table>
<c:set var="org_languagetool_tags_table_outputmode" value="SCRIPT" scope="request" />
<c:set var="org_languagetool_tags_table_firstcolumn" value="TRUE" scope="request" />
	<script type="text/javascript">
		$(document).ready(function() {
		    $('#${id}').dataTable( {
		    	"sAjaxSource": "<c:url value="${datasource}" />",
		    	"bProcessing": true,
		    	"bServerSide": true,
		    	"sPaginationType": "bootstrap",
		    	"oLanguage": {"sLengthMenu": "<spring:message code="table.records.count" />",
		    				  "sInfo": "<spring:message code="table.showing.records" />"},
		        "aoColumns":[
		                   	<jsp:doBody />
		                   	]
		    } );
		} );	
	</script>	

column.tag

<%@ tag language="java" %>
<%@ attribute name="field" required="true" %>
<%@ attribute name="label" required="true" rtexprvalue="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<c:choose>
  <c:when test="${org_languagetool_tags_table_outputmode == 'TABLE'}">
    <th><spring:message code="${label}" /></th>
  </c:when>
  <c:when test="${org_languagetool_tags_table_outputmode == 'SCRIPT'}">
    <c:choose>
      <c:when test="${org_languagetool_tags_table_firstcolumn == 'TRUE' }">
        <c:set var="org_languagetool_tags_table_firstcolumn" value="FALSE" scope="request" />
      </c:when>
      <c:otherwise>,</c:otherwise>
    </c:choose>
    {"mDataProp":"${field}"}
  </c:when>
</c:choose>

It’s not required unless you’re putting these in a jar but I also wrote out a tag library descriptor.

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
  <tlib-version>1.0</tlib-version>
  <short-name>Table</short-name>
  <uri>http://bthurley.org/tags/table</uri>
  
  <tag-file>
    <name>table</name>
    <path>/WEB-INF/tags/table/table.tag</path>
  </tag-file>  
  
  <tag-file>
    <name>column</name>
    <path>/WEB-INF/tags/table/column.tag</path>
  </tag-file>
 
</taglib>

By using a tld I’ve been able to specify a uri for importing and using the tag. Otherwise you can give the path to import the tag.

<%@ taglib prefix="tab" uri="http://bthurley.org/tags/table" %>
<tab:table id="customers" datasource="/customers/get" >
  <tab:column field="id" label="customers.field.id" />
  <tab:column field="name" label="customers.field.name" />
</tab:table>

I guess I’ll be adding to these tags as time goes on and I think of new things.

jqGrid RESTful URL formatter

jQuery seems to have become ubiquitous these days and if you want a scrolling, paging, dynamic grid then jqGrid is an excellent plugin. I’ve been using it in my project with a RESTful JSON web service to return the grid content and I needed a way to format the ID column into a URL. First I had a look at the documentation to see what jqGrid has out of the box.

There is a formatter called ‘link’ or ‘showLink’ but it didn’t quite work as I wanted as it forces you to use a URL of the form site.com/page?id=1. My application is using RESTful URL’s so I wanted something more like site.com/page/1. This left me with a couple of options.

  1. Format the URL on the server and return it from the JSON web service
  2. Create a custom formatter

For me 1 was out of the question because this was a formatting issue that belonged in the view layer. My JSON is being returned as a POJO from a spring controller. The conversion to JSON is handled automatically by Jackson. So you can see I didn’t really want this URL formatting going on in the controller, or worse the model layers.

This is the custom formatter I created for this purpose.

jQuery.extend($.fn.fmatter , {
RESTfulLink : function(cellvalue, options, rowdata)
{
var opts = options.colModel.formatoptions.url;
if(typeof opts == 'string')
{
return '<a href="'+opts + cellvalue+'">'+cellvalue+'</a>';
}else{
return cellvalue;
}
}
});

The first line allows us to create a formatter by extending jqGrid. It is also possible to create a formatter as a standalone function but I can’t see any reason why you would need to do this. The rest is standard JavaScript defining a formatter called RESTfulLink. The parameters are the standard parameters required for a formatter function. This can all be found on the jqGrid wiki.

The interesting part that isn’t mentioned in the documentation, or anywhere else I could find, is how to access a formatter option. This might make more sense once we’ve seen how you use this formatter.

<script>
  jQuery("#grid_id").jqGrid({
  ...
     colModel: [
      ...
     {name:'id', index:'id', width:60, align:"center", editable: true, formatter:'RESTfulLink', formatoptions:{url:'site.com/page/'}},
      ... ]
   ... });

We select the formatter in the same way as any of the standard ones. Note that if we’d done this is a standalone function instead of extending jqGrid then the quotes would have been dropped. E.G. formatter:RESTfulLink. The data we need to create the link has been passed in using the formatoptions object. I’ve created a custom parameter called url but since JavaScript is dynamically typed you could pass in whatever you want.

If you look back at the custom formatter you should see the url parameter being used to construct the URL by appending the contents of the ID column to whatever the URL is. I couldn’t find this documented anywhere so I ended up debugging my function and inspecting the parameters passed in. Otherwise there’s no way I would have guessed it would be options.colModel.formatoptions.url!

So that’s my first proper post, I hope it makes sense. Please leave a comment if you liked it or to suggest an improvement.

Thanks

Ben