Sunday, February 5, 2012

Taking The New Wicket Plugin For Netbeans For A Test Ride

I was very, very pleased and happy to read Geertjan's blog entry announcing his updated and shall I dare say modern (my words, of course) Wicket Plugin for Netbeans v7.1. Previous versions of the Wicket Plugin were tied to the Wicket libraries that were included within the plugin's nbm files. Whenever you created a new Wicket project the plugin would contribute those jars to the new project. If you wanted your project to use a different version of those jars you had to remove the Wicket jars from your project and then add the version of the jars that you preferred. Not that this was a big deal, mind you, but it just seemed, to me at least, that this was an unnecessary built-in dependency.

In this release Geertjan has removed all such dependencies while also improving the plugin's synergy with Netbeans Library Manager. Netbeans allows you to create your own libraries using its Library Manager. The libraries you create are nothing more than named collections of 1 or more jars that when selected are added to your project's class path. When creating a new project the new Wicket plugin will list all libraries that you have created using the Library Manager whose names start with 'wicket'. If you create your own library for Wicket v1.5.3 for example and name it Wicket-1.5.3 then when you create a new Wicket project the plugin will find it and offer it as an option in the Version list of libraries. If you select it from the list the jar files associated with the library will be included in your new project.


In the above snapshot you can see that in the Version list on the Frameworks page the libraries whose names all start with wicket (case insensitive) are listed as options.

While I have always believed that the Wicket plugin propelled Netbeans to the height of being the preeminent IDE for Wicket development, I am further convinced of that by the release of this latest version of the Wicket Plugin. Great job, Geertjan and might I be so presumptuous as to offer you much thanks, not just from myself but from the Wicket developer community in general, for your continued effort and dedication. Marvelous!

Sunday, December 25, 2011

Merry Christmas And A Happy New Year Message... The Wicket Way

add(new Label("label","Wishing You All A Very Merry Christmas And A Happy New Year!"));

Wednesday, December 21, 2011

A Reusable jQuery UI Autocomplete Wicket Component

* One in a series of articles on Wicket Interfaces & Methods To Master *

In my previous article I demonstrated using Wicket's AbstractAjaxBehavior to integrate with the jQuery UI Autocomplete component. In this article I build upon those concepts to create a reusable JQueryAutocompleteTextField. If you haven't read my previous article please do so now. Also, this article assumes you know how to add libraries (jars, libraries, projects) to a project using Netbeans.

Note: you may check out read-only working copies of the source for this article anonymously over HTTP:

  • The wicket/jQuery integration library svn checkout - http://wicket-jquery-integration.googlecode.com/svn/trunk/ wicket-jquery-integration-read-only
  • The demo application svn checkout - http://wicket-jquery-integration-demo-app.googlecode.com/svn/trunk/ wicket-jquery-integration-demo-app-read-only

Wicket provides numerous reusable components and behaviors that makes page composition simple. Any custom components that we develop should be just as easy to use and be as reusable across projects as well. These factors serve as motivation for encapsulating all the logic I presented in the previous article into a fully self contained reusable component hosted in its own jar file which can be reused in any project just like any one of Wicket's standard components. 

I will be using Netbeans v7.0.1 throughout this article. If you aren't using Netbeans along with the Wicket Plugin for your Wicket development then you aren't using the best IDE available for Wicket development period. This powerful duo provides a level of support for Wicket development that no other IDE can offer. I highly recommend that if you haven't done so already to please download and install Netbeans and then install the Wicket Plugin. You won't regret it, I promise you.

This article will be a little more in depth than my previous ones. So go make yourself a pot of coffee and then lets roll up our sleeves and get on with it then...

Objectives And Requirements

First off, any project worth your effort and time is worth at least minimaly defining its objectives and requirements. Otherwise, how would you know if you are successful or not? So, before we get into the nuts and bolts of the implementation, let us minimally identify the objectives and requirements of the JQueryAutocompleteTextField component:
  1. JQueryAutocompleteTextField must fully encapsulate any resources it requires. In the case of our component, the resource is the Javascript code needed for implementation on the client. Therefore the Javascript should be a packaged resource and adapted and rendered along with the markup for the JQueryAutocompleteTextField. User's shouldn't have to write jQuery script to use JQueryAutocompleteTextField.
  2. JQueryAutocompleteTextField should be just as simple to create as it is to create an out-of-the-box TextField. It also has to be as generic as an out-of-the-box TextField. In the case of JQueryAutocompleteTextField we will extend Wicket's TextField and add our own generic implementation onto that.
  3. JQueryAutocompleteTextField has to be reusable across projects so that means it has to be hosted in its own jar file.
With our requirements and objectives now having been stated, lets move onto the project structure we will need in order to create a reusable Wicket component library.

Create A Library Project

As I previously stated above in Objectives And Requirements, item #3, the JQueryAutocompleteTextField needs to be hosted in its own jar file. Our goal here is to provide a reusable library. Users of this library will have to include it in their classpath in order to compile their own code and they will also need to have the Wicket jar files on their classpath so that the library can resolve its dependencies on Wicket as well.
  1. Fire up Netbeans and from the main menu select File | New Project | Java | Java Application and click Next.
  2. Enter any name you want for the Project Name. I am using WicketJQueryIntegration. If the Use Dedicated Folder for Storing Libraries option is selected, unselect it. Also unselect Create Main Class if it is selected and then click Finish for Netbeans to generate the project.
  3. Right click on the default src package and select Refactor | Rename and enter any package name you wish to use. I am using com.wicket.jquery.integration.autocomplete.
You should now have a project structure that looks this:







The next thing to do is to add Wicket 1.5 to our project. If you don't already have Wicket you can go to http://wicket.apache.org/ and download it. I am using version 1.5.3.

WicketJQueryIntegration also requires google-gson which you can download if you don't already have it. Add it to the project as well.

With our dependencies added to the project and accounted for we need to do one more thing and that is to insure that the libraries we require for compilation aren't packaged in the jar - it is the responsibility of the users of JQueryAutocompleteTextField to provide the dependencies on these in their classpath. If we don't do this, then we are building in a level of dependency on the exact same versions of these libraries and we don't want to do that.

Right click on the project node and select Properties and select Packaging. Uncheck the Copy Dependent Libraries option and click OK.











We now have a project suitable for creating a reusable library that doesn't impose dependencies upon its users and our project structure now looks like this:























Implementing JQueryAutocompleteTextField

We will begin implementation by creating a subclass of AbstractAjaxBehavior. In my previous article we used an implementation of AbstractAjaxBehavior that was tightly coupled to providing support for selecting states. However, because our goal is to create a reusable implementation, one that isn't tightly coupled to any particular use case, our implementation must therefore provide flexibility, allowing the user to implement their own requirements.

Create a Java class in the com.wicket.jquery.integration.autocomplete package. I named mine AbstractJQueryAutocompleAjaxBehavior. In the editor mark the class as abstract and extend the class from AbstractAjaxBehavior.

The constructor needs to take one parameter, a string whose value is a jQuery selector that identifies the target html text field element that will be used to host the jQuery UI Autocomplete component. Our class also needs a field to store this value:


public abstract class AbstractJQueryAutocompleAjaxBehavior extends AbstractAjaxBehavior {

    // The jQuery selector to be used by the jQuery ready handler that registers the autocomplete behavior
    final private String jQuerySelector;

    /**
     * Constructor
     * @param jQuerySelector - a string containing the jQuery selector
     * for the target html element (<input type='text'... of the jQuery UI
     * Autocomplete component
     */
    public AbstractJQueryAutocompleAjaxBehavior(String jQuerySelector) {
        super();
        this.jQuerySelector = jQuerySelector;
    }


As was stated in our Objective And Requirements, item #1, we don't want the users of our component to have to write their own jQuery code to wire up jQuery UI's Autocomplete component and therefore our component must render that code to the client page. Here's the boilerplate javascript that we want to use to render to the client page:

$(document).ready(function(){
    $('${selector}').autocomplete({
        source: function(req, add){
            //pass request to server  
            $.ajax({
                url: '${callbackUrl}',
                type: 'GET',
                cache: false,
                data: req,
                dataType: 'json',
                success: function(json){
                    var suggestions = [];  

                    //process response  
                    $.each(json, function(i, val){  
                        suggestions.push(val.name);  
                    });
                        
                    // call autocomplet callback method with results
                    add(suggestions);
                },
                error: function(XMLHttpRequest, textStatus, errorThrown){
                    //alert('error - ' + textStatus);
                    console.log('error', textStatus, errorThrown);
                }
            });            
        }
    });
});


Create a new Javascript file in the com.wicket.jquery.integration.autocomplete package and name it autocomplete.js. Then copy the code above into it and save the file.

We are faced with a slight dilemma because we have no way of knowing which html element the user is going to use. The solution, as implemented, is to allow the user to tell us that by providing a jQuery selector in the constructor that identifies it. But wait, how can we alter the Javascript to use this selector? Well, fortunately for us Wicket makes this kind of easy. Wicket supports modifying packaged resources through a process known as interpolation which just means replacing one thing in the resource with another thing. In Wicket's case the things that need to be replaced are identified by ${...} markers. In each marker we provide a name for the marker. In the above Javascript file we provide for two markers, ${selector} which will be replaced with the jQuery selector provided in the constructor and ${callbackUrl} which will be replaced by the callback url to our Wicket Ajax event handler which we will later implement by overriding onRequest.

When we interpolate we provide a HasMap that contains key/value objects. Their keys must match the names we gave each marker and their associated values are what will be used to replace the marker with. When our Javascript resource is contributed to the page it will therefore be rendered with both the jQuery selector and the callback URL. Let's implement that now by overriding the renderHead method of AbstractAjaxBehavior:

    /**
     * Contributes a jQuery ready handler that registers autocomplete
     * behavior for the html element represented by the selector.
     * 
     * The generation of the ready handler uses interpolation, applying
     * the jQuery selector and the variable name of the return call
     * back url.
     * 
     * @param component
     * @param response 
     */
    @Override
    public void renderHead(Component component, IHeaderResponse response) {
        super.renderHead(component, response);

        Map<String, CharSequence> map = new HashMap<String, CharSequence>(2);
        map.put("selector", jQuerySelector);
        map.put("callbackUrl", getCallbackUrl());
        PackageTextTemplate packageTextTemplate = new PackageTextTemplate(getClass(), "autocomplete.js", "text/javascript");
        String resource = packageTextTemplate.asString(map);
        response.renderJavaScript(resource, jQuerySelector);
    }

In the code above we create a HashMap and add the jQuery selector and callback url to it, the names of which as you can see match those we used in our markers. Additionally, we obtain a PackageTextTemplate resource for the autocomplete.js file which resides in the same package as this class and then we call its asString method passing it the HashMap. asString will interpolate our resource using the HashMap we provided and return the interpolated content of the resource as a String. That solves our slight dilemma quite nicely I think and with very little code.

Finally we need to actually render the resource to the client page. This is done by calling renderJavaScript, passing it the interpolated resource and the jQuerySelector parameter as a unique id.

Besides rendering the Javascript we must also handle the actual Ajax callback by overriding the onRequest method. Remember, when we stated our objective and requirements we said in item #2 that the component has to be generic. All that means is that we won't assume how the user intends to use the component. Here is the code for our generic implementation of onRequest :

    @Override
    public void onRequest() {
        Logger logger = LoggerFactory.getLogger(this.getClass());
        logger.info("ajax request received");

        RequestCycle requestCycle = RequestCycle.get();
        Request request = requestCycle.getRequest();
        IRequestParameters irp = request.getRequestParameters();
        StringValue term = irp.getParameterValue("term");
        List<?> matches = getMatches(term.toString());
        String json = convertListToJson(matches);
        requestCycle.scheduleRequestHandlerAfterCurrent(new TextRequestHandler("application/json", "UTF-8", json));
    }


In the code above we retrieve the parameter passed to the callback which is the term the user entered into the TextField tied to the jQuery Ui Autocomplete component. We use that term by passing it to an abstract method which we define as follows:

public abstract List<?> getMatches(String term);

Because we declared this method as abstract it will have to be implemented by the user of AbstractJQueryAutocompleAjaxBehavior which I will get to shortly but this is one way we keep this generic, by deferring implementation of this method. We also keep this generic by defining this method's return value using generics and in our case as  a List of some object.

Finally, onRequest then calls convertListToJson, a method that converts the List of objects returned from the getMatches method to a Javascript array of JSON objects. That array is then returned to the client which will be passed to the success callback method of the Autocomplete component defined in our Javascript.

Here's the complete code for AbstractJQueryAutocompleAjaxBehavior:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.wicket.jquery.integration.autocomplete;

import com.google.gson.Gson;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.wicket.Component;
import org.apache.wicket.behavior.AbstractAjaxBehavior;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.TextRequestHandler;
import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.template.PackageTextTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author jeffrey
 */
public abstract class AbstractJQueryAutocompleAjaxBehavior extends AbstractAjaxBehavior {

    // The jQuery selector to be used by the jQuery ready handler that registers the autocomplete behavior
    final private String jQuerySelector;

    /**
     * Constructor
     * @param jQuerySelector - a string containing the jQuery selector
     * for the target html element (<input type='text'... of the jQuery UI
     * Autocomplete component
     */
    public AbstractJQueryAutocompleAjaxBehavior(String jQuerySelector) {
        super();
        this.jQuerySelector = jQuerySelector;
    }

    /**
     * Contributes a jQuery ready handler that registers autocomplete
     * behavior for the html element represented by the selector.
     * 
     * The generation of the ready handler uses interpolation, applying
     * the jQuery selector and the variable name of the return call
     * back url.
     * 
     * @param component
     * @param response 
     */
    @Override
    public void renderHead(Component component, IHeaderResponse response) {
        super.renderHead(component, response);

        Map<String, CharSequence> map = new HashMap<String, CharSequence>(2);
        map.put("selector", jQuerySelector);
        map.put("callbackUrl", getCallbackUrl());
        PackageTextTemplate packageTextTemplate = new PackageTextTemplate(getClass(), "autocomplete.js", "text/javascript");
        String resource = packageTextTemplate.asString(map);
        String uniqueName = Long.toString(Calendar.getInstance().getTimeInMillis());
        response.renderJavaScript(resource, uniqueName);
    }

    @Override
    public void onRequest() {
        Logger logger = LoggerFactory.getLogger(this.getClass());
        logger.info("ajax request received");

        RequestCycle requestCycle = RequestCycle.get();
        Request request = requestCycle.getRequest();
        IRequestParameters irp = request.getRequestParameters();
        StringValue term = irp.getParameterValue("term");
        List<?> matches = getMatches(term.toString());
        String json = convertListToJson(matches);
        requestCycle.scheduleRequestHandlerAfterCurrent(new TextRequestHandler("application/json", "UTF-8", json));
    }

    public abstract List<?> getMatches(String term);
    
    /*
     * Convert List to json object.
     * 
     * Dependency on google-gson library which is 
     * available at http://code.google.com/p/google-gson/
     * and which must be on your classpath when using this
     * library.
     */
    private String convertListToJson(List<?> matches) {
        Gson gson = new Gson();
        String json = gson.toJson(matches);
        return json;
    }

}


All that remains for completing our component's implementation is to implement JQueryAutocompleteTextField and this actually is the easiest part to do. Here's the complete code to JQueryAutocompleteTextField:

package com.wicket.jquery.integration.autocomplete;

import java.util.List;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.IModel;


/**
 *
 * @author jeffrey
 */
public abstract class JQueryAutoCompleteTextField<T extends Object> extends TextField<T> {
    
    private final String jQuerySelector;

    public JQueryAutoCompleteTextField(String id, IModel<T> model, Class<T> type, String jQuerySelector) {
        super(id, model, type);
        this.jQuerySelector = jQuerySelector;
        common();
    }

    public JQueryAutoCompleteTextField(String id, IModel<T> model, String jQuerySelector) {
        super(id, model);
        this.jQuerySelector = jQuerySelector;
        common();
    }

    public JQueryAutoCompleteTextField(String id, Class<T> type, String jQuerySelector) {
        super(id, type);
        this.jQuerySelector = jQuerySelector;
        common();
    }

    public JQueryAutoCompleteTextField(String id, String jQuerySelector) {
        super(id);
        this.jQuerySelector = jQuerySelector;
        common();
    }

    private void common(){
        add(new AbstractJQueryAutocompleAjaxBehavior(jQuerySelector) {

            @Override
            public List<?> getMatches(String term) {
                return JQueryAutoCompleteTextField.this.getMatches(term);
            }
        });
    }
    
    public abstract List<?> getMatches(String term);
}


In the code above we provide numerous constructors allowing the user versatility in how they construct the component. Additionally, each constructor calls the method common which adds an instance of AbstractJQueryAutocompleAjaxBehavior to it. Because in AbstractJQueryAutocompleAjaxBehavior we declared getMatches as abstract we are here forced to provide for its actual implementation. All our implementation does is call JQueryAutocompleteTextField's getMatches method which is also declared as abstract. By defining this abstract method in JQueryAutocompleteTextField our users will have to provide their own implementation which will depend upon their individual use-cases and fulfills our requirement that this component provide a generic implementation allowing it be used however the user's use-case might dictate.

That wraps up our implementation of JQueryAutocompleteTextField but a few things remain to be done. First we need to build the jar file so that we can use JQueryAutocompleteTextField in our Wicket applications and secondly we need to test it. We will cover both beginning with building the jar file.

Building The Jar File

To build the components jar file right click the project node and select Clean And Build. Netbeans will compile the project and package the component in its own jar file.

Notice how none of the libraries we are using to build the component are in the jar file - this is because when we created the project we instructed Netbeans not to include them. Users of the component will now be able to use what ever versions of these dependencies they see fit to use.

Now all that remains is for us to test our component to make sure that it actually works so lets do that now by creating a small Wicket test application using Netbeans. Make sure you have already installed the Wicket Plugin.
  
Testing Our Implementation
  1. From Netbeans main menu select File | New Project | Java Web | Web Application which will open the New Project wizard.
  2. In the wizard enter any name you like for the test project. I named mine WicketAndJQueryAjax and make sure that the Set As Main Project option is selected. Click Next. 
  3. Select either GlassFish Server or Apache Tomcat for the server option and click Next.
  4. From the list of available Frameworks select Wicket and then click Finish.
Netbeans will generate a complete Wicket project including the Wicket jars that the Wicket Plugin contributes. Because our component is dependent upon Wicket v1.5 we will have to replace the ones provided by the Wicket Plugin with the 1.5 versions. So remove all the contributed Wicket jars and replace them with those from the 1.5 distribution. Also, add the google-gson jar file to the project as well. 

We also need to include our components jar also:
  1. Right click on the test project node and select Properties.
  2. From the Categories pane select Libraries and click the Add Project button. 
  3. In the Add Project window select our components project, WicketJQueryIntegration and click the Add Project Jar Files button.
We now want to instruct Netbeans to build our components project when we build our test project:
  1. Right click on the test project node and select Properties and select Libraries from the Categories pane in the Project Properties window.
  2. Select the option Build Required Projects and click the OK button.
Now, whenever we build out test project, Netbeans will check to see if our components project needs to be rebuilt and will do so first before building our test project.

We won't be using Wicket's markup inheritence in our project since it is quite simple so delete the following files from the com.myapp.wicket package:
  • BasePage.java
  • BasePage.html
  • FooterPanel.java
  • FooterPanel.html
  • HeaderPanel.java
  • HeaderPanel.html 
Our test project's structure should now look like this:































Our test project will mimic what my previous article did which was to implement an Autocomplete component that allowed the user to select from a list of states that match their input. 

Open the HomePage.html file in the editor by double clicking on it. When you do, both it and the HomePage.java file will open. This behavior, one of many, is contributed by the Wicket Plugin.

With the HomePage.html file now opened in the editor, replace all of its content with the following:

<!DOCTYPE html> 
<html xmlns:wicket="http://wicket.apache.org"> 
    <head> 
        <meta charset="UTF-8"> 
        <title>Wicket Example</title> 
        <link type="text/css" href="css/start/jquery-ui-1.8.16.custom.css" rel="Stylesheet" />    
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/jquery-ui-1.8.16.custom.min.js"></script>
    </head> 
<body>
    <h1>Marrying Wicket And jQuery UI Auto Complete Ajax</h1>
    <h2>jQuery UI Auto Complete State Lookup</h2>
    <form class="jqueryid_form2" wicket:id="form1">
        US State Lookup: <input class="jqueryid_state" type="text" wicket:id="state"/><br/>
    </form>
</body> 
</html>


Notice that in the above there is no reference to a jQuery UI Autocomplete component, just the normal markup one would expect in any Wicket application. Also notice, there is no jQuery ready handler either. The real magic takes place when the component is rendered by Wicket.

Now replace all of the contents of HomePage.java with the following:

/*
 * HomePage.java
 *
 * Created on December 3, 2011, 10:21 AM
 */
package com.myapp.wicket;

import com.wicket.jquery.integration.autocomplete.JQueryAutoCompleteTextField;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.PropertyModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HomePage extends WebPage {

    private final Form<HomePage> form1;
    private final TextField<String> stateTextField;
    private String state = "";

    /*
     * A State class jsonified into an array and returned to the client
     */
    static class State {

        State() {
        }

        State(String name) {
            this.name = name;
        }
        String name;
    }

    private static class StatesDb {

        private static final String[] states = new String[]{
            "Alabama",
            "Alaska",
            "Arizona",
            "Arkansas",
            "California",
            "Colorado",
            "Connecticut",
            "Delaware",
            "Florida",
            "Georgia",
            "Hawaii",
            "Idaho",
            "Illinois",
            "Indiana",
            "Iowa",
            "Kansas",
            "Kentucky",
            "Louisiana",
            "Maine",
            "Maryland",
            "Massachusetts",
            "Michigan",
            "Minnesota",
            "Mississippi",
            "Missouri",
            "Montana",
            "Nebraska",
            "Nevada",
            "New Hampshire",
            "New Jersey",
            "New Mexico",
            "New York",
            "North Carolina",
            "North Dakota",
            "Ohio",
            "Oklahoma",
            "Oregon",
            "Pennsylvania",
            "Rhode Island",
            "South Carolina",
            "South Dakota",
            "Tennessee",
            "Texas",
            "Utah",
            "Vermont",
            "Virginia",
            "Washington",
            "West Virginia",
            "Wisconsin",
            "Wyoming"
        };

        static List<State> getStatesLike(String target) {

            List<State> matches = new ArrayList<State>();
            for (String s : states) {
                if (s.toLowerCase().startsWith(target.toLowerCase())) {
                    State state = new State(s);
                    matches.add(state);
                }
            }
            return matches;
        }
        
    }

    public HomePage() {    /*
         * A private static class for states which supports
         * querying for states that are like target and 
         * returning those in a list.
         */

        add(form1 = new Form<HomePage>("form1") {

            @Override
            protected void onSubmit() {
                super.onSubmit();
                Logger logger = LoggerFactory.getLogger(this.getClass());
                logger.info("The user selected: " + state);
            }
        });

        /*
         * A TextField with JQueryAutocompleteBehavior
         */
        form1.add(stateTextField = new JQueryAutoCompleteTextField<String>("state", 
                new PropertyModel<String>(this, "state"), "input.jqueryid_state") {

            @Override
            public List<?> getMatches(String term) {
                List<State> statesLike = StatesDb.getStatesLike(term.toString());
                return statesLike;
            }
        });

    }
}


Notice in the above that we add a JQueryAutoCompleteTextField to the form and that we also provide our own implementation for its getMatches method allowing us to tailor the component to our specific use case which is to find a list of states that match the value of term.

Run the project and it should render the following in the browser:












Now start slowly typing New York into the text box and as you do will be presented with a list of choice matching what characters you have entered:
















Nice, hey? But wait, there's more. Select one of the suggestions from the list and it will propagate the text box. Once propagated, hit Enter to submit the form and now look at the server's log file and you will see log messages that your selection was submitted with the form.

Magicians are never supposed to reveal their tricks to the public but browser pages have no such ethics so now let us peek at the markup that was rendered to the client and relate that back to our component:
































I have highlighted the points of interest in red that relate back to our component. As you can see, the jQuery ready handler itself was contributed and our component tailored it to use the selector we provided it. In addition, it also used the callback url. The success function receives the json object we responded with in our component's onRequest method which had called our getMatches method that we implemented in HomePage.java which returned a List of State objects.

Summary

Wicket v1.5 provides awesome flexibility by allowing easy integration with other component libraries. . The same techniques I used here can be applied in many other cases to create other types of components. The only limits are your imagination so imagine! My imagination tells me that there are many other jQuery UI components just waiting for a similar approach. Hmmm...

I hope you have enjoyed following along with me developing an awesome Wicket/jQuery Ui Autocomplete component. Feel free to leave your comments and please remember to come back soon as I am always cooking something new up to share with you.

Saturday, December 17, 2011

Marrying Wicket And jQuery UI Autocomplete Ajax

* One in a series of articles on Wicket Interfaces & Methods To Master *



Note: the code demonstrated in this article uses the query (v1.6.2) and jquery.ui (v1.8.16) Javascript libraries for client-side scripting and the Google gson library for servers-side JSON support.

Here we pick up from my previous article, Marrying Wicket And jQuery Ajax, but today I will demonstrate how to marry Wicket and jQuery UI, specifically the jQuery UI autocomplete component.

Auto completion allows the user to quickly lookup and select values with minimum key strokes, a fact that has made it a very popular user interface design element. And while it is certainly possible to code the javascript necessary to support auto completion by hand, the jQuery UI library provides a very nice implementation and as a bonus you also get all the graphical sugar that the UI provides.

When I began trying to implement this I tried Googling to find out how others have approached this and as a result I learned that no one seemed to have posted a pure Wicket solution but rather relied upon wiQuery's implementation. As I prefer to not use wiQuery and to roll my own reusable "pure" Wicket based solutions I came up with the solution that you will find here in this article which only uses Wicket 1.5 and the jQuery UI library. So lets roll up our sleeves and get on with it...

Here's the Java code:

/*
 * HomePage.java
 *
 * Created on December 3, 2011, 10:21 AM
 */
package com.myapp.wicket;

import com.google.gson.Gson;
import java.lang.String;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.behavior.AbstractAjaxBehavior;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.TextRequestHandler;
import org.apache.wicket.util.string.StringValue;

public class HomePage extends WebPage {

    private final Form<HomePage> form1;
    private final AbstractAjaxBehavior aab1;
    private final TextField<String> state;

    static class State {

        State() {}

        State(String name) {
            this.name = name;
        }
        
        String name;
    }

    public HomePage() {

        add(form1 = new Form<HomePage>("form1"));

        form1.add(state = new TextField<String>("state"));

        /*
         * Ajax Behavior provides an event
         * listener as well as the request
         * handler.
         */

        add(aab1 = new AbstractAjaxBehavior() {

            @Override
            public void renderHead(Component component, IHeaderResponse response) {
                super.renderHead(component, response);
                response.renderJavaScript("var callbackUrl = '" + aab1.getCallbackUrl() + "';", "callbackurl");
            }

            // handle the ajax request
            @Override
            public void onRequest() {
                System.out.println("ajax request received");

                RequestCycle requestCycle = RequestCycle.get();
                Request request = requestCycle.getRequest();
                IRequestParameters irp = request.getRequestParameters();
                StringValue state = irp.getParameterValue("term");
                List<State> statesLike = MockDb.getStatesLike(state.toString());
                String json = convertListToJson(statesLike);
                requestCycle.scheduleRequestHandlerAfterCurrent(new TextRequestHandler("application/json", "UTF-8", json));
            }
        });

    }

    /*
     * Convert List to json object
     */
    private String convertListToJson(List<?> matches) {
        Gson gson = new Gson();
        String json = gson.toJson(matches);
        return json;
    }

    /*
     * A Mock Database
     */
    static class MockDb {

        private static final String[] states = new String[]{
            "Alabama",
            "Alaska",
            "Arizona",
            "Arkansas",
            "California",
            "Colorado",
            "Connecticut",
            "Delaware",
            "Florida",
            "Georgia",
            "Hawaii",
            "Idaho",
            "Illinois",
            "Indiana",
            "Iowa",
            "Kansas",
            "Kentucky",
            "Louisiana",
            "Maine",
            "Maryland",
            "Massachusetts",
            "Michigan",
            "Minnesota",
            "Mississippi",
            "Missouri",
            "Montana",
            "Nebraska",
            "Nevada",
            "New Hampshire",
            "New Jersey",
            "New Mexico",
            "New York",
            "North Carolina",
            "North Dakota",
            "Ohio",
            "Oklahoma",
            "Oregon",
            "Pennsylvania",
            "Rhode Island",
            "South Carolina",
            "South Dakota",
            "Tennessee",
            "Texas",
            "Utah",
            "Vermont",
            "Virginia",
            "Washington",
            "West Virginia",
            "Wisconsin",
            "Wyoming"
        };

        static List<State> getStatesLike(String target) {

            List<State> matches = new ArrayList<State>();
            for (String s : states) {
                if (s.toLowerCase().startsWith(target.toLowerCase())) {
                    State state = new State(s);
                    matches.add(state);
                }
            }
            return matches;
        }
    };
}


In the code above we create an AbstractAjaxBehavior and add it to the page to provide a request handler for the ajax call. Our AbstractAjaxBehavior class overrides two methods:

  1. renderHead, in which we grab the behavior's call back url and contribute it via Javascript to the page where it will be used as the url to call by the jQuery UI autocomplete method.
  2. onRequest, in which we process the ajax request and in which we use the request parameter whose name is 'term' to look up all the states that begin with its value and convert the names of those states to an array of json objects in the form of [{name : "statename"}...]. Finally, we schedule a TextRequestHandler passing that array as a parameter which will be sent back to the client as the result of the ajax call. Note that our response content type is "application/json", "UTF-8". 

And that's really all it takes for Wicket v1.5 to support ajax request that must return json. Now, lets take a look at the markup including the Javascript needed to support our integration with jQuery UI and Wicket:

<!DOCTYPE html> 
<html xmlns:wicket="http://wicket.apache.org"> 
    <head> 
        <meta charset="UTF-8"> 
        <title>Wicket Example</title> 
        <link type="text/css" href="css/start/jquery-ui-1.8.16.custom.css" rel="Stylesheet" />    
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/jquery-ui-1.8.16.custom.min.js"></script>
        <wicket:head>
        </wicket:head>    
        <script type="text/javascript" >
        $(document).ready(function(){
            $('input.jqueryid_state').autocomplete({source: function(req, add){
                //pass request to server  
                $.ajax({
                    url: callbackUrl,
                    type: 'GET',
                    cache: false,
                    data: req,
                    dataType: 'json',
                    success: function(json){
                        var suggestions = [];  

                        //process response  
                        $.each(json, function(i, val){  
                            suggestions.push(val.name);  
                        });
                        
                        // call autocomplet callback method with results
                        add(suggestions);
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown){
                        //alert('error - ' + textStatus);
                        console.log('error', textStatus, errorThrown);
                    }
                });            
            }});
        });
        </script>
    </head> 
<body>
    <h1>Marrying Wicket And jQuery Ajax</h1>
    <h2>jQuery UI Auto Complete State Lookup</h2>
    <form class="jqueryid_form2" wicket:id="form1">
        US State Lookup: <input class="jqueryid_state" type="text" wicket:id="state"/><br/>
                    <div id="statelist"/>
    </form>
</body> 
</html>


In the above Javascript code we create a jQuery ready handler which sets up jQuery UI autocomplete. Here we use a json object that contains the property named source and which provides a callback function that autocomplete will call to retrieve the results matching the user's input. In this callback we use jQuery's ajax method to make an ajax call back to the Wicket event listener whose url is stored in the global variable callbackUrl which we obtained from the AbstractAjaxBehavior we created in our Java code.

Finally, the result from the ajax call, which is an array of json objects whose val properties are the names of the states are passed back to autocomplete by calling the add callback method. jQuery Ui autocomplete uses these to present a list of options to the user as can be seen from the following screen shot:

That's pretty impressive I think, especially considering the minimal amount of code we had to write, both on the client and the server. Being able to use this technique along with using Wicket's default Ajax implementation is empowering, allowing us to use the right technologies that best suits the use case.

I hope you enjoyed following along with me and please remember to come back soon because I always have something interesting to write about Wicket.

Sunday, December 4, 2011

Marrying Wicket And jQuery Ajax

* One in a series of articles on Wicket Interfaces & Methods To Master *

Note: the code demonstrated in this article uses the jquery and jquery.form Javascript libraries for client-side scripting and the Google gson library for servers-side JSON support.

One of the pleasures of working with Wicket is how easy it makes doing Ajax based DOM replacement. Wicket Ajax wires up an event listener and ties it to a DOM event such as on click, keyup, etc., and upon receipt of the Ajax request hands off an AjaxTarget to an event handler. In the event handler you have access to any form input values with which you can update a database record and update the page by adding components to the AjaxTarget.

Sometimes, though, a Web page requires some Ajax processing that doesn't neatly fit into the mold which Wicket provides out of the box. For instance, one friction point with Wicket is integration with jQuery's Ajax implementation. jQuery is the preeminent Javascript framework and for good reason: it makes doing the hard stuff easy including initiating Ajax requests based on the user actions on the page.

What then, does it take to marry (so to speak) the strengths of both jQuery and Wicket and benefit from their combined use as far as Ajax is concerned? Well, as I will show you, it isn't hard at all and in fact it is quite easy! Ok, so lets get started.

Note: For the two examples I am presenting here I am using the latest version of jQuery as well as the jQuery Form plugin. For example 2 I am also using the Google gson library which provides excellent JSON functionality and which is very easy to use.

The first example demonstrates how to use jQuery and the jQuery Form plugin to submit a form via Ajax and how to wire up Wicket to process the request as well as send back a response. Here, the user enters their first and last name and clicks the form's submit button which causes the form to be submitted via Ajax and a response sent back to the client from Wicket which mirrors their input. The key point to take away from this example is the use of Wicket's AbstractAjaxBehavior to create an event listener as well as an event handler which packages a response back to the client.

The second example demonstrates how to use jQuery and the jQuery Form plugin to implement a dynamic lookup list which consists of an input field in which the user can type and a dynamically created and displayed list of states that match the user's input. As with the first example, the key point to take away from this example is the use of Wicket's AbstractAjaxBehavior.

In both examples, client side Javascript using jQuery is responsible for the heavy lifting meaning it is the client code that wires up the events that initiate the Ajax responses and not Wicket. This is one clear difference in implementations between the way Wicket does Ajax and the way Ajax is done using client side code.

The following Java and Markup/Javascript code is for both examples, 1 and 2

 - Java Wicket Code -

/*
 * HomePage.java
 *
 * Created on December 3, 2011, 10:21 AM
 */
package com.myapp.wicket;


import com.google.gson.Gson;
import com.sun.tools.internal.jxc.apt.Const;
import java.lang.String;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.behavior.AbstractAjaxBehavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.TextRequestHandler;
import org.apache.wicket.util.string.StringValue;


public class HomePage extends WebPage {


    private Form form1;
    private final TextField firstName;
    private final TextField lastName;
    private final AbstractAjaxBehavior aab1;
    private final Form form2;
    private final TextField state;
    private final AbstractAjaxBehavior aab2;


    public HomePage() {
        /* Example 1
         * Ajax Behavior provides an event
         * listener as well as the request
         * handler.
         */
        add(aab1 = new AbstractAjaxBehavior() {


            // handle the ajax request
            @Override
            public void onRequest() {
                System.out.println("ajax request received");


                RequestCycle requestCycle = getComponent().getRequestCycle();
                Request request = requestCycle.getRequest();
                IRequestParameters irp = request.getPostParameters();
                StringBuilder sb = new StringBuilder();
                sb.append("firstName: " + irp.getParameterValue("firstName"));
                sb.append(", ");
                sb.append("lastName: " + irp.getParameterValue("lastName"));
                requestCycle.scheduleRequestHandlerAfterCurrent(new TextRequestHandler(sb.toString()));
            }
        });




        /*
         * A form whose action tag will be set to the 
         * above behavior's call back url.
         */
        add(form1 = new Form("form1") {


            // set the form's action tag to the behavior's call back url
            @Override
            protected void onComponentTag(ComponentTag tag) {
                super.onComponentTag(tag);
                tag.put("action", aab1.getCallbackUrl());
            }
        });


        /*
         * Form inputs for first and last name.
         */
        form1.add(firstName = new TextField("firstName"));


        form1.add(lastName = new TextField("lastName"));


        /* Example 2
         * Ajax Behavior provides an event
         * listener as well as the request
         * handler.
         */
        add(aab2 = new AbstractAjaxBehavior() {


            // handle the ajax request
            @Override
            public void onRequest() {
                System.out.println("ajax request received");


                RequestCycle requestCycle = getComponent().getRequestCycle();
                Request request = requestCycle.getRequest();
                IRequestParameters irp = request.getPostParameters();
                StringValue state = irp.getParameterValue("state");
                List statesLike = MockDb.getStatesLike(state.toString());
                requestCycle.scheduleRequestHandlerAfterCurrent(new TextRequestHandler(convertListToJson(statesLike)));
            }
        });


        add(form2 = new Form("form2") {


            @Override
            protected void onComponentTag(ComponentTag tag) {
                super.onComponentTag(tag);
                tag.put("action", aab2.getCallbackUrl());
            }
        });


        form2.add(state = new TextField("state"));
    }


    /*
     * Convert List to json object
     */
    private String convertListToJson(List matches) {
        Gson gson = new Gson();
        String json = gson.toJson(matches);
        return json;
    }


    /*
     * A Mock Database
     */
    static class MockDb {


        private static final String[] states = new String[]{
            "Alabama",
            "Alaska",
            "Arizona",
            "Arkansas",
            "California",
            "Colorado",
            "Connecticut",
            "Delaware",
            "Florida",
            "Georgia",
            "Hawaii",
            "Idaho",
            "Illinois",
            "Indiana",
            "Iowa",
            "Kansas",
            "Kentucky",
            "Louisiana",
            "Maine",
            "Maryland",
            "Massachusetts",
            "Michigan",
            "Minnesota",
            "Mississippi",
            "Missouri",
            "Montana",
            "Nebraska",
            "Nevada",
            "New Hampshire",
            "New Jersey",
            "New Mexico",
            "New York",
            "North Carolina",
            "North Dakota",
            "Ohio",
            "Oklahoma",
            "Oregon",
            "Pennsylvania",
            "Rhode Island",
            "South Carolina",
            "South Dakota",
            "Tennessee",
            "Texas",
            "Utah",
            "Vermont",
            "Virginia",
            "Washington",
            "West Virginia",
            "Wisconsin",
            "Wyoming"
        };


        static List getStatesLike(String target) {


            List matches = new ArrayList();
            for (String s : states) {
                if (s.toLowerCase().startsWith(target.toLowerCase())) {
                    matches.add(s);
                }
            }
            return matches;
        }
    };
}


- Markup And jQuery code -

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
<head>
<meta charset="UTF-8">
<title>Wicket Example</title>
<script type="text/javascript" src="javascript/jquery.js"></script>
<script type="text/javascript" src="javascript/jquery.form.js"></script>
<script type="text/javascript" >
$(document).ready(function(){
/*
* jQuery Ajax form submission and Wicket response
*/
$('#clickme').click(function(){
// get the form input values
// var fName = $('input.jqueryid_firstName').val();
// var lName = $('input.jqueryid_lastName').val();
// ajax callback to the server using jQuery
$('form.jqueryid_form1').ajaxSubmit({cache: false, success: function(html){$('#responses').append(html + "<br/>");}});
});

/*
* jQuery Ajax lookup dropdown list
*/
$('input.jqueryid_state').keyup(function(){

var showStatesJson = function(json){
var listDomElement = ""
$.each(json, function(index, value){
if(listDomElement == ""){
listDomElement = "<select size=\"5\">";
}
listDomElement += "<option>" + value + "</option>"
});
if(listDomElement != ""){
listDomElement += "</select>";
}
$('#statelist').html(listDomElement);
};

// get the form input values
// var target = $(this).val();

var options = {
dataType: "json",
cache: false,
success: showStatesJson
};


if($(this).val() != ""){
$('form.jqueryid_form2').ajaxSubmit(options);
}else{
$('#statelist').html("");
}
});

});
</script>
</head>
<body>
<h1>Marrying Wicket And jQuery Ajax</h1>
<h2>jQuery Ajax form submission and Wicket response</h2>
<form class="jqueryid_form1" wicket:id="form1">
First Name: <input class="jqueryid_firstName" type="text" wicket:id="firstName"/><br/>
Last Name: <input class="jqueryid_lastName" type="text" wicket:id="lastName" /><br/>
</form>
<button id="clickme">Click Me, Please</button>
<div id="responses"/>

<h2>jQuery Ajax lookup dropdown list</h2>
<form class="jqueryid_form2" wicket:id="form2">
US State Lookup: <input class="jqueryid_state" type="text" wicket:id="state"/><br/>
<div id="statelist"/>
</form>
</body>
</html>


Example 1 - Submit a form via Ajax

As the above Wicket code for example 1 shows, what we are doing is adding an AbstractAjaxBehavior to the page and using its callback URL (it points to an event listener which delegates the response to the onRequest method) as the value for the form's action tag which we set by overriding the form's onComponentTag method. The jQuery Form plugin will use the value of the action tag as the URL to call when making the Ajax call.

The really interesting code is inside the AbstractAjaxBehavior's onRequest method which is responsible for handling the request as well as the response back to the client. If you have done similar in Wicket versions prior to v1.5 then you will notice that a lot has changed with v1.5 and is in fact much neater.

In onRequest we obtain a RequestCycle from the page from which we get the actual Request. From the request we are able to easily obtain the params that were posted from the form which are firstName and lastName and with which we create a string and return that string to the client by using a TextRequestHandler.

Basically, all onRequest processing will follow a similar model regardless of its use case - get a request, get the params, do something with the params and send back a response.

The response we are sending back is text and that is why we are using a TextRequestHandler which takes a string parameter representing the text to send back to the client in its constructor.

The code on the client side for example 1 couldn't be easier. Using common jQuery practices we create a call back function to handle the click event on the form. In the handler for the click event we submit the form via Ajax. The Form plugin has numerous options but here you can see that it requires minimal configuration. I am merely requesting that the response not be cached (IE is notorious in this regard) and passing a callback method to handle the success response from the server which just appends it to the DOM in a div whose id is response.

As example 1 above demonstrated, the key to marrying jQuery and Wicket is having jQuery call the correct URL on the server which points to a component based event listener and which delegates the event to AbstractAjaxBehavior's onRequest method. This is the glue that binds the 2 sides of the coin so to speak. As we will now see, it is this same glue that allows us to implement something more intricate than simply echoing back the values of a form submit.

Example 2 - Dynamic Lookup List via Ajax

Web 2.0 has brought numerous improvements to the user's experience and Ajax is to thank for that obviously. One of the nicer features is the ways we can use partial input to provide results to the user as in the case of auto complete elements. Let's expand on example 1 above and show how by marrying jQuery and Wicket this is quite easy to implement.

The use case here is simple: Provide a text box in which the user can type the characters for a US state and provide the user with a list of states that match their input from which they can select one.

Just as we did for example 1 we are again creating an AbstractAjaxBehavior and overriding its onRequest method. Notice how the code is similar to that in example 1. The only difference is what we do with the params we receive and how we use it to create the response back to the client which in this cases requires JSON. The client is wired to submit the form on every keyup event which will post the content of the text field back to the server. In Wicket we get that value and use it to look up all the states that start with that value which are returned to the client as JSON.

The client creates an event handler to handle the keyup event on the text field which it uses to obtain the text field's value and issue an Ajax request to the event listener pointed to by the form's action attribute. It processes the returned JSON object by creating a select element along with options which contain the states that match the user's input and adds the select element to the DOM.

Screen Images Of Examples


Screen Image Of Example 1 and 2 - User hasn't entered anything yet
Screen Image Of Example 1 and 2 - User has entered data in both



Summary

As these examples have demonstrated, marrying jQuery and Wicket to work together isn't rocket science and in fact is quite easy to do. The benefit of this marriage is that you can now easily leverage the strength of each, jQuery and Wicket, to their best advantages; you can now comfortably implement that which is easier to do in either jQuery or Wicket, taking advantage of each of their strengths. The option of having the ability to take advantage of the strengths of each layer in the development stack is an empowering asset.