<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- We need a doctype to allow us to use special characters like
We use a "strict" DTD to make IE follow the alignment rules. -->
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
<body class="container">
<h3>AJAX Select Dependency (1)</h3>
<noscript class="js-required">
${message:javascript_required}
</noscript>
In this example, Source and Make are independent of each other, but Model depends on Make AND Source combined.<br/>
Note that there are no models in Local Honda or Imported Holden combinations.
<div class="eg">
<t:form t:id="searchCriteria">
<div class="form-group form-inline">
<div class="form-group">
<t:zone t:id="carSourceZone" id="carSourceZone" style="display: inline;">
<t:label for="carSource"/>
<t:select t:id="carSource" model="carSources" blankOption="ALWAYS" blankLabel="Choose..." validate="required" secure="never"
zone="carModelZone"/>
</t:zone>
</div>
<div class="form-group">
<t:zone t:id="carMakeZone" id="carMakeZone" style="display: inline;">
<t:label for="carMake"/>
<t:select t:id="carMake" model="carMakes" blankOption="ALWAYS" blankLabel="Choose..." validate="required" secure="never"
zone="carModelZone"/>
</t:zone>
</div>
<div class="form-group">
<t:zone t:id="carModelZone" id="carModelZone" style="display: inline;">
<t:label for="carModel"/>
<t:select t:id="carModel" model="carModels" blankOption="ALWAYS" blankLabel="Choose..." validate="required" secure="never"/>
</t:zone>
</div>
<div class="form-group">
<t:label for="keywords"/>
<t:textfield t:id="keywords" class="keywords"/>
</div>
<div class="form-group">
<p class="form-control-static">(optional)</p>
</div>
</div>
<div class="form-group">
<t:submit value="Save"/>
</div>
<t:errors globalOnly="true"/>
</t:form>
</div>
The tricky part of this is that AJAX Select requests pass only one context value, ie. the value of the Select; <br/>
so when you change Source how can you also send the chosen Make, and when you change Make how can you also send the chosen Source?<br/>
We solve it by persisting <em>chosenCarSource</em> and <em>chosenCarMake</em> in the generated URLs by using <em>@ActivationRequestParameter</em>.<br/><br/>
References:
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/ActivationRequestParameter.html">@ActivationRequestParameter</a>,
<a href="http://tapestry.apache.org/ajax-and-zones.html">Ajax and Zones</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/Zone.html">Zone</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/Select.html">Select</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/TextField.html">TextField</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/Request.html">Request</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/ajax/AjaxResponseRenderer.html">AjaxResponseRenderer</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/InjectComponent.html">@InjectComponent</a>.<br/><br/>
<t:pagelink page="Index">Home</t:pagelink><br/><br/>
<t:tabgroup>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/ajax/AjaxSelectDependency1.tml"/>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/ajax/AjaxSelectDependency1.java"/>
<t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/ajaxselect1.css"/>
</t:tabgroup>
</body>
</html>
package jumpstart.web.pages.examples.ajax;
import org.apache.tapestry5.annotations.ActivationRequestParameter;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.InjectPage;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.Zone;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
@Import(stylesheet = "css/examples/ajaxselect1.css")
public class AjaxSelectDependency1 {
static final private String SOURCE_LOCAL = "Local";
static final private String SOURCE_IMPORTED = "Imported";
static final private String[] ALL_SOURCES = new String[] { SOURCE_LOCAL, SOURCE_IMPORTED };
static final private String MAKE_HOLDEN = "Holden";
static final private String MAKE_HONDA = "Honda";
static final private String MAKE_TOYOTA = "Toyota";
static final private String[] ALL_MAKES = new String[] { MAKE_HOLDEN, MAKE_HONDA, MAKE_TOYOTA };
static final private String MODEL_COMMODORE = "Commodore";
static final private String MODEL_CAMRY = "Camry";
static final private String MODEL_COROLLA = "Corolla";
static final private String MODEL_PRIUS = "Prius";
static final private String MODEL_ACCORD = "Accord";
static final private String MODEL_CIVIC = "Civic";
static final private String MODEL_JAZZ = "Jazz";
static final private String[] LOCAL_HOLDEN_MODELS = new String[] { MODEL_COMMODORE };
static final private String[] IMPORTED_HOLDEN_MODELS = new String[] {};
static final private String[] LOCAL_TOYOTA_MODELS = new String[] { MODEL_CAMRY };
static final private String[] IMPORTED_TOYOTA_MODELS = new String[] { MODEL_CAMRY, MODEL_COROLLA, MODEL_PRIUS };
static final private String[] LOCAL_HONDA_MODELS = new String[] {};
static final private String[] IMPORTED_HONDA_MODELS = new String[] { MODEL_ACCORD, MODEL_CIVIC, MODEL_JAZZ };
static final private String[] NO_MODELS = new String[] {};
// Activation request parameters (AKA query parameters)
@ActivationRequestParameter("source")
private String chosenCarSource;
@ActivationRequestParameter("make")
private String chosenCarMake;
// Screen fields
@Property
private String[] carSources;
@Property
private String carSource;
@Property
private String[] carMakes;
@Property
private String carMake;
@Property
private String[] carModels;
@Property
private String carModel;
@Property
private String keywords;
// Other pages
@InjectPage
private AjaxSelectDependency2 page2;
// Generally useful bits and pieces
@InjectComponent
private Zone carSourceZone;
@InjectComponent
private Zone carMakeZone;
@InjectComponent
private Zone carModelZone;
@Inject
private Request request;
@Inject
private AjaxResponseRenderer ajaxResponseRenderer;
// The code
void setupRender() {
carSources = ALL_SOURCES;
carMakes = ALL_MAKES;
carModels = NO_MODELS;
}
void onValueChangedFromCarSource(String carSource) {
// Record the source in the activation parameters (AKA query parameters) so it is available in requests from the
// other zones.
chosenCarSource = carSource;
// Refresh the makes and models.
carMake = chosenCarMake;
carMakes = ALL_MAKES;
refreshCarModels();
if (request.isXHR()) {
ajaxResponseRenderer.addRender(carMakeZone).addRender(carModelZone);
}
}
void onValueChangedFromCarMake(String carMake) {
// Record the make in the activation parameters (AKA query parameters) so it is available in requests from the
// other zones.
chosenCarMake = carMake;
// Refresh the sources and models.
carSource = chosenCarSource;
carSources = ALL_SOURCES;
refreshCarModels();
if (request.isXHR()) {
ajaxResponseRenderer.addRender(carSourceZone).addRender(carModelZone);
}
}
private void refreshCarModels() {
// Show the models of the chosen source AND make.
carModel = null;
carModels = NO_MODELS;
if (chosenCarSource != null && chosenCarMake != null) {
if (chosenCarSource.equals(SOURCE_LOCAL)) {
if (chosenCarMake.equals(MAKE_HOLDEN)) {
carModels = LOCAL_HOLDEN_MODELS;
}
else if (chosenCarMake.equals(MAKE_HONDA)) {
carModels = LOCAL_HONDA_MODELS;
}
else if (chosenCarMake.equals(MAKE_TOYOTA)) {
carModels = LOCAL_TOYOTA_MODELS;
}
}
else if (chosenCarSource.equals(SOURCE_IMPORTED)) {
if (chosenCarMake.equals(MAKE_HOLDEN)) {
carModels = IMPORTED_HOLDEN_MODELS;
}
else if (chosenCarMake.equals(MAKE_HONDA)) {
carModels = IMPORTED_HONDA_MODELS;
}
else if (chosenCarMake.equals(MAKE_TOYOTA)) {
carModels = IMPORTED_TOYOTA_MODELS;
}
}
}
}
Object onSuccess() {
page2.set(carSource, carMake, carModel, keywords);
return page2;
}
}
.eg {
margin: 20px 0;
padding: 14px;
color: #888;
border: 1px solid #ddd;
border-radius: 6px;
-webkit-border-radius: 6px;
-mox-border-radius: 6px;
}
.eg select {
width: auto;
margin-right: 12px;
}
.eg .keywords {
width: auto;
}