Building Components in Adobe CQ5 – Part 2: A tutorial on jQuery, AJAX and Sling

In this second part of my series on CQ5 and jQuery, we are going to look at how to implement a jQuery plugin that uses AJAX to retrieve data from CQ5. I picked the jQuery Flexigrid plugin as the target for this tutorial. The sample that we will build is to retrieve a list of users from CQ5 and display that list in the grid component. Although this is use case is meant for an administrative type of task (we would not display a list of registered users to just anyone), I picked this scenario because it will show us how to execute a callback to a JSP in CQ to retrieve JSON formatted data. Since the default Sling servlet in CQ5 can be used to retrieve content in JSON format for pretty much anything, I wanted to chose a fringe use case where we would not use the default Sling servlet and provide our own request handler so we can understand the complete workflow involved.

This tutorial builds on the first part of this series. We are going to create a new component in our /app/samples tree and re-use the clientlibs approach to include the required Flexigrid client files. We’ll then create a component JSP that will render the Flexigrid plugin. Once the grid is displayed, it will make a callback to the server to fetch the data to be displayed. This will be the focus of this post – showing how to configure and handle the call back through Sling and return the result of a CQ5 API call (get all users) in JSON back to the grid to be displayed.

  1. In CRXDE, create a new component and and associated clientlibs node under the /app/samples tree we had created previously. See the following screenshot as a reference.
  2. Download the Flexigrid plugin pack and unpack it on your machine. in the extracted folder, you will have css and js folders.
  3. Populate the clientlibs node with the flexigrid plugin files. Again, here is a screenshot as a reference.

    NOTE: Notice that I set the categories property to ajaxsamples. Of course, best practices such as the comments in part 1 of my series would have us organize our clientlibs in a location other than /apps. In the spirit of keeping this tutorial as simple as possible, I am including the clientlibs node in-line with the component.
  4. Create the base script that will render the grid onto the screen. Edit the myajaxsample.jsp to include the clientlibs as well as display the grid. For additional information on Flexigrid and it’s options, check out the developer’s site. here is the code for the base component.
    <%@include file="/libs/foundation/global.jsp"%>
    <%@page session="false" %>
    
    <!-- Pick up the client libraries  -->
    <cq:includeClientLib categories="ajaxsamples" />
    
    <script type="text/javascript">
    
    /* Grab the JCR path to the content entry that calls this component
     * with Sling, you cannot call a script, you must call the jcr content
     * node that resolves to the representation (script).
     */
    var baseURL = "<%= currentNode.getPath() %>";
    
    jQuery(function ($) {
    
        $('.useraccount-table').flexigrid({
            url: baseURL + '.json', // this will trigger the JSON selector in Sling
            dataType: 'json', // NOTE: Flexigrid executes a POST, not GET to retrieve data
            colModel : [ {
                display : 'User ID', name : 'id', width : 215, sortable : true, align : 'left', hide: false
            }, {
                display : 'First Name', name : 'givenName', width : 100, sortable : true, align : 'left', hide: false
            }, {
                display : 'Last Name', name : 'familyName', width : 100, sortable : true, align : 'left', hide: false
            },{
                display : 'Email', name : 'email', width : 215, sortable : true, align : 'left', hide: false
            }],
            buttons : [
                {name: 'Add', bclass: 'add', onpress : test},
                {name: 'Edit', bclass: 'edit', onpress : test},
                {name: 'Delete', bclass: 'delete', onpress : test},
                {separator: true}
                ],
           searchitems : [
                {display: 'User ID', name : 'user_id',isdefault: true},
                {display: 'First Name', name : 'givenName'},
                {display: 'Last Name', name : 'familyName'}
                ],
            sortname: "id",
            sortorder: "asc",
            usepager: true,
            title: "Cool Grid from CQ5",
            useRp: true,
            rp: 15,
            showTableToggleBtn: false,
            singleSelect: true,
            width: 700,
            height: 200
        });
    });
    
    function test() {
    	alert("Not implemented yet.");
    }
    
    </script>
    
    <!-- Rendered by Flexigrid jQuery plugin -->
    <table class="useraccount-table"></table>
    

    NOTE: We need to capture the current node’s path on line 13 when rendering the component due to CQ5′s RESTful architecture. The only way to execute the JSP that will render the JSON response is to have the component call itself with the .json selector. For more information on how Sling works, read this.

  5. Create a new file that will be our script to return the json formatted data back to our component. This file must have the following filename: myajaxsample.json.POST.jsp. From this filename, you can see that the response format is JSON and this file handles the POST http request to the component (refer to the baseURL variable on line 13 above).
    <%@page session="false" %>
    <%@include file="/libs/foundation/global.jsp"%>
    <%@ page import="org.apache.sling.jcr.api.SlingRepository" %>
    <%@ page import="com.day.cq.security.UserManager" %>
    <%@ page import="com.day.cq.security.UserManagerFactory" %>
    <%@ page import="com.day.cq.security.User" %>
    <%@ page import="com.day.cq.security.Authorizable" %>
    <%@ page import="com.day.cq.security.profile.Profile" %>
    <%@ page import="java.util.Iterator" %>
    <%@ page import="java.util.List" %>
    <%@ page import="java.util.ArrayList" %>
    <%@ page import="com.day.cq.commons.TidyJSONWriter" %>
    
    <%
    
    //Local variables
    final SlingRepository repos = sling.getService(SlingRepository.class);
    final UserManagerFactory umFactory = sling.getService(UserManagerFactory.class);
    
    Session session = null;
    Iterator<User> userIterator = null;
    Iterator<Authorizable> authorizableIterator = null;
    try
    {
    	// Ensure that the currently logged on user has admin privileges.
    	session = repos.loginAdministrative(null);
    
    	final UserManager um = umFactory.createUserManager(session);
    	final TidyJSONWriter writer = new TidyJSONWriter(response.getWriter());
    
    	userIterator = um.getUsers();
    	List<User> users = new ArrayList<User>();
    	User tmpUser;
    
    	// copy iterator into a List for additional manipulations.
    	while(userIterator.hasNext())
    	{
    		tmpUser = userIterator.next();
    		users.add(tmpUser);
    
    	}
    
    	//Begin writing JSON response
    	writer.setTidy("true".equals(request.getParameter("tidy")));
    	writer.object();
    	writer.key("page").value(1);
    	writer.key("total").value(users.size());
    	writer.key("rows").array();
    
    	for(int i=0; i < users.size(); i++)
    	{
    
    	    User aUser = users.get(i);
    	    Profile aProfile = aUser.getProfile();
    
    	    writer.object();
    	    writer.key("id").value(aUser.getID());
    	    writer.key("cell").array();
    	    writer.value(aUser.getID());
    	    writer.value(aProfile.getGivenName());
    	    writer.value(aProfile.getFamilyName());
    	    writer.value(aProfile.getPrimaryMail());
    	    writer.endArray();
    	    writer.endObject();
    	}
    
    	    writer.endArray();
    	    writer.endObject();
    	    session.logout();
    }
    catch (Exception e)
    {
    	System.out.println("myajaxsample Exception Occured: " + e.getMessage());
    }
    finally
    {
    	 session.logout();
    	 session = null;
    }
    %>
    

    NOTE: I am using the TidyJSONWriter class in CQ5 to generate the JSON response. The Flexigrid component expects the following JSON message format:

    total: (no of rec)
    page : (page no)
    rows : [{cell: [ (col1 value) , (col2 value) ,.. ] },
    {cell: [ (col1 value) , (col2 value) ,.. ] }]
    
  6. Now we have all of the pieces in place to use our new component. Here is a screenshot of my finished project as a reference. Don’t forget that you will need a cq:Dialog node for your component to show up in Sidekick – even if you don’t need one. I just copied the dialog node tree I created in part 1 of this series.

You can now create a new page in CQ and enable this component using the design view and add it to the parsys. Place the new component on your new page. You may find that the component does not display until you refresh the page, This is because the clientlibs have not been included in the page yet. Once you refresh the page, you should see something like this:

For convenience, here is the component package.

,,,

Comments Closed