53
MONTREAL 1/3 JULY 2011 Ajax.framework: Under the Hood Chuck Hill Global Village Consulting, Inc. WOWODC 2011

Ajax Under The Hood

Embed Size (px)

DESCRIPTION

Have you ever wondered how Ajax requests fit in with WebObject's Request-Response loop? Have you ever wanted to make a new Ajax component or debug a problem in an existing one? Do you want to use a different JavaScript library with WebObjects? This session will look at what was done to make Ajax work in a component action world, looking at changes to the Request-Response loop and the page cache. You will learn how to use AjaxComponent, AjaxDynamicElement, and the other classes that are used to create WebObjects Ajax components.

Citation preview

Page 1: Ajax Under The Hood

MONTREAL 1/3 JULY 2011

Ajax.framework: Under the HoodChuck HillGlobal Village Consulting, Inc.WOWODC 2011

Page 2: Ajax Under The Hood

Session Overview

• My Goals

• Outline:

• Ajax vs Classical WebObjects

• How Things Work vs Just Works

• Building Blocks

• Case Study

• No JSON

Page 3: Ajax Under The Hood

• Jean-Francois Veillette

• Anjo Krank

• Mike Schrag

• the rest of us

• you

A Word on our Sponsors

Page 4: Ajax Under The Hood

• Not a toy scripting language

• NOT Java, “Lisp in C’s clothing”

• functional programming

• loose “duck” typing and prototypal inheritance

• dynamic objects

• Douglas Crockford and Yahoo

A Word on JavaScript

Page 5: Ajax Under The Hood

Classical Request/Response Loop

Page 6: Ajax Under The Hood

Ajax R-R in a Nutshell

Page 7: Ajax Under The Hood

New R-R Loop Phase

• handleRequest

• replaces action method

• returns response for Ajax request

• called by invokeAction

• ... at the right time!

• takeValues→invokeAction→handleRequest→appendToResponse?’

• returns AjaxResponse (important!)

Page 8: Ajax Under The Hood

Ajax Action R-R Loop

• “The Basic”

• takeValues

• invokeAction

• handleRequest

• whatever

• can return JS, HTML, or nothing

• client-side result varies

Page 9: Ajax Under The Hood

Ajax Update R-R Loop• “The Meat Lover’s”

• ...?_u=MyContainerID&1239887663

• invokeAction

• handleRequest

• AjaxResponse.generateResponse

• invokeAction (yes! again!)

• handleRequest (on AUC)

• appendChildrenToResponse (AUC)

Page 10: Ajax Under The Hood

Ajax Submit, Action, & Update• “All Dressed”

• takeValuesFromRequest

• invokeAction

• handleRequest

• AjaxResponse.generateResponse

• invokeAction (yes! again!)

• handleRequest (on AUC)

• action method (result discarded)

• appendChildrenToResponse (AUC)

Page 11: Ajax Under The Hood

Ajax Replace R-R Loop• “Poutine Pizza”

• ...?_r=true&1239887663

• takeValuesFromRequest

• invokeAction

• handleRequest

• action method (component) returned as replacement)

• intuitive, but may be dangerous

Page 12: Ajax Under The Hood

Special Ajax Classes

• AjaxRequestHandler: don’t store page

• ERXAjaxApplication: no takeVlaues for ?_u=xxx&2309482093

• ERXAjaxSession: Ajax Page Caching

• ERXAjaxContext: partial form submits

• AjaxResponse: generateResponse→invokeAction for AUC

Page 13: Ajax Under The Hood

Classical Page Caching

Page 14: Ajax Under The Hood

Ajax Page Caching

Page 15: Ajax Under The Hood

Support Classes

• AjaxComponent

• AjaxDynamicElement

• AjaxUtils

• AjaxOptions, AjaxOption, AjaxConstantOption, AjaxValue

Page 16: Ajax Under The Hood

AjaxComponent

• WOComponent replacement

• Stateful

• Mostly familiar usage

• handleRequest

• addRequiredWebResources

Page 17: Ajax Under The Hood

AjaxDynamicElement

• WODynamicGroup replacement

• Stateless

• Harder to use than WOComponent

• Binding and template access

• handleRequest

• addRequiredWebResources

Page 18: Ajax Under The Hood

AjaxUtils• createResponse

• javascriptResponse

• Add JS and CSS resources (via ERXResponseRewriter)

• shouldHandleRequest

• binding convenience with JSON parsing

• response generation

• updateDomElement

• redirecting

• etc. seek and ye shall find

Page 19: Ajax Under The Hood

JavaScript/JSON Value Encoding

new Ajax.Request('/some_url', { method: 'get', parameters: {company: 'example', limit: 12} });

Page 20: Ajax Under The Hood

AjaxValue

• converts to JavaScript/JSON format

• type-aware value

• type guessing ability

• javascriptValue

• [a, b, ...], {a:b; ...}, escaping in strings etc.

Page 21: Ajax Under The Hood

AjaxOption.Type

• simple enumeration

• AjaxValue data types

• update if you extend AjaxValue

Page 22: Ajax Under The Hood

AjaxOption• named for Prototype parameter “options”

• bridge from bindings to JavaScript

• component or dynamic element

• Parameters

• JavaScript Name

• Type

• Binding Name

• Default Value

Page 23: Ajax Under The Hood

AjaxConstantOption

• Used for computed values

public AjaxConstantOption( String javaScriptName, Object constantValue, Type type)

Page 24: Ajax Under The Hood

AjaxOptions

• with an S

• collection of key-value pairs

• used as component* or class

• *component usage likely buggy

• works with StringBuffer and WOResponse

• works best with

AjaxOption.createAjaxOptionsDictionary

Page 25: Ajax Under The Hood

AjaxOptions Usage

public NSDictionary createAjaxOptions(WOComponent component) { NSMutableArray<AjaxOption> list = new NSMutableArray<AjaxOption>(); list.addObject(new AjaxOption("frequency", AjaxOption.NUMBER)); list.addObject(new AjaxOption("onLoading", AjaxOption.SCRIPT)); list.addObject(new AjaxOption("evalScripts", Boolean.TRUE, AjaxOption.BOOLEAN)); ... NSMutableDictionary<String, String> options = AjaxOption.createAjaxOptionsDictionary(list, component, associations()); return options; }

response.appendContentString("AUC.registerPeriodic('" + id + "'," + canStop + "," + stopped + ",");AjaxOptions.appendToResponse(options, response, context);response.appendContentString(");");

Page 26: Ajax Under The Hood

Part II

• Being the Second Part

Page 27: Ajax Under The Hood

Twenty Steps to WO-Ajax

• Who loves ya baby?

• From discovery to commit

• slowly

• in 20 steps

Page 28: Ajax Under The Hood

Step 1: Review and Plan

• Find something

• See if it is worth using

• Plan how you will use it

Page 29: Ajax Under The Hood

Step 2: Check that License

• Licenses like the GPL are best avoided

• For Wonder: BSD, MIT or Apache 2 are preferred

• For your own projects consider the terms

Page 30: Ajax Under The Hood

Step 3: Download Source Files

• Determine JavaScript files needed

• Often component specific and supporting

Page 31: Ajax Under The Hood

Step 4: Download Resources

• Get example HTML from demo source

• CSS files

• Image resources

• License file

• Other resources?

Page 32: Ajax Under The Hood

Step 5: Start Small

• AjaxComponent or AjaxDynamicElement?

• Ajax, ERCoolComponents, or private?

• Create class and/or .wo

• Add JS, CSS, images, etc to WebServerResources

Page 33: Ajax Under The Hood

Step 6: addRequiredWebResources

protected void addRequiredWebResources(WOResponse response, WOContext context) {

addScriptResourceInHead(context, response, "Ajax", "controls.js"); addScriptResourceInHead(context, response, "Ajax", "prototype.js");

addScriptResourceInHead(context, response, "ERCoolComponents", "Rating/livepipe.js"); addScriptResourceInHead(context, response, "ERCoolComponents", "Rating/rating.js"); addStylesheetResourceInHead(context, response, "ERCoolComponents", "Rating/rating.css");

}

Page 34: Ajax Under The Hood

Step 7: Static appendToResponse

public void appendToResponse(WOResponse response, WOContext context) {

super.appendToResponse(response, context);

response.appendContentString("<div id=\"rating_one\" class=\"rating_container\">"); response.appendContentString("</div>");

response.appendContentString("<script type=\"text/javascript\">"); response.appendContentString("var rating_one = new Control.Rating('rating_one');"); response.appendContentString("</script>");

}

Page 35: Ajax Under The Hood

Step 8: handleRequest

public WOActionResults handleRequest(WORequest request, WOContext context) {

System.out.println("CCRating " + request.headers());

return null;}

Page 36: Ajax Under The Hood

Step 9: Initial Testing

• Smoke Test Time!

• Is control rendered correctly?

• Does CSS load?

• Are images visible?

• Does it “work” client side?

Page 37: Ajax Under The Hood

Step 10: Check Client Side Errors

• Use FireBug or Safari Web Inspector

• Look for errors in JavaScript console

• Look for resources that fail to load

Page 38: Ajax Under The Hood

Step 11: Update Paths in CSS

Resource paths in CSS are likely wrong:

background-image:url(/stylesheets/rating.gif);

These are resolved (by Wonder) relative to the CSS file’s location:

background-image:url(rating.gif);

Page 39: Ajax Under The Hood

Step 12a: First Dynamic Steps

public void appendToResponse(WOResponse response, WOContext context) {

super.appendToResponse(response, context);

String id = (String) valueForBinding( "id", ERXWOContext.safeIdentifierName(context, false), context.component());

response.appendContentString("<div ");

appendTagAttributeToResponse(response, "id", id); appendTagAttributeToResponse(response, "class", "rating_container");

response.appendContentString("></div>");

Page 40: Ajax Under The Hood

Step 12b: First Dynamic Steps

appendToResponse continued...

response.appendContentString("<script type=\"text/javascript\">");response.appendContentString("var ");response.appendContentString(id);response.appendContentString(" = new Control.Rating('");response.appendContentString(id);response.appendContentString("'); </script>");}

Page 41: Ajax Under The Hood

Step 13a: Rating Options<script type="text/javascript">

var e_1_0_0_1_3_7 = new Control.Rating('e_1_0_0_1_3_7', {value: 5, min: 2, max: 8} );

</script>

Page 42: Ajax Under The Hood

Step 13b: Rating Options response.appendContentString("'); </script>");

response.appendContentString("', "); AjaxOptions.appendToResponse(createOptions(context), response, context); response.appendContentString("); </script>");}

protected NSMutableDictionary createOptions(WOContext context) { NSMutableArray ajaxOptionsArray = new NSMutableArray(); ajaxOptionsArray.addObject(new AjaxOption("min", AjaxOption.NUMBER)); ajaxOptionsArray.addObject(new AjaxOption("max", AjaxOption.NUMBER)); ajaxOptionsArray.addObject(new AjaxOption("value", AjaxOption.NUMBER)); return AjaxOption.createAjaxOptionsDictionary( ajaxOptionsArray, context.component(), associations());}

Page 43: Ajax Under The Hood

Step 14: Rest of BindingsajaxOptionsArray.addObject(new AjaxOption("capture", AjaxOption.BOOLEAN));

ajaxOptionsArray.addObject(new AjaxOption("classNames", AjaxOption.DICTIONARY));

ajaxOptionsArray.addObject( new AjaxOption("input", "inputId", null, AjaxOption.STRING));

ajaxOptionsArray.addObject(new AjaxOption("multiple", AjaxOption.BOOLEAN));ajaxOptionsArray.addObject(new AjaxOption("rated", AjaxOption.BOOLEAN));ajaxOptionsArray.addObject(new AjaxOption("reverse", AjaxOption.BOOLEAN));ajaxOptionsArray.addObject(new AjaxOption("updateOptions", AjaxOption.DICTIONARY));

ajaxOptionsArray.addObject( new AjaxOption("updateParameterName", "formValueName", null, AjaxOption.STRING));

Page 44: Ajax Under The Hood

Step 15a: Server Interaction

ajaxOptionsArray.addObject( new AjaxOption("updateParameterName", "formValueName", null, AjaxOption.STRING));

ajaxOptionsArray.addObject( new AjaxConstantOption("updateParameterName", "formValueName", formValueName(context), AjaxOption.STRING));

ajaxOptionsArray.addObject(new AjaxConstantOption("updateUrl", AjaxUtils.ajaxComponentActionUrl(context), AjaxOption.STRING));

protected String formValueName(WOContext context) { return (String)valueForBinding( "formValueName", id(context) + "_value", context.component());

Page 45: Ajax Under The Hood

Step 15b: Server Interaction

public WOActionResults handleRequest(WORequest request, WOContext context) { System.out.println("CCRating " + request.headers()); System.out.println("CCRating " + request.uri()); System.out.println("CCRating " + request.formValues());

Object ratingValue = request.formValueForKey(formValueName(context));

if (ratingValue instanceof String) { ratingValue = Integer.valueOf((String)ratingValue); }

setValueForBinding(ratingValue, "value", context.component());

return null;}

Page 46: Ajax Under The Hood

Step 16: WO Integration: action Binding

public WOActionResults handleRequest(WORequest request, WOContext context) { Object ratingValue = request.formValueForKey(formValueName(context)); if (ratingValue instanceof String) { ratingValue = Integer.valueOf((String)ratingValue); } setValueForBinding(ratingValue, "value", context.component());

// Nothing gets returned to the client from the CCRating action so we // discard any result from firing the action binding if (hasBinding("action")) { valueForBinding("action", context.component()); }

return null;}

Page 47: Ajax Under The Hood

Step 17a: WO Integration: Form Inputprotected boolean actAsInput(WOContext context) {

return booleanValueForBinding("actAsInput", true, context.component());}

protected NSMutableDictionary createOptions(WOContext context) {... if ( ! actAsInput(context)) { ajaxOptionsArray.addObject(new AjaxConstantOption("updateUrl", AjaxUtils.ajaxComponentActionUrl(context), AjaxOption.STRING)); } else { ajaxOptionsArray.addObject(new AjaxConstantOption("input", id(context) + "_input", AjaxOption.STRING)); }

Page 48: Ajax Under The Hood

Step 17b: WO Integration: Form Input

public void appendToResponse(WOResponse response, WOContext context) {...if (actAsInput(context)) { response.appendContentString("<input ");

appendTagAttributeToResponse(response, "type", "hidden"); appendTagAttributeToResponse(response, "id", id + "_input"); appendTagAttributeToResponse(response, "name", formValueName(context));

appendTagAttributeToResponse(response, "value", valueForBinding("value", context.component()));

response.appendContentString("/>");...}

Page 49: Ajax Under The Hood

Step 17c: WO Integration: Form Input

public void takeValuesFromRequest(WORequest request, WOContext context) { if (actAsInput(context)) { setValueFromFormValue(request, context); } super.takeValuesFromRequest(request, context); }

protected void setValueFromFormValue(WORequest request, WOContext context) { Object ratingValue = request.formValueForKey(formValueName(context)); if (ratingValue instanceof String) { ratingValue = Integer.valueOf((String)ratingValue); } setValueForBinding(ratingValue, "value", context.component()); }

Page 50: Ajax Under The Hood

Step 18: Clean and Document

• Remove debugging code

• General code clean-up and refactoring

• JavaDocs for class

• JavaDocs for methods, @param, @return

Page 51: Ajax Under The Hood

Step 19: @binding JavaDocs * @binding value the value to show in the ratings widget and the value set

when the user selects a different rating

* @binding actAsInput optional, default is <code>true</code>, if false updates the value binding when clicked and optionally calls action method

* @binding action optional, action method to fire when rating changed. Ignored if actAsInput is <code>true</code> or unbound

* @binding min optional, the value sent to the server when the lowest rating is selected, indirectly controls the number of rating points displayed

Page 52: Ajax Under The Hood

Step 20: Create .api File

Page 53: Ajax Under The Hood

Q&A

MONTREAL 1/3 JULY 2011

Ajax.framework: Under the Hood

Chuck HillGlobal Village Consulting