• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Using Ajax in ATG applications
 

Using Ajax in ATG applications

on

  • 777 views

 

Statistics

Views

Total Views
777
Views on SlideShare
777
Embed Views
0

Actions

Likes
0
Downloads
50
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Using Ajax in ATG applications Using Ajax in ATG applications Document Transcript

    • ATG Technical White Paper
    • ATG Technical White Paper 1 Introduction Any Ajax code that wishes to interact with an ATG application will, at some point, need to interact with ATG form handlers. Posting to ATG form handlers can be problematical because the form handler mechanism has some security infrastructure that prevents posts to forms before they have been initialized. Form initialization happens by rendering the form on a JSP page. When calling form handlers from Ajax it is often desirable for the form handlers to return data in the form of JSON, rather than returning entire HTML pages or even page fragments. This document will illustrate a mechanism to enable the posting of data to forms using Ajax; it will illustrate how to respond using JSON and it will also show how to gracefully degrade the behavior to the standard HTML-only mechanisms if JavaScript is not available. ATG has standardized on Dojo as the Ajax framework of choice for rich application interfaces using JavaScript. This paper will use Dojo as it provides tools that make the creation of the XMLHttpRequests to the server, and the processing of JSON responses easier. Starting out Let’s create a new module called AjaxExample. In that module create a J2EE application called ajaxexample with a web-app called ajaxexample.war. You can do this in Eclipse by selecting the File > New > Project… menu item. Then select Java > ATG Wizards > New ATG Module. In the Module Configuration panel you should add the “WebUI” module as an Additional Required Module. This module contains the Dojo files. This example has two additional directories that were set up in the Java Settings step. Firstly, the source directory is changed to “src/” by choosing “Add Folder”, entering “src” in the folder name and selecting “Replace existing project source folder entry to solve nesting”. Additionally, the “Default output folder” is set to “AjaxExample/lib”. Creating the Form Handler Now you have the basic folder structure set up, let’s create a form handler. In Eclipse, choose File > New > Class. Set the Package to atg.ajaxexample (or something appropriate to you). Set the Name to AjaxExampleFormHandler. Set the Superclass to atg.droplet.GenericFormHandler and click “Finish”. Now we need to add some attributes for the form: Using Ajax in ATG applications
    • ATG Technical White Paper 2 package atg.ajaxexample; import atg.droplet.GenericFormHandler; public class AjaxExampleFormHandler extends GenericFormHandler { public static final String ACCEPT_HEADER = "Accept"; protected String id; protected String title; protected String description; protected String successURL; protected String errorURL; protected String ajaxSuccessURL; protected String ajaxErrorURL; } Now we can use Eclipse’s source generation tool to create the setters and getters. Highlight the fields in the code and right click; select Source > Generate Getters and Setters. Select everything except the ACCEPT_HEADER and click OK. You class should now look like this: Using Ajax in ATG applications
    • ATG Technical White Paper 3 package atg.ajaxexample; import atg.droplet.GenericFormHandler; public class AjaxExampleFormHandler extends GenericFormHandler { public static final String ACCEPT_HEADER = "Accept"; protected String id; protected String title; protected String description; protected String successURL; protected String errorURL; protected String ajaxSuccessURL; protected String ajaxErrorURL; public String getAjaxErrorURL() { return ajaxErrorURL; } public void setAjaxErrorURL(String ajaxErrorURL) { this.ajaxErrorURL = ajaxErrorURL; } public String getAjaxSuccessURL() { return ajaxSuccessURL; } public void setAjaxSuccessURL(String ajaxSuccessURL) { this.ajaxSuccessURL = ajaxSuccessURL; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getErrorURL() { return errorURL; } public void setErrorURL(String errorURL) { this.errorURL = errorURL; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSuccessURL() { return successURL; } public void setSuccessURL(String successURL) { this.successURL = successURL; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } To finish off our form handler we need a handleXXX() method. We’ll call it handleCreate(): Using Ajax in ATG applications
    • ATG Technical White Paper 4 /* Handle methods */ public void handleCreate(DynamoHttpServletRequest request, DynamoHttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { String success = getSuccessURL(); String error = getErrorURL(); // do some work this.checkFormRedirect(success, error, request, response); } Now that you have a completed form handler class you need to create a Nucleus configuration for it. To do this, create a properties file /ajaxexample/AjaxExampleFormHandler.properties in the config/ directory. You can also use the Nucleus perspective in Eclipse. Create the component as request scoped, which is how most form handlers in ATG are defined. Now we have the form handler component, we need to create a JSP page to call that form handler (this page is called formexample.jsp and is in the web-app root folder): <%@ taglib uri="http://www.atg.com/taglibs/daf/dspjspTaglib1_0" prefix="dsp" %> <dsp:page> <dsp:importbean bean="/ajaxexample/AjaxExampleFormHandler"/> <dsp:importbean bean="/atg/dynamo/droplet/ErrorMessageForEach"/> <html> <head> <title>My Ajax Example Form</title> </head> <body> <dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param bean="AjaxExampleFormHandler.formError" name="value"/> <dsp:oparam name="true"> <dsp:droplet name="ErrorMessageForEach"> <dsp:param bean="AjaxExampleFormHandler.formExceptions" name="exceptions"/> <dsp:oparam name="output"> <span style="color: #ff0000;"><dsp:valueof param="message"/></span> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> <dsp:form action="formexample.jsp"> Id: <dsp:input type="text" bean="AjaxExampleFormHandler.id"/> <br/> Title: <dsp:input type="text" bean="AjaxExampleFormHandler.title"/> <br/> Description: <dsp:input type="text" bean="AjaxExampleFormHandler.description"/> <br/> <dsp:input type="hidden" bean="AjaxExampleFormHandler.successURL" value="results.jsp"/> <dsp:input type="hidden" bean="AjaxExampleFormHandler.errorURL" value="formexample.jsp"/> <dsp:input type="submit" bean="AjaxExampleFormHandler.create" value="Create"/> </dsp:form> </body> </html> Using Ajax in ATG applications
    • ATG Technical White Paper 5 As you can see this is a simple ATG form as you’d expect to see in any page. The successURL points to a page called results.jsp (defined below) and the errorURL points back to this page. <%@ taglib uri="http://www.atg.com/taglibs/daf/dspjspTaglib1_0" prefix="dsp" %> <dsp:page> <dsp:importbean bean="/ajaxexample/AjaxExampleFormHandler"/> <html> <body> Results (HTML):<br/> <dl> <dt>Id:</dt> <dd><dsp:valueof bean="AjaxExampleFormHandler.id"/></dd> <dt>Title</dt> <dd><dsp:valueof bean="AjaxExampleFormHandler.title"/></dd> <dt>Description</dt> <dd><dsp:valueof bean="AjaxExampleFormHandler.description"/></dd> <dt>Created at:</dt> <dd><dsp:valueof bean="/atg/dynamo/service/CurrentDate.secondAsDate"/></dd> </dl> <br/> <dsp:a href="formexample.jsp">Back to Form Example</dsp:a> </body> </html> </dsp:page> You should now be able to test your application. Use runAssembler to create your application EAR, remembering to include the module AjaxExample on the command line. Deploy to your application server and go to http://[server]:[port]/ajaxexample/formexample.jsp. You should be able to enter values in the fields and see the data mirrored in the results.jsp page. Adding some Ajax Now that our application is working correctly we can move on to adding the Ajax functionality. Firstly, we need the Dojo libraries. These are located in the WebUI module that we included earlier. To add the following to the <head> section of formexample.jsp: <script type="text/javascript"> var djConfig={ baseScriptUri: "/dojo-04/", disableFlashStorage: true, parseWidgets: false, isDebug: false }; </script> <script type="text/javascript" src="/dojo-04/dojo.js"></script> This sets up the environment for Dojo and includes the Dojo JavaScript library. Now we also want to make sure that our code gets initialized when the page is loaded. Add the following in the <HEAD> section of formexample.jsp, immediately after the code above: Using Ajax in ATG applications
    • ATG Technical White Paper 6 <script type="text/javascript"> dojo.require("dojo.widget.*"); dojo.addOnLoad(function(){ atg_ajaxexample_formsubmitter.initialize(); }); </script> <script type="text/javascript" src="script/formsubmitter.js"></script> So now our JavaScript environment is set up, we need to create the file “script/formsubmitter.js”. This file will contain all of our implementation code. So, what are we going to need to do? Firstly, we need to steal the click event from any buttons that are submitting forms. To do this we are going to need to be able to identify those buttons. So let’s assume that they will all use a CSS class in the HTML, let’s say “formsubmitter”. In the JSP we can do this: <dsp:input type=”submit” value=”Create” bean=”/ajaxexample/AjaxExampleFormHandler.create” iclass=”formsubmitter”/> The iclass=”formsubmitter” parameter will get turned into a class=”formsubmitter” by the tag. So now we can find our buttons (or any component that uses the class, such as an <A> tag) by using the Dojo function dojo.html.getElementsByClass(). Now we can iterate over the returned elements and bind their click functions to submit the form they are associated with as an XMLHttpRequest. Here’s the code: dojo.require("dojo.html.*"); dojo.require("dojo.event.*"); dojo.require("dojo.io.*"); dojo.require("dojo.widget.*"); atg_ajaxexample_formsubmitter = { hijackClassName: "formsubmitter", initialize: function() { this.hijackAllFormSubmitNodes(); }, hijackAllFormSubmitNodes: function() { dojo.debug("Connecting to all elements with class " +atg_ajaxexample_formsubmitter.hijackClassName); var elements = dojo.html.getElementsByClass(atg_ajaxexample_formsubmitter.hijackClassName); for (var i=0; i<elements.length; i++){ this.hijackNode(elements[i]); } }, So far we’ve included any necessary Dojo components and created the initialize function. Next we need to actually write the code to capture the onclick calls and redirect them to use XMLHttpRequest. This means using the dojo.io.bind() function. This creates an XMLHttpRequest under the covers. However, it also has some built in machinery to collect up all the values from form fields and put them into the POST parameters. This is handy and saves us a lot of work. [ Code continues… ] Using Ajax in ATG applications
    • ATG Technical White Paper 7 hijackNode: function(node) { dojo.debug("Hijacking node"); dojo.debug(node); if (node.isHijacked){ dojo.debug("Node is already hijacked - ignoring"); return; } node.isHijacked=true; // Create object with common params for io.bind call var _this = this; var bindParams={ headers: { "Accept" : "application/json" }, mimetype: "application/json", load: function(type, data, evt) { _this.handleResponse(data,evt,node); }, error: function(type, evt) { _this.handleError(type, evt); }, timeout: function(type, evt) { _this.handleError(type, evt); } }; if (node.nodeName=="INPUT"){ dojo.event.connect(node, "onclick", function(evt){ evt.preventDefault(); // Get parent form node for this input node var formNode=dojo.html.getParentByType(node,"FORM"); // Create content object with the name/value pair of the submit button // that's been clicked dojo.io.bind doesn't send the value of submit // buttons when serializing the form as it doesn't know which one has // been clicked. Server side FormHandlers need this data to invoke the // correct formHandler method. var content={}; content[node.name]=node.value; dojo.debug("Form clicked - submitting form"); dojo.debug(formNode); // Add the form node and the submit button name/value to the io.bind params dojo.lang.mixin(bindParams,{ formNode: formNode, content: content }); dojo.io.bind(bindParams); }); } else{ dojo.debug("Node is not a form submit - ignoring"); } }, handleResponse: function(data,evt,node){ this.enableNode(node); alert(data.title+" created at "+data.time); }, handleError: function(type,evt){ alert("Error: "+evt.message); }, So this function does all the work for us. There are a few things to notice in this function. Most importantly is the setting of the bindParams variable. Notice the first two attributes are “headers” and “mimetype”. These have both been set to “application/json”. In the form handler we are Using Ajax in ATG applications
    • ATG Technical White Paper 8 going to use this to distinguish between requests submitted using the standard form post mechanism of the browser and the form posted by our JavaScript. In the case of a standard post we want to redirect to the successURL, but in the case of JavaScript we want to return JSON so we’ll need to redirect to a different location: the ajaxSuccessURL, which will be JSP that returns JSON data instead of HTML. Notice the handleResponse() method pops up an alert() with the title and time from the returned JSON data. The code above only deals with <INPUT> tags, see the attached code sample for handling of <A> tags. Now we have all the mechanisms on the client side to post the form to the server using XMLHttpRequest instead of the normal browser post. Next, we need to handle requests from the JavaScript code a little differently on the server side. We want all the same logic for handling the setXXX() methods but in the handleCreate() method we need to switch successURLs if the request is from the JavaScript. This is where the Accept header comes in. If the browser posts the request then the Accept header will be “text/html”. Our JavaScript code explicitly set this to “application/json”. So all we need to do in the handleCreate() method is check that header, if the value is JSON then use the ajaxSuccessURL/ajaxErrorURL and if not use the regular value: public void handleCreate(DynamoHttpServletRequest request, DynamoHttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { String success = getSuccessURL(); String error = getErrorURL(); if (isAjaxRequest(request)){ success = getAjaxSuccessURL(); error = getAjaxErrorURL(); setUseForwards(true); } // do some work this.checkFormRedirect(success, error, request, response); } private boolean isAjaxRequest(DynamoHttpServletRequest request) { String acceptHeader = request.getHeader(ACCEPT_HEADER); if (acceptHeader == null){ return false; } boolean isAjax = (acceptHeader.indexOf("text/json")!=-1) || (acceptHeader.indexOf("application/json")!=-1); return isAjax; } The function isAjaxRequest() just checks to see if the Accept header is either “text/json” or “application/json”. Some Ajax frameworks will use “text/json” so we can use this code with other frameworks. Now we need to modify the JSP page to pass the extra URLs and set the iclass on the submit button: <dsp:form action="formexample.jsp"> Id: <dsp:input type="text" bean="AjaxExampleFormHandler.id"/> <br/> Title: <dsp:input type="text" bean="AjaxExampleFormHandler.title"/> <br/> Using Ajax in ATG applications
    • ATG Technical White Paper 9 Description: <dsp:input type="text" bean="AjaxExampleFormHandler.description"/> <br/> <dsp:input type="hidden" bean="AjaxExampleFormHandler.successURL" value="results.jsp"/> <dsp:input type="hidden" bean="AjaxExampleFormHandler.errorURL" value="formexample.jsp"/> <dsp:input type="hidden" bean="AjaxExampleFormHandler.ajaxSuccessURL" value="json/results.jsp"/> <dsp:input type="hidden" bean="AjaxExampleFormHandler.ajaxErrorURL" value="json/error.jsp"/> <dsp:input type="submit" bean="AjaxExampleFormHandler.create" value="Create" iclass="formsubmitter"/> </dsp:form> Finally, we need to provide the JSP pages for the JSON response and error pages. In these pages we’re going to create the JSON using the JSON tag library from James Wiltshire. It is an ATG open source project available on SourceForge. Note, the JSON taglib requires JSP 2.0, so you’ll need to be running an application server that supports it (DAS 6.3.0 does not). If you are not using a JSP 2.0 supported applications server you can generate the JSON by hand – it isn’t hard. json/results.jsp <%@ taglib uri="http://www.atg.com/taglibs/daf/dspjspTaglib1_0" prefix="dsp" %> <%@ taglib uri="http://www.atg.com/taglibs/json" prefix="json" %> <%@ page contentType="application/json" %> <dsp:page> <dsp:importbean bean="/ajaxexample/AjaxExampleFormHandler"/> <json:object> <json:property name="error" value="${false}"/> <json:property name="id"> <dsp:valueof bean="AjaxExampleFormHandler.id"/> </json:property> <json:property name="title"> <dsp:valueof bean="AjaxExampleFormHandler.title"/> </json:property> <json:property name="description"> <dsp:valueof bean="AjaxExampleFormHandler.description"/> </json:property> <json:property name="time"> <dsp:valueof bean="/atg/dynamo/service/CurrentDate.secondAsDate"/> </json:property> </json:object> </dsp:page> json/error.jsp <%@ taglib uri="http://www.atg.com/taglibs/daf/dspjspTaglib1_0" prefix="dsp" %> <%@ taglib uri="http://www.atg.com/taglibs/json" prefix="json" %> <%@ page contentType="application/json" %> <dsp:page> <dsp:importbean bean="/ajaxexample/AjaxExampleFormHandler"/> <dsp:importbean bean="/atg/dynamo/droplet/ErrorMessageForEach"/> <json:object> <json:property name="error" value="${true}"/> <json:array name="errors"> <dsp:droplet name="ErrorMessageForEach"> <dsp:param bean="AjaxExampleFormHandler.formExceptions" name="exceptions"/> <dsp:oparam name="output"> Using Ajax in ATG applications
    • ATG Technical White Paper 10 <json:property> <dsp:valueof param="message"/> </json:property> </dsp:oparam> </dsp:droplet> </json:array> </json:object> </dsp:page> What happens at runtime? So to test the application I’ve enable the WebDeveloper toolbar extension in Firefox. On the Disable menu you can turn JavaScript on and off. We’ll start with it off. I’m also using the Firebug extension to track the requests. With JavaScript turned off… Looks great. Enter some values in the fields and click “Create” and we get: Let’s look at the requests to the server. Notice that only formexample.jsp is requested because JavaScript is turned off. Using Ajax in ATG applications
    • ATG Technical White Paper 11 When the submit is made by clicking the button you can see the following request/response pair generated by the browser. Notice the Content-Type of the response and the Accept header of the request. Also notice the URL of the response “results.jsp”. With JavaScript on… Hmm! Looks the same. Enter some values in the fields and click “Create” and we get this: So the JavaScript took over and sent the post as an XMLHttpRequest. Here’s the relevant request and response headers: Using Ajax in ATG applications
    • ATG Technical White Paper 12 Notice this time the Content-Type is application/json and the Accept header is the same. Also notice the size of the response in a third that of the HTML version. Even though the same data is coming back we are using the succinct mark-up of JSON. The total response is: {error:false,id:"dfgsdfg",title:"sdfgsdfg",description:"sdfhsdfghs",tim e:"Wed Jun 27 20:13:47 PDT 2007"} Even this trivial example has its advantages. Wrap-up So there you have it. An Ajax-based form handling scheme with a nice fallback position if JavaScript is not available. Bibliography JSON taglib for JSP http://json-taglib.sourceforge.net Dojo Toolkit http://dojotoolkit.org Using Ajax in ATG applications