Friday, March 21, 2014

Simple Example for using jQuery Form Plugin (malsup)

jQuery form plugin is used to 'ajaxify' your HTML pages. Using this plugin we can submit requests using ajax and the plugin provides different configurable options too. In our example we will show how to use 'target' option where we can specify a particular element of the HTML page that needs to be updated with the response received from ajax request. Only this portion of page gets updated and thus gives a good user experience.

Using jQuery form plugin is very much simple.
  1. You need to have a HTML form.
  2. Add jQuery and jQuery Form plugins.
  3. Register your HTML form to use jQuery form plugin. (This can be done by either using ajaxForm or ajaxSubmit methods of the form plugin) 
In below example, we will use a Java Servlet to display a HTML form and then register this form with ajaxForm and ajaxify it's submit method.

Step-1:
  • Create a dynamic web project named test-app in Eclipse.
  • Create a Servlet named DisplaySearchServlet. This servlet is used to present a simple html form.
  • As you can see the required jQuery and jQuery form plugins are added in the HTML page too.
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

/**
 * A Simple Servlet that displays a HTML page with a search form.
 * This search form will be registered with 'form' plugin in such a way that 
 * output obtained on submitting the form is sent to 'search_output' html divison.
 * @author phani
 */

@WebServlet("/DisplaySearchServlet")
public class DisplaySearchServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

@Override
  public void doGet(HttpServletRequest request,HttpServletResponse response)  
    throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println
      ("<html>\n" +
       "<head>\n"+    
       "<script type=\"text/javascript\" src=\"js/jquery-1.9.1.js\"></script>\n"+
       "<script type=\"text/javascript\" src=\"js/jquery.form.js\"></script>\n"+
       "<script type=\"text/javascript\" src=\"js/my_custom.js\"></script>\n"+
       "</head>\n" +
       "<body>\n" +
       "<form id=\"search_form\" name=\"search_form\" method=\"post\" action=\"/test-app/DisplaySearchServlet\">\n" +
       "<fieldset>"+      
       "<legend>Employee Search Criteria</legend>"+
       "<table>\n"+
      
       "<tr height=\"20px\">"+
       "<td width=\"100px\">"+
       "<label for=\"Name\">Name&nbsp;</label>\n"+
       "</td>"+
       "<td width=\"150px\">"+
       "<select height=\"15%\" name=\"NameCondition\" id=\"NameCondition\" >" +
         "<option value=\"Equal\">Equal</option>" +
         "<option value=\"In\">In</option>" +
         "<option value=\"Not Equal\">Not Equal</option>" +
         "<option value=\"Not In\">Not In</option>" +
         "</select>"+
        "</td>"+
        "<td width=\"250px\">"+
        "<input type=\"text\" name=\"Name\" id=\"Name\" size=\"30\"  value=\"\" " +
        "</td>\n"+
        "</tr>"+
       
       "<tr height=\"20px\">"+
       "<td width=\"100px\">"+
       "<label for=\"Role\">Role&nbsp;</label>\n"+
       "</td>"+
       "<td width=\"150px\">"+
       "<select height=\"15%\" name=\"RoleCondition\" id=\"RoleCondition\" >" +
         "<option value=\"Equal\">Equal</option>" +
         "<option value=\"In\">In</option>" +
         "<option value=\"Not Equal\">Not Equal</option>" +
         "<option value=\"Not In\">Not In</option>" +
         "</select>"+
        "</td>"+
        "<td width=\"250px\">"+
        "<input type=\"text\" name=\"Role\" id=\"Role\" size=\"30\"  value=\"\" " +
        "</td>\n"+
        "</tr>"+
       
       "</table>\n"+
       "</fieldset>"+ 
       "<fieldset><legend>Actions</legend>"+
       "<input type=\"submit\" name=\"Retrieve\" id=\"Retrieve\"  value=\"Retrieve\">"+
       "</fieldset>"+
       "</form>"+
       "Below is search_output division, output obtained after submitting form will be populated in this divison"+
       "<div id=\"search_output\">&nbsp;</div>\n"+
       "</body>"+
       "</html>");
  }

protected void doPost(HttpServletRequest request, HttpServletResponse response) 
  throws ServletException, IOException {
 StringBuilder sb = new StringBuilder();
 ServletOutputStream out = response.getOutputStream();
 response.setCharacterEncoding("UTF-8");
 response.setContentType("text/html");
 sb.append("<p>").append("We can write code to retrieve the employee's based on"
   + "above conditions and display them here in table. </p>"
   + "<p>Form Plugin ensures only"
   + "this division (i.e target registered using ajaxForm options) gets updated").append("</p>");
 sb.append("<table border = \"1\" style=\"width:300px\"><tr><th>Firstname</th><th>Lastname</th><th>Role</th></tr>"+
   "<tr><tr><td>Jill</td><td>Smith</td><td>Engineer</td></tr>"+
   "<tr><td>Eve</td><td>Jackson</td><td>Admin</td></tr>"+
   "<tr><td>John</td><td>Doe</td><td>Manager</td></tr>"+
   "</table>");
 out.println(sb.toString());
 out.close();

 }
}
Step -2 :
  • Download the requireds plugins jquery-1.9.1.js and jquery.form.js and put them under 'js' directory in eclipse project's WebContent directory.
  • As you can see from above code, there's another java script with name my_custom.js. This is the javascript we will write in which we will register our HTML form with jQuery form plugin.
Step - 3:
  • Create my_custom.js javascript in the 'js' directory under eclipse project's WebContent directory.
  • We used a javascript variable options and the 'target' property is pointed to 'search_output' HTML division. You can see that this HTML div is defined in the above HTML page.
  • Register our HTML form (search_form) with jQuery form plugin using ajaxForm method.
 $(document).ready(function () {
 var options = {
    target: '#search_output'
 };
 $('#search_form').ajaxForm(options);  
 });
  • Deploy the war file and verify 'DisplaySearchServlet'. This can be seen by typing "http://localhost:8080/test-app/DisplaySearchServlet" in your browser window. (Assuming you deployed the war file on your 'localhost' and default port of your tomcat web server is 8080).
  • Click on 'view source' of the web page and make sure all the javascript files are included. If not, then something wrong with your eclipse project setup.
  • 'DisplaySearchServlet' should show something like below in the html page:

You can select some conditions and input some data and click on 'Retrieve' button. Now the form is configured to send the request back to the same 'DisplaySearchServlet' using 'post' method. (As can be seen in HTML). This request is now sent using ajax as this form is registered with jQuery form plugin.

DisplaySearchServlet's 'doPost' method returns a simple string as response. And the response received is populated in the 'search_output' HTML division only. There should be no change in the form input and selections you made. HTML page looks like below after populating the search_output div with ajax response:


Thus we have a working html form setup with jQuery form plugin where ajax is used to submit the form. In our next post we will see how to combine 'ajax' and 'non-ajax' ways of submitting to the same form. And the difference between ajaxForm and ajaxSubmit methods of the form plugin.

Select2 Autocomplete Vs jQuery UI Autocomplete

Before finalizing on Select2 plugin i tried my hands on jQuery UI AutoComplete plugin too. I used both these plugins mainly from 'remote dataset' point of view. Below is a small comparison of these plugins:
  1. Dependencies: All you need to use Select2 is just jquery plugin. But in case of jQuery UI Autocomplete, there are other dependencies like UI Core, jQuery Widget Factory, Position and Menu plugins.
  2. Wide variety of options supported in Select2. In select2, it's easy to configure 'multiple' values or a different 'separator' or configure 'maximumSelectionSize'. Everything that's needed was thought and options/callback functions to override are provided. In case of jQuery UI Autocomplete plugin, we have to do custom Javascript coding to achieve even a simple functionality like allowing 'multiple' values.
  3. Select2 supports lazy-appending of results when the result list is scrolled to the end. (Provided the remote service supports some kind of pagination mechanism). I haven't found anything similar to this in jQuery Autocomplete.
  4. By default select2 doesn't allow adding a new choice from user's search term whereas jQuery Autocomplete UI allows. In case of select2 we have to use 'createSearchChoice' callback function as explained in previous post, for creation of new choices that are not available from remote query result.
  5. Finally the JSON response expected from remote request is different in both cases. In case of jQuery Autocomplete plugin, the JSON remote response should be an array of objects like below:
    [{"value":"Phani"},{"value":"Kiran"}]
    In case of select2 plugin, objects in the JSON response should have both 'id' and 'text' properties like below :
    [{"id":"1","text":"Phani"},{"id":"2","text":"Kiran"}]
    It's also possible to just have 'text' property, but in that case we need to use the 'id' call-back function of select2 as 'id' is mandatory for select2 plugin to work. Refer to this post for related information.

Thursday, March 20, 2014

Jquery's Document Ready Handler

Writing Unobtrusive JavaScript comes with some other considerations.
  1. First and foremost moving the 'behavior' (i.e JavaScript code) outside the markup results in increased lines of code. Here Jquery comes to our aid in accomplishing more with less lines of code. And it inherently takes care of the browser incompatibilities too. So we can very well say that we write Unobtrusive JavaScript  mostly in 'Jquery' style/notaion. (You might have noticed the Jquery style in the example in previous link)
  2. Unobtrusive JavaScript talks about registering necessary event handler programmatically. Where we can register these? window.onload handler is a traditional choice for this.
  3. Disadvantage of putting this code in window.onload handler is - for this code to get executed we have to wait until all the external resources (images, videos etc etc present in HTML) are fully loaded. This results in poor user experience as the 'rich behavior' defined in scripts is not seen until browser finishes loading and displaying every image and resource.
  4. This disadvantage questions the approach oj Unobtrusive Javascript itself.
Jquery's solution to this problem is to use document ready handler.Here we instruct the browser to wait until only DOM is loaded before executing the code. (No waiting for loading of external images etc). And Jquery handles this in a consistent manner across different browsers.

<input type="radio" name="view_radio" id="global" onclick="selectList(this.value)">

$(document).ready(function() {
   $('[name="view_radio"]:radio').click(function() {  
      var checked = $('[name="view_radio"]:radio:checked').val();
      if(checked == "X") {       
        // do X processing         
      } else {
        // do else processing
      }
  }
});

What is 'Unobtrusive JavaScript'?

This is the first interesting term i came across when i started reading Jquery In Action.
  • Unobtrusive JavaScript is about 'separating the behavior (i.e JavaScript) from the Web Page Structure/Markup (i.e HTML).
  • This is very much similar to separating the 'Style' (i.e CSS) from the HTML.
  • Following this approach achieves 'separation' of responsibilites- i.e All style related information is captured in separate CSS files, All Java Script behavior (Handling of events etc etc) is captured in separate JS files and the web page will just contain the HTML markup.
So the next question is how to write Unobtrusive JavaScript (or how not to write obtrusive JavaScript)?
  • Don't put any script blocks within the body of the page.
  • Don't write in-line JavaScript. Below line is an example of obtrusive JavaScript:
    <input type="radio" name="view_radio" id="global" onclick="selectList(this.value)">
    

    The JavaScript function that is invoked when a radio button is selected is defined as an attribute of a HTML element - i.e Behavior and markup are combined.Unobtrusive solution is to separate the behavior from markup (i.e remove the onclick attribute in HTML) and register the necessary event handlers programmatically.
$('[name="view_radio"]:radio').click(function() {  
     var checked = $('[name="view_radio"]:radio:checked').val();
     if(checked == "X") {       
   // do X processing         
     } else {
  // do else processing
}
}

Improved maintainability and solving browser incompatibilities are the main advantages of following this approach. So next time you see an inline handler coded directly in HTML , think about how to remove it and register for the events directly in JavaScript.

Friday, March 14, 2014

How to add a new selectable choice from user's search term in Select2 ?

In our previous example we saw how to integrate select2 plug-in with remote data retrieved in JSON format. As per the bare bones select2 options we used till now, we can select only options available from the remote data query output. What if we want to include a new choice on the fly? For this we have to include the createSearchChoice call-back function while invoking the select2's constructor.

Below is an extract from select2's documentation for 'createSearchChoice':

Creates a new selectable choice from user's search term. Allows creation of choices not available via the query function. Useful when the user can create choices on the fly, eg for the 'tagging' usecase.

createSearchChoice : createNewSelectItem, 
And then define the function like below outside the select2 constructor:
        
   function createNewSelectItem(term,data) {
       if ($(data).filter (function() {return this.text.localeCompare(term)===0;}).length===0) {
               return { id:term, text:term };
      } 
   }
If a new choice is created it is displayed first in the selection list so that user may select it by simply pressing enter.

After adding the 'createSearchChoice' call-back function to our example, we can select new students like Robert, Stephen etc as shown in the below screenshot even though they are not part of the JSON response received from the remote call.



Tuesday, March 11, 2014

How to add 'Tooltip' to Select2 ?

A common requirement when using select2 plug-in is to add a 'tooltip' that will be visible when we hover the mouse over the selected element. This requirement is a must when we have some issues with width and a part of the selected element is not visible.

Adding tooltip with this plug-in is cakewalk.(Once you find out how to do :) )

The select2 plug-in constructor has 2 call-back function parameters - formatSelection and formatResult  that can be used to render the current selection and the result.

These methods can return either a HTML string, a DOM element, or a jQuery object that renders the selection. We can use these methods to return a 'div' element whose 'title' attribute is populated with the text. Actually by default select2 plug-in returns each selected element as a 'div'. It's just we are adding a 'title' attribute to it for adding tooltip. Below is an example. Add these 2 parameters in the select2 constructor :

formatResult: format,
formatSelection: format
And then define the 'format' function like below outside the select2 constructor:

        
function format(item) {
        var originalText = item.text;
        return "<div title ='" + originalText + "'>" + originalText + "</div>";
}

We can use these call-back functions for more advanced rendering  too. Here in our example, we are rendering (displaying) both the result and the selection similarly. You can render them separately too if required. Refer to the 'Loading Remote Data' example in Select2 documentation . In that example, a remote request is made to rottentomatoes and 'formatResult' function is used to render result's (i.e movie's) poster thumbnail and synopsis. But the 'formatSelection' function is used to render just the movie's name in the Select2 .

Sunday, March 9, 2014

Select2 gotcha's and Other Useful Parameters


  • If you followed the steps in previous post, you could have a working select2 where select options are filled with JSON response coming from a remote request. But you would find it unable to select an option from the drop-down. This is because Select2 requires an id property to be set for items returned in the 'results' parameter. This can be done in 2 ways:

  1. Add 'id' object in the JSON response, in which case the response will be an Array of JSON objects like below:
  2. [{"id":"1","text":"Abhijeet"},{"id":"2","text":"Abhishek"},{"id":"3","text":"Anurag"},{"id":"4","text":"Anuj"},{"id":"5","text":"Gaurav"},{"id":"6","text":"Subodh"},{"id":"7","text":"Phani"}]
    
  3. Use the 'id' call-back function. So in our original example from previous post, we can add the below option in the select2 constructor (in custom_myselect.js). Remember this is not part of 'ajax' options, it is a separate option by itself.
    id: function(data) { return data.text; }
Going with option 1 of adding 'id' in the JSON response, i observed that after selection, if we submit the form the value(s) given in 'id' are sent to get or post action. As a user, we might think that the value selected - i.e 'text' should be forwarded, but gotcha. Option 2 solve this issue and therefore i prefer using it.


  • When 'multiple' option is set to true - i.e select2 is configured to have multiple options selected - by default comma (,) is used as separator. If in case your data also includes commas, you can use the 'separator' option to define your own separator.
  • 'initSelection'  function call-back  allows the user to initialize the selection based on the value of the element select2 is attached to. Make note that this doesn't work if the element (to which select2 is attached) is not already populated with a value. In that case use the 'data' method to set the selection.
  • minimumInputLength - Number of characters that are necessary before making a request.