Author: jkuhnert
Date: Sun Jun 24 19:24:19 2007
New Revision: 550318

URL: http://svn.apache.org/viewvc?view=rev&rev=550318
Log:
Added some more documentation.

Added:
    tapestry/tapestry4/trunk/src/site/apt/ajax/basics.apt
    tapestry/tapestry4/trunk/src/site/apt/ajax/json.apt
Modified:
    tapestry/tapestry4/trunk/src/site/apt/ajax/responsebuilder.apt
    tapestry/tapestry4/trunk/src/site/site.xml
    
tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/Home.page

Added: tapestry/tapestry4/trunk/src/site/apt/ajax/basics.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/src/site/apt/ajax/basics.apt?view=auto&rev=550318
==============================================================================
--- tapestry/tapestry4/trunk/src/site/apt/ajax/basics.apt (added)
+++ tapestry/tapestry4/trunk/src/site/apt/ajax/basics.apt Sun Jun 24 19:24:19 
2007
@@ -0,0 +1,239 @@
+ ------
+Ajax/DHTML Guide - Basics
+ ------
+Jesse Kuhnert
+ ------
+24 June 2007
+ ------
+
+Ajax Development Basics
+
+  The overall concept behind the majority of built in AJAX functionality in 
Tapestry is that everything should work exactly the same way in your 
pages/components
+  in AJAX requests as it does during normal page rendering.  There are a few 
corner cases that have no real intuitive XHR equivalent which we will discuss as
+  well.
+
+* Including JavaScript in your HTML Template
+
+  One of the first things you will need to do is make sure you have the 
necessary javascript includes.  Tapestry makes extensive use of the 
{{{http://dojotoolkit.org}Dojo}}
+  javascript toolkit and provides a couple of options for including it for you 
automatically via the {{{../components/general/shell.html}Shell}} or
+  {{{../components/general/scriptincludes.html}ScriptIncludes}} components.  
An example of defining a basic outer layer using the 
{{{../components/general/shell.html}Shell}}
+  component would look like:
+
++-------------------------------------------------------------------------------------------------------------
+<html jwcid="@Shell" title="Basic Ajax Page">
+<body jwcid="@Body">
+
+    <p>Basic javascript inclusion sample.</p>
+
+</body>
+</html>
++-------------------------------------------------------------------------------------------------------------
+
+* Updating Components:  The universal updateComponents parameter
+
+  One of the most frequently used pieces of ajax functionality is the 
<<<updateComponents="foo,bar">>> parameter that is now implemented by all of 
the core Tapestry
+  components - where it makes sense.   Some of the more familiar of these are 
{{{../components/link/directlink.html}DirectLink}}, 
{{{../components/form/linksubmit.html}LinkSubmit}},
+  {{{../components/form/form.html}Form}}, 
{{{../components/form/imagesubmit.html}ImageSubmit}} and 
{{{../components/form/submit.html}Submit}}.  We'll build on our previous
+  example and use the {{{../components/link/directlink.html}DirectLink}} 
component to refresh a current time display.
+
++-------------------------------------------------------------------------------------------------------------
+..
+<p>Basic javascript inclusion sample.</p>
+
+<p>
+    <a jwcid="@DirectLink" listener="listener:setTime" 
updateComponents="time">Refresh time</a>.
+</p>
+
+<div jwcid="[EMAIL PROTECTED]" value="ognl:time" renderTag="true" />
+..
++-------------------------------------------------------------------------------------------------------------
+
+  The corresponding java page class:
+
++-------------------------------------------------------------------------------------------------------------
+..
+public abstract BasicAjax extends BasePage {
+
+    public abstract void setTime(Date time);
+
+    public void setTime()
+    {
+        setTime(new java.util.Date());
+    }
+}
++-------------------------------------------------------------------------------------------------------------
+
+  That's it!  Building and running this small example should be all you need 
to start using the very basic AJAX functionality provided by Tapestry.  
+
+** updateComponents == IComponent.getClientId()
+
+  There are a few subtle things happening here that may not be apparent at 
first glance.  One of the more important changes in Tapestry 4.1.x was the 
addition of
+  the 
{{{../apidocs/org/apache/tapestry/IComponent.html#getClientId()}IComponent.getClientId()}}
 method to all component classes.  When dealing with client side javascript
+  functionality the unique client side element id of the html elements your 
components generate becomes very important.  What this method gives you is the 
unique element id
+  attribute that will be written for any given component depending on the 
context in which you call it.
+
+  If you are in a {{{../components/general/for.html}For}} loop then the 
<<<clientId>>> will automatically be incremented for each loop and always 
represent the unique
+  value of the component.  The same semantics work whether in 
{{{../components/form/form.html}Forms}} / 
{{{../components/general/for.html}For}} or any other nested
+  type of structure.  This has been one of key changes in the API that has 
made the AJAX functionality provided <much> easier to work with.
+
+  In our example the <<<updateComponents>>> parameter given made use of the 
new smart auto binding functionality added to the framework recently, but you 
could write
+  the equivalent parameter using an ognl expression as well:
+
++-------------------------------------------------------------------------------------------------------------
+<a jwcid="@DirectLink" listener="listener:setTime"
+    updateComponents="ognl:components.time.clientId">Refresh time</a>
++-------------------------------------------------------------------------------------------------------------
+
+  Things can get a lot uglier once you start trying to update multiple 
components:
+
++-------------------------------------------------------------------------------------------------------------
+<a jwcid="@DirectLink" listener="listener:setTime"
+    updateComponents="ognl:{components.time.clientId, 
page.components.status.clientId}">Refresh time</a>
++-------------------------------------------------------------------------------------------------------------
+
+  The much easier simple string list <<<updateComponents="compA, compB, 
compC">>> is not only shorter / easier to understand but also more efficient 
and robust
+  in many different circumstances.  For instance, the auto binding 
functionality would be able to detect that you were trying to update a 
component contained within
+  one of your own custom components as well as a component on the page 
containing your component and wire up and find all the correct <<<clientId>>> 
values for you
+  automatically.
+
+  The thing to walk away with here is that in 98% of the cases you run in to 
this style of <<<updateComponents>>> syntax is the way to go:
+
++-------------------------------------------------------------------------------------------------------------
+<a jwcid="@DirectLink" listener="listener:setTime" 
updateComponents="time,status">Refresh time</a>
++-------------------------------------------------------------------------------------------------------------
+
+** How clientId values are generated
+
+  The actual value that is output by a component is determined by a number of 
rules depending on the context and type of component involved.  Of course,  no 
id will
+  be generated at all if your custom components don't call the new
+  
{{{../apidocs/org/apache/tapestry/AbstractComponent.html#renderIdAttribute(org.apache.tapestry.IMarkupWriter,%20org.apache.tapestry.IRequestCycle)}AbstractComponent.renderIdAttribute(IMarkupWriter,
 IRequestCycle)}}
+  method provided in the base <<<AbstractComponent>>> superclass that most 
components derive from.  When that method is called the rules for determining 
what value to render out
+  in to the generated html element are evaluated in this order:
+
+  [[1]] <<informal id parameter bound?>> - If the definition of the component 
being rendered had an informal <<<id="UpdatedComponent">>> parameter specified 
that will
+  take higher precendence than anything else.  It may eventually end up being 
output as <<<id="UpdatedComponent_4">>> if your component is being rendered in 
a loop but
+  it will still use the id as the basis for its client side id values.
+
++----------------------------------------------
+<div jwcid="@Any" id="customId" >Content</div>
++----------------------------------------------
+
+  [[1]] <<use IComponent.getId() value>> - If no informal id parameter is 
bound then the next candidate used to seed the clientIds is the value returned 
from
+  <<<IComponent.getId()>>>.  This will be the id specified in your 
<<<Page.page>>> file or the id assigned in your html snippet as in:
+
++----------------------------------------------
+<div jwcid="[EMAIL PROTECTED]">Content</div>
+
+or .page file:
+
+<component id="customId" type="Any">
+</component>
++----------------------------------------------
+
+  [[1]] <<form component?>> - If the component in question implements 
{{{../apidocs/org/apache/tapestry/form/IFormComponent.html}IFormComponent}} 
then the same semantics
+  as above apply with the caveat that the inner workings of client id 
generation work slightly differently here as they are synchronized back with 
the normal
+  {{{../components/form/form.html}Form}} input name/clientId semantics that 
Tapestry has always had.
+
+  []
+  
+  The previously mentioned <<<renderIdAttribute>>> method is the crucual piece 
that anyone writing custom components will need to make sure to call if you 
plan on
+  overriding <<<IComponent.renderComponent>>>.  One very simplistic example of 
calling this new base method would be:
+
++-------------------------------------------------------------------------------------------------------------
+..
+public abstract MyCustomComponent extends AbstractComponent {
+
+    public void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
+    {
+        writer.begin("div");
+
+        writer.renderIdAttribute(writer, cycle); 
<----------------------------------*
+        writer.renderInformalParamters(writer, cycle);
+
+        writer.print("Custom text content..");
+
+        writer.end();
+    }
+}
++-------------------------------------------------------------------------------------------------------------
+
+** Common Mistake:  Can't update a component not already rendered on the page
+
+  One of more common questions on the users mailing list inevitably involves 
this innocent looking block of code modified from our previous examples:
+
++-------------------------------------------------------------------------------------------------------------
+..
+<p>Basic javascript inclusion sample.</p>
+
+<p>
+    <a jwcid="@DirectLink" listener="listener:setTime" 
updateComponents="time">Refresh time</a>.
+</p>
+
+<span jwcid="@If" condition="ognl:time != null">
+    <div jwcid="[EMAIL PROTECTED]" value="ognl:time" renderTag="true" />
+</span>
+..
++-------------------------------------------------------------------------------------------------------------
+
+  The problem with this code is that the component being requested to update - 
<<<time>>> - doesn't actually exist as a valid client side
+  html element when you click on the <<<Refresh Time>>> link.  The initial 
block of html generated from this template will really look
+  like:
+
++-------------------------------------------------------------------------------------------------------------
+..
+<p>Basic javascript inclusion sample.</p>
+
+<p>
+    <a href="http://localhost:80080/app?..."; id="DirectLink"
+        onClick="return tapestry.linkOnClick(this.href)">Refresh time</a>.
+</p>
+..
++-------------------------------------------------------------------------------------------------------------
+
+  The crucial piece missing in the above sample snippet is an html block for 
our <<<time>>> component.  It wasn't rendered initially because
+  the time value was null.  The semantics of how the client side XHR io logic 
works basically comes down to:
+
+  * <<make XHR IO Request>> - Initiate client side XHR request which will 
eventually return a block of html for our <<<time>>> component
+  of:
+
++-------------------------------------------------------------------------------------------------------------
+<div id="time">10:49 am 1/2/2007</div>
++-------------------------------------------------------------------------------------------------------------
+
+  * <<find the matching client side dom node and update it>> - This is the 
part that breaks down.  The very basic minimal example of how it is
+  done would look something like:
+
++-------------------------------------------------------------------------------------------------------------
+..
+var responseNode = getResponseHtml(); // this is the time div block from the 
last example
+
+var updateNode = document.getElementById("time");
+updateNode.innerHTML = getContentAsString(responseNode);
+..
++-------------------------------------------------------------------------------------------------------------
+
+  The resulting client side error that is logged should be something along the 
lines of <<<"couldn't find a matching client side dom node to update with id of 
'time'">>>.
+
+  The solution to get around this varies for each situation but the most 
common / easiest method is usually done by just wrapping the block that 
conditionally renders
+  with a simple {{{../components/general/any.html}Any}} component:
+
++-------------------------------------------------------------------------------------------------------------
+..
+<p>Basic javascript inclusion sample.</p>
+
+<p>
+    <a jwcid="@DirectLink" listener="listener:setTime" 
updateComponents="updateArea">Refresh time</a>.
+</p>
+
+<span jwcid="[EMAIL PROTECTED]">
+    <span jwcid="@If" condition="ognl:time != null">
+        <div jwcid="[EMAIL PROTECTED]" value="ognl:time" renderTag="true" />
+    </span>
+</span>
+..
++-------------------------------------------------------------------------------------------------------------
+
+  Now the example specifies a <<<updateComponents="updateArea">>> definition 
for which components to update and has the 
{{{../components/general/any.html}Any}} updated
+  instead of <<<time>>> directly as it <will> exist on the client side prior 
to our requesting an update on it and shouldn't interfere overly much with 
whatever
+  CSS/design we have setup.
+  
\ No newline at end of file

Added: tapestry/tapestry4/trunk/src/site/apt/ajax/json.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/src/site/apt/ajax/json.apt?view=auto&rev=550318
==============================================================================
--- tapestry/tapestry4/trunk/src/site/apt/ajax/json.apt (added)
+++ tapestry/tapestry4/trunk/src/site/apt/ajax/json.apt Sun Jun 24 19:24:19 2007
@@ -0,0 +1,136 @@
+ ------
+Ajax/DHTML Guide - JSON (JavaScript Object Notation)
+ ------
+Jesse Kuhnert
+ ------
+24 June 2007
+ ------
+
+JSON - JavaScript Object Notation
+
+  JSON is a very popular data transmission format used in many XHR 
applications - the ever popular gmail application makes extensive use of it.  
The official
+  web site has much more information on all the details at {{http://json.org}}.
+
+  While the majority of default XHR (ajax) behaviour provided by Tapestry uses
+  XML as the exchange format - because of how easy it is to wrap existing html 
blocks - it does also provide a great deal of support/API functionality for you
+  to utilize JSON data transmission formats in your own applications.
+
+  This document will go over some of the basics of using this part of the API 
on the client / server side as well as show examples of how it is used by 
Tapestry
+  itself in components such as the 
{{{../components/dojo/autocompleter.html}Autocompleter}}. 
+
+* JSON Basics
+
+  The basic idea behind the JSON format is that you can output something using 
it from the server and your browser can evaluate it and access the structures 
you
+  define directly - like any other javascript object.  For example,  we could 
define a response listing the general attributes of a user in some theoretical 
system
+  we are building:
+
++--------------------------------------------------------------------------------------
+{name:"Dr. No", occupation:"Super villain", age:52, email:"[EMAIL PROTECTED]"}
++--------------------------------------------------------------------------------------
+
+  The above block of JSON can be interpreted and used on the client side in 
javascript with a couple simple statements:
+
++--------------------------------------------------------------------------------------
+var user=eval("{name:'Dr No', occupation: 'Super villain'}");
+alert("User occupation:" + user.occupation + " name:" + user.name);
++--------------------------------------------------------------------------------------
+
+  The format also supports returning array like structures as well as nesting 
of differnt kind of structures as in:
+
++--------------------------------------------------------------------------------------
+{users:[
+    {name:"Dr. No", occupation:"Super villain", age:52, email:"[EMAIL 
PROTECTED]"},
+    {name:"Henrietta Smith"},
+    {name:"Lev Nikolayevich Myshkin", occupation:"Idiot"}
+    ]
+}
+
+or just..:
+
+["10", "30", "14", "5"]
++--------------------------------------------------------------------------------------
+
+  You get the idea..  One of the more useful things provided by 
{{http://json.org}} is a sample java API for working with the format - 
{{http://www.json.org/java/index.html}}.
+  Tapestry has incorporated this java code in to the API proper <(with some 
minor improvements)> so creating our first example JSON object output can be as 
simple as:
+
++-----------------------------------------------------------------------------------------------------------------
+..
+org.apache.tapestry.json.JSONObject json = new JSONObject();
+json.put("name", "Dr. No");
+json.put("occupation", "Super villain");
+..
+calling json.toString() should produce:
+{"name":"Dr. No", "occupation":"Super villain"}
++-----------------------------------------------------------------------------------------------------------------
+
+  <<See also:>> 
{{{../apidocs/org/apache/tapestry/json/package-summary.html}Tapestry JSON API}}
+
+* {{{../apidocs/org/apache/tapestry/IJSONRender.html}IJSONRender}}:  Writing 
JSON capable components
+
+  To support this new format we've added a new optional interface that you can 
implement in your components - 
{{{../apidocs/org/apache/tapestry/IJSONRender.html}IJSONRender}}:
+
++-----------------------------------------------------------------------------------------------------------------
+public void renderComponent(IJSONWriter writer, IRequestCycle cycle)
+{
+}
++-----------------------------------------------------------------------------------------------------------------
+
+  The basic idea is the same as the typical 
<<<IComponent.renderComponent(IMarkupWriter, IRequestCycle)>>> call - except 
that in this case you are dealing with
+  a {{{../apidocs/org/apache/tapestry/json/IJSONWriter.html}IJSONWriter}} 
instance instead of the more familiar 
{{{../apidocs/org/apache/tapestry/IMarkupWriter.html}IMarkupWriter}}.
+  This interface is really just a wrapper around the JSON api provided by 
Tapestry.
+
+  Once you have implemented this 
{{{../apidocs/org/apache/tapestry/IJSONRender.html}IJSONRender}} interface in 
one of your components that is pretty much all there is to
+  do.  The JSON method you implement will only be called if a JSON request is 
processed by Tapestry <AND> the request has specified your component as one of 
the components
+  to update and capture the response of.  Otherwise the normal html markup 
based methods will be called on your component.
+
+** Client Side Processing
+
+  Processing json response data on the client side isn't really something 
Tapestry can do for you,  so you'll have to have your own consumer of this data 
set up
+  to handle it beforehand.  If you use the standard Tapestry API's then the 
global {{{../jsdoc/files/core-js.html#tapestry.loadJson}tapestry.loadJson}} 
javascript function
+  will be invoked.  Currently this function does nothing other than decrement 
the global <<<tapestry.requestsInFlight>>> javascript variable value.  You can 
replace or
+  do an event connection on this function to provide your own implementation.  
An example of doing an event connection would be:
+
++-----------------------------------------------------------------------------------------------------------------
+dojo.event.connect(tapestry, "loadJson", function(type, data, http, kwArgs){
+    // do your stuff...the data argument is your json object
+});
++-----------------------------------------------------------------------------------------------------------------
+
+* Example:  Tapestry's Autocompleter Component
+
+  One example of a core Tapestry component that implements the 
{{{../apidocs/org/apache/tapestry/IJSONRender.html}IJSONRender}} interface is 
the
+  {{{../components/dojo/autocompleter.html}Autocompleter}} component.  The 
method implementation of that component looks like this:
+
++-----------------------------------------------------------------------------------------------------------------
+public void renderComponent(IJSONWriter writer, IRequestCycle cycle)
+{
+   IAutocompleteModel model = getModel();
+
+   if (model == null)
+     throw Tapestry.createRequiredParameterException(this, "model");
+
+   List filteredValues = model.getValues(getFilter());
+
+   if (filteredValues == null)
+    return;
+
+   Object key = null;
+   String label = null;
+   JSONObject json = writer.object();
+
+   for (int i=0; i < filteredValues.size(); i++) {
+    Object value = filteredValues.get(i);
+
+    key = model.getPrimaryKey(value);
+    label = model.getLabelFor(value);
+    json.put(getDataSqueezer().squeeze(key), label );
+   }     
+}
++-----------------------------------------------------------------------------------------------------------------
+
+  This component actually makes use of some new base classes - like 
{{{../apidocs/org/apache/tapestry/dojo/form/AbstractFormWidget.html}AbstractFormWidget}}
 - don't
+  let this distract you from the <<<IJSONRender>>> interface portion.  They 
are both mutually exclusive and totally unrelated.
+
+  That is pretty much it.  This component hands off the majority of client 
side functionality to a Dojo widget and only provides the widget with a URL 
string produced
+  by a pre-generated JSON request.  You can find the full source and all of 
the gory details of the rest of
+  that 
{{{http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/dojo/form/Autocompleter.java?view=markup}here}}.

Modified: tapestry/tapestry4/trunk/src/site/apt/ajax/responsebuilder.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/src/site/apt/ajax/responsebuilder.apt?view=diff&rev=550318&r1=550317&r2=550318
==============================================================================
--- tapestry/tapestry4/trunk/src/site/apt/ajax/responsebuilder.apt (original)
+++ tapestry/tapestry4/trunk/src/site/apt/ajax/responsebuilder.apt Sun Jun 24 
19:24:19 2007
@@ -43,7 +43,7 @@
 @EventListener(targets = "projectChoose", events = "onValueChanged")
 public void projectSelected(IRequestCycle cycle)
 {
-       cycle.getResponseBuilder().updateComponent("myComponentId");
+  cycle.getResponseBuilder().updateComponent("myComponentId");
 }
 
 ....

Modified: tapestry/tapestry4/trunk/src/site/site.xml
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/src/site/site.xml?view=diff&rev=550318&r1=550317&r2=550318
==============================================================================
--- tapestry/tapestry4/trunk/src/site/site.xml (original)
+++ tapestry/tapestry4/trunk/src/site/site.xml Sun Jun 24 19:24:19 2007
@@ -82,19 +82,17 @@
             <item name="Development Guide" href="/developmentguide/index.html" 
collapse="true">
                 <item name="Exception Pages" 
href="/developmentguide/exceptionpages.html" />
                 <item name="Testing" href="/developmentguide/testing.html" />
-
-                <item name="HiveMind Services" 
href="/developmentguide/hivemind/index.html" >
-                    <item name="Rounded Corners" 
href="/developmentguide/hivemind/roundedcorners.html" />
-                </item>
-
+                <item name="Rounded Corners" 
href="/developmentguide/hivemind/roundedcorners.html" />
                 <item name="Performance Tuning" 
href="/developmentguide/performancetuning.html" />
             </item>
             
             <item name="XHR/DHTML Guide" href="/ajax/index.html" 
collapse="true">
                 <item name="Introduction" href="/ajax/index.html" />
+                <item name="Basics" href="/ajax/basics.html" />
                 <item name="Debugging" href="/ajax/debugging.html" />
                 <item name="EventListener" href="/ajax/eventlistener.html" />
                 <item name="ResponseBuilder" href="/ajax/responsebuilder.html" 
/>
+                <item name="JSON" href="/ajax/json.html" />
             </item>
             
             <item name="JavaScript Reference" href="/javascript/index.html" 
collapse="true">

Modified: 
tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/Home.page
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/Home.page?view=diff&rev=550318&r1=550317&r2=550318
==============================================================================
--- 
tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/Home.page
 (original)
+++ 
tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/Home.page
 Sun Jun 24 19:24:19 2007
@@ -22,6 +22,6 @@
 <page-specification class="org.apache.tapestry.timetracker.page.TaskEntryPage">
 
    <property name="dlHidden" initial-value="true" />
-   
+    
 </page-specification>
 


Reply via email to