Ok, my head's gonna explode (yeah! I should've listened to Patrick
Casey!!)... but I have a solution *almost* there... At least for the
simpler case.
I think the perfomance considerations can be solved later, if they're
really needed. A simple "behind the scenes" form submit with some
components being refreshed would be enough for this case (better keep it
simple!)
Right now, I have a kind of derivation of AjaxDirectLink, AjaxForm,
Refresh, etc... components. I called "AjaxListenerInvoker" and it
generates a JavaScript function I can later hook into my components to
invoke a listener on "onchange()" events, or such.
For example: This generates a unique Javascript function on the body of
my document.
<span jwcid="[EMAIL PROTECTED]"
listener="listener:changeCountry"
updateComponents="ognl:{components.stateField.id}"
/>
Then, my 'master' select:
<div>
<label jwcid="@FieldLabel" field="component:country" />
<select jwcid="[EMAIL PROTECTED]" displayName="Country"
validators="validators:required"
onchange="ognl:components.countryChangeInvoker.href"
model="ognl:referenceData.getCountriesModel(true)"
value="ognl:value.country" />
</div>
And my 'detail' select:
<div jwcid="[EMAIL PROTECTED]"
id="ognl:components.stateField.id">
<label jwcid="@FieldLabel" field="component:state" />
<select jwcid="[EMAIL PROTECTED]" displayName="State"
validators="validators:required"
model="ognl:referenceData.getStatesModel(true, countryId)"
value="ognl:value.state" />
</div>
Now... right now a change in the master select would submit the form
(using AjaxListenerInvoker) and invoke a listener on the page. The
listener does nothing... just because right now the values are generated
on form rendering, not on listener invocation. That could change (and
does change on some components).
The only problem I have right now is that updateComponents is failing to
work. It seems that it invokes the listener fine, gets the appropiate
response to the selected country, but the states field disappears from
the page!! I'm hitting my head on the wall on this, maybe you can help out.
I implemented the script component similar to what Tacos' does. Like this:
AjaxListenerInvoker.script:
*****************************************************************************************************
<?xml version="1.0"?>
<!DOCTYPE script PUBLIC
"-//Apache Software Foundation//Tapestry Script Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Script_3_0.dtd">
<script>
<include-script resource-path="/net/sf/tacos/ajax/components/tacos.js" />
<include-script
resource-path="/net/sf/tacos/ajax/components/prototype.js" />
<input-symbol key="hiddenId" class="java.lang.String" required="yes"/>
<input-symbol key="formId" required="yes" />
<input-symbol key="url" required="yes" />
<input-symbol key="evalScripts" required="yes" />
<input-symbol key="updateObject" required="yes" />
<input-symbol key="effects" />
<input-symbol key="statusElement" />
<let key="functionName" unique="yes">
ajaxListenerInvoker_${hiddenId}
</let>
<let key="href">
javascript:${functionName}();
</let>
<body>
<unique> <![CDATA[
dojo.require("dojo.fx.html");
]]></unique>
<![CDATA[
function ${functionName}() {
Tapestry.find('${hiddenId}').value = "T";
tacos.formSubmit(
{
url: "${url}",
processScripts: "${evalScripts}",
effects: ${effects},
updateObject: ${updateObject},
formId: "${formId}",
changeUrl: false,
statusElement: "${statusElement}"
});
}
]]>
</body>
</script>
*****************************************************************************************************
AjaxListenerInvoker.jwc:
*****************************************************************************************************
<component-specification allow-body="no" allow-informal-parameters="no">
<parameter name="updateComponents" />
<parameter name="updateBlocks" />
<parameter name="processScripts" default-value="ognl:false" />
<parameter name="updateObject" />
<parameter name="listener" required="yes" />
<parameter name="parameters" />
<parameter name="direct" />
<parameter name="effects" />
<parameter name="statusElement" />
<parameter name="stateful" />
<parameter name="disabled" />
<parameter name="id" property="idParameter" default-value="id"/>
<parameter name="anchor"/>
<parameter name="renderer"/>
<inject property="listenerInvoker"
object="infrastructure:listenerInvoker"/>
<inject property="ajaxEngineService" object="service:tacos.ajaxdirect"/>
<inject property="script" type="script"
object="AjaxListenerInvoker.script"/>
<reserved-parameter name="href"/>
<reserved-parameter name="onclick"/>
</component-specification>
*****************************************************************************************************
AjaxListenerInvoker.java (I stripped all comments... after all they are
all in Spanish :P):
*****************************************************************************************************
import *;
/** @author Leonardo Quijano Vincenzi **/
public abstract class AjaxListenerInvoker extends AbstractFormComponent {
@Override
protected boolean getCanTakeFocus() {
return false;
}
protected boolean isClicked(IRequestCycle cycle, String name) {
String value = cycle.getParameter(name);
return HiveMind.isNonBlank(value);
}
public static Object[] constructServiceParameters(Object
parameterValue) {
Object result[] = null;
if(parameterValue != null) {
if(parameterValue instanceof Object[]) { result = (Object
[]) parameterValue; }
else if(parameterValue instanceof List) {
List list = (List) parameterValue;
result = list.toArray();
}
else { return new Object[] { parameterValue }; }
}
return result;
}
/** @see AbstractFormComponent#renderFormComponent(IMarkupWriter,
IRequestCycle) */
@SuppressWarnings("unchecked")
@Override
protected void renderFormComponent(IMarkupWriter writer,
IRequestCycle cycle) {
boolean disabled = isDisabled();
IForm form = getForm();
String name = getName();
String hiddenId =
cycle.getUniqueId(TapestryUtils.convertTapestryIdToNMToken(getIdParameter()));
setClientId(hiddenId);
form.addHiddenValue(name, hiddenId, "");
if(!disabled) {
Object[] parameters =
constructServiceParameters(getParameters());
String[] updateComponents = new String[0];
if (getUpdateComponents() != null) {
updateComponents = (String[]) getUpdateComponents()
.toArray(new String[getUpdateComponents().size()]);
}
//TODO: Add more html block logic hooks
String[] updateBlocks = new String[0];
if (getUpdateBlocks() != null) {
updateBlocks = (String[])getUpdateBlocks()
.toArray(new String[getUpdateBlocks().size()]);
}
AjaxDirectServiceParameter dsp = new AjaxDirectServiceParameter(
(IDirect) form, parameters, updateComponents,
updateBlocks, isDirect());
String reqUrl = getAjaxEngineService().getLink(
cycle, false, dsp).getAbsoluteURL();
Map<String, Object> symbols = new HashMap<String, Object>();
symbols.put("hiddenId", hiddenId);
symbols.put("formId", form.getName());
symbols.put("url", reqUrl);
symbols.put("evalScripts", String.valueOf(isProcessScripts()));
if(getUpdateObject() != null) {
symbols.put("updateObject", getUpdateObject());
}
else {
symbols.put("updateObject", "null");
}
if (getEffects() != null) {
symbols.put("effects", StringUtils.escapeJavaScriptHash(
getEffects()));
}
else{
symbols.put("effects", "{}");
}
if(getStatusElement() != null) {
symbols.put("statusElement", getStatusElement());
}
PageRenderSupport pageRenderSupport =
TapestryUtils.getPageRenderSupport(cycle, this);
getScript().execute(
cycle, pageRenderSupport, symbols);
setHref((String) symbols.get("href"));
setFunctionName((String) symbols.get("functionName"));
}
}
/** @see AbstractFormComponent#rewindFormComponent(IMarkupWriter,
IRequestCycle) */
@Override
protected void rewindFormComponent(IMarkupWriter writer,
IRequestCycle cycle) {
if(isClicked(cycle, getName())) {
handleClick(cycle, getForm());
}
}
private void handleClick(final IRequestCycle cycle, IForm form) {
final IActionListener listener = getListener();
final IActionListener action = getAction();
if(listener == null && action == null) {
return;
}
final ListenerInvoker listenerInvoker = getListenerInvoker();
Object parameters = getParameters();
if (parameters != null) {
if (parameters instanceof Collection) {
cycle.setListenerParameters(((Collection)
parameters).toArray());
}
else {
cycle.setListenerParameters(new Object[] { parameters });
}
}
if(listener != null) {
listenerInvoker.invokeListener(listener,
AjaxListenerInvoker.this, cycle);
}
if(action != null) {
Runnable notify = new Runnable() {
public void run() {
listenerInvoker.invokeListener(
action, AjaxListenerInvoker.this, cycle);
}
};
form.addDeferredRunnable(notify);
}
}
public abstract IActionListener getListener();
public abstract IActionListener getAction();
public abstract Object getParameters();
public abstract String getHref();
public abstract void setHref(String href);
public abstract ListenerInvoker getListenerInvoker();
public abstract IScript getScript();
public abstract AjaxDirectService getAjaxEngineService();
public abstract Collection getUpdateComponents();
public abstract Collection getUpdateBlocks();
public abstract boolean isProcessScripts();
public abstract String getUpdateObject();
public abstract boolean isDirect();
public abstract String getEffects();
public abstract String getStatusElement();
public abstract boolean isDisabled();
}
*****************************************************************************************************
--
Ing. Leonardo Quijano Vincenzi
Director Técnico
DTQ Software
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]