blob: 4e820349c164114eebe286d13bbc05712664cf4f [file] [log] [blame]
<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<jx:template xmlns:jx="http://apache.org/cocoon/templates/jx/1.0" xmlns:cinclude="http://apache.org/cocoon/include/1.0"
xmlns:i18n="http://apache.org/cocoon/i18n/2.1" xmlns:ft="http://apache.org/cocoon/forms/1.0#template" xmlns:fi="http://apache.org/cocoon/forms/1.0#instance">
<!-- Import the macros that define CForms template elements -->
<jx:import uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link href="local-resources/dreamteam.css" type="text/css" rel="stylesheet"/>
<title>The Dream Team</title>
</head>
<body>
<h1>The Dream Team</h1>
<p>Now is the time to choose your dream team.</p>
<div class="legend">
<table>
<tr>
<td><i>Legend</i></td>
</tr>
<tr>
<td>
<img src="resources/forms/img/move_up.gif"/>
</td>
<td>Move this row 1 up</td>
</tr>
<tr>
<td>
<img src="resources/forms/img/move_down.gif"/>
</td>
<td>Move this row 1 down</td>
</tr>
<tr>
<td>
<img src="resources/forms/img/delete.gif"/>
</td>
<td>Delete this row</td>
</tr>
<tr>
<td>
<img src="resources/forms/img/new.gif"/>
</td>
<td>Add a new row below this row</td>
</tr>
</table>
</div>
<ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST" ajax="true">
<ft:repeater id="teammembers">
<jx:choose>
<jx:when test="${form.getChild('teammembers').getSize() == 0}">
<p>
<strong> There is no team yet. </strong> <br/>
</p>
</jx:when>
<jx:otherwise>
<table>
<thead>
<tr>
<td class="tableheader">
<ft:repeater-widget-label widget-id="position"/>
</td>
<td class="tableheader">
<ft:repeater-widget-label widget-id="memberId"/>
</td>
<td class="tableheader">
<ft:repeater-widget-label widget-id="country"/>
</td>
<td class="tableheader">
Actions
<!--ft:repeater-widget-label widget-id="select"/-->
</td>
</tr>
</thead>
<tbody>
<!-- The contents of the repeater-rows element
is a template that will be applied to each
row in the repeater.
-->
<ft:repeater-rows>
<tr>
<td>
<ft:widget id="position">
<fi:styling submit-on-change="true"/>
</ft:widget>
</td>
<td>
<ft:widget id="memberId">
<fi:styling submit-on-change="true"/>
</ft:widget>
</td>
<td>
<!-- FIXME: there's a bug below with Ajax, as the i18n:text element surrounds
the widget, and is therefore not produced when only the widget is updated
(and not the surrounding row) -->
<i18n:text><ft:widget id="country"/></i18n:text>
</td>
<td>
<!-- Don't display the "move up" button
on the first row
-->
<jx:choose>
<jx:when test="${repeaterLoop.first}">
<img src="resources/forms/img/blank_btn.gif"/>
</jx:when>
<jx:otherwise>
<ft:widget id="up">
<fi:styling type="image" src="resources/forms/img/move_up.gif"/>
</ft:widget>
</jx:otherwise>
</jx:choose>
<!-- Don't display the "move down" button
on the last row
-->
<jx:choose>
<jx:when test="${repeaterLoop.last}">
<img src="resources/forms/img/blank_btn.gif"/>
</jx:when>
<jx:otherwise>
<ft:widget id="down">
<fi:styling type="image" src="resources/forms/img/move_down.gif"/>
</ft:widget>
</jx:otherwise>
</jx:choose>
<ft:widget id="delete">
<fi:styling type="image" src="resources/forms/img/delete.gif"/>
</ft:widget>
<ft:widget id="add">
<fi:styling type="image" src="resources/forms/img/new.gif"/>
</ft:widget>
<!--ft:widget id="select"/-->
</td>
</tr>
</ft:repeater-rows>
</tbody>
</table>
</jx:otherwise>
</jx:choose>
</ft:repeater>
<table>
<tr>
<td style="border: 0px solid;">
<ft:widget id="addmember"/>
</td>
<td style="border: 0px solid;">
<jx:if test="${widget.getChild('teammembers').getSize() &gt; 0}">
<ft:widget id="removemember"/>
</jx:if>
</td>
</tr>
<tr>
<td style="border: 0px solid;">
<input type="submit" value="Submit"/>
</td>
<td style="border: 0px solid;"/>
</tr>
</table>
</ft:form-template>
<p class="developer-header"> Developer Notes </p>
<h2>Page definition</h2>
<p class="developer-note"> This page is defined in <ul class="developer-note">
<li>
<b>teamTemplate.jx</b>, the form template, </li>
<li>
<b>teamDef.xml</b>, the widget definitions and</li>
<li>
<b>teamBind.xml</b>, the widget binding to Java Beans.</li>
</ul>
<p class="developer-note"> The pipeline calls the function <i>prot_buildTeam</i> in <b>team.js</b>. There is no
naming rule for flow functions, but adding a prefix ('prot_') as a simple kind of security measure to prevent
outsiders from directly calling the function. Security by obscurity you might call it. </p>
<h2>What happens?</h2>
<p class="developer-note"> In <i>prot_buildTeam</i> a list of 'positions' is generated from the information in
the Manager class and passed onto the form. </p>
<p class="developer-note"> Below is the definition of the <i>position</i> widget. </p>
<div class="code">
<div class="codeIndent"> &lt;fd:field id="position"&gt; <div class="codeIndent">
&lt;fd:label&gt;Position&lt;/fd:label&gt; </div>
<div class="codeIndent"> &lt;fd:datatype base="string"/&gt; </div>
<div class="codeIndent"> &lt;fd:selection-list dynamic="true" type="flow-jxpath"
list-path="positionList" value-path="value" label-path="label"/&gt; </div>
<div class="codeIndent"> &lt;fd:on-value-changed&gt; <div class="codeIndent">
&lt;fd:javascript&gt; <div class="codeIndent"> // this script can be found in team.js<br/>
updateNameWidget(event); </div> &lt;/fd:javascript&gt; </div>
&lt;/fd:on-value-changed&gt; </div> &lt;/fd:field&gt; </div>
</div>
<p class="developer-note"> The widget <i>position</i> has a selection-list of type <i>flow-jxpath</i>. The other
attributes define the list to use: </p>
<ul class="developer-note">
<li>
<i>list-path</i> holds the name of the list that is passed in the "viewData" attribute </li>
<li>
<i>value-path</i> holds the name of the variable or method that returns the value to use </li>
<li>
<i>label-path</i> holds the name of the variable or method that returns the label for the value </li>
</ul>
</p>
<h2>Internationalization</h2>
<p class="developer-note"> The function also shows how to build this list with localized (i.e. i18n) labels. </p>
<div class="code">
<div class="codeIndent"> ...<br/> // key = key code for position<br/> // set the label to the localized message
using i18n<br/> positionList[i + 1] = {value: key, label: new I18nMessage(key)};<br/> ... </div>
</div>
<p class="developer-note"> Adding the appropriate code to switch locale is left as an exercise. :-) </p>
<h2>Dependency between widgets</h2>
<p class="developer-note"> The section <i>on-value-changed</i> holds the javascript that handles the dependency.
When the value of the <i>positionlist</i> changes, the <i>memberId</i> widget should get a new list of
names.<br/> The script that handles this update can be entered directly between the <i>javascript</i> tags, but
adding a descriptive function name there and moving the function to a separate javascript file keeps the widget
definition file clearer. Note the <i>event</i> parameter.<br/> Remember to add the
<i>submit-on-change="true"</i> attribute to the styling tag! </p>
<h2>Repeater validation</h2>
<p class="developer-note"> This repeater also has a repeater validation section, which prevents using duplicate
names. Again, the script can be entered directly in the widget definition file, but it can also be added to a
function that is placed in a separate javascript file. <b>Note:</b> this function takes <i>widget</i> (the
current repeater) as parameter and <b>MUST</b> return a boolean value (true = success).<br/> The repeater
validation function is called when the <i>Submit</i> button is pressed. If a validation error occurs, the first
invalid row will be marked with a blue <font style="color: blue">!</font>. </p>
<h2>Some observations</h2>
<p class="developer-note"> The samples provide a great set of XSL files to convert the template and styling tags
of the widgets into HTML. They start with <b>forms-samples-styling.xsl</b> and can be found in the
<i>samples/blocks/forms/resources</i> folders. </p>
<p class="developer-note">
<b>Note:</b> when you use these XSL files, remember to use them as the last transformation in your pipeline, or
make sure they find the appropriate tags to add some scripts to the html <i>&lt;head&gt;</i> and
<i>&lt;body&gt;</i> tags. Especially without a reference to the <b>forms-lib.js</b> file, the
on-value-changed event will not be sent to the server and therefore no script is executed. </p>
<p class="developer-note">
<b>Note:</b> At this moment (2004/10/20) a bug exists that throws a NullPointerException when the javascript in
the validation section is executed with the Rhino debugger enabled! </p>
<h2>When everything is valid</h2>
<p class="developer-note"> When <i>Submit</i> is pressed, and all rows validate, the <i>prot_buildTeam</i>
function takes over and hands the selected team to the Manager class for further processing. Finally the
function passes flow onto the <i>showteam</i> pipeline. </p>
<p>
<a href="../">Back to Forms samples</a>
</p>
</body>
</html>
</jx:template>