A little different refactor and i come to this class:
I think this is much easier to understand and to implement.
any objections for this relative small api change?
public abstract class AbstractDefaultAjaxBehavior extends
AbstractAjaxBehavior
{
private static final long serialVersionUID = 1L;
/** reference to the default indicator gif file. */
public static final ResourceReference INDICATOR = new ResourceReference(
AbstractDefaultAjaxBehavior.class, "indicator.gif");
/** reference to the default ajax support javascript file. */
private static final ResourceReference JAVASCRIPT = new
JavascriptResourceReference(
AbstractDefaultAjaxBehavior.class, "wicket-ajax.js");
/** reference to the default ajax debug support javascript file. */
private static final ResourceReference JAVASCRIPT_DEBUG = new
JavascriptResourceReference(
AbstractDefaultAjaxBehavior.class, "wicket-ajax-debug.js");
/**
* Subclasses should call super.onBind()
*
* @see wicket.behavior.AbstractAjaxBehavior#onBind()
*/
protected void onBind()
{
getComponent().setOutputMarkupId(true);
}
/**
* @see wicket.behavior.AbstractAjaxBehavior#renderHead(
wicket.markup.html.IHeaderResponse)
*/
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
final IDebugSettings debugSettings = Application.get
().getDebugSettings();
response.renderJavascriptReference(WicketEventReference.INSTANCE);
response.renderJavascriptReference(JAVASCRIPT);
if (debugSettings.isAjaxDebugModeEnabled())
{
response.renderJavascript("wicketAjaxDebugEnable=true;",
"wicket-ajax-debug-enable");
response.renderJavascriptReference(JAVASCRIPT_DEBUG);
}
}
/**
* @return ajax call decorator used to decorate the call generated by
this
* behavior or null for none
*/
protected IAjaxCallDecorator getAjaxCallDecorator()
{
return null;
}
/**
* @return javascript that will generate an ajax GET request to this
* behavior
*/
protected CharSequence getCallbackScript()
{
return getCallbackScript(false);
}
/**
* @return javascript that will generate an ajax GET request to this
* behavior *
* @param onlyTargetActivePage
* if true the callback to this behavior will be ignore if
the
* page is not the last one the user accessed
*
*/
protected CharSequence getCallbackScript(boolean onlyTargetActivePage)
{
return generateCallbackScript("wicketAjaxGet('" +
getCallbackUrl(onlyTargetActivePage) + "'");
}
/**
* @return javascript that will run when the ajax call finishes
* successfully
*/
protected CharSequence getPreconditonScript()
{
return null;
}
/**
* @return javascript that will run when the ajax call finishes with an
* error status
*/
protected CharSequence getFailureScript()
{
return null;
}
/**
* @return an optional javacript expression that determines whether the
request
* will actually execute (in form of return XXX;);
*/
protected CharSequence getSuccessScript()
{
return null;
}
/**
* Returns javascript that performs an ajax callback to this behavior.
The
* script is decorated by the ajax callback decorator from
* [EMAIL PROTECTED] AbstractDefaultAjaxBehavior#getAjaxCallDecorator()}.
*
* @param partialCall
* Javascript of a partial call to the function performing
the
* actual ajax callback. Must be in format
* <code>function(params,</code> with signature
* <code>function(params, onSuccessHandler,
onFailureHandler</code>.
* Example: <code>wicketAjaxGet('callbackurl'</code>
*
* @return script that peforms ajax callback to this behavior
*/
protected CharSequence generateCallbackScript(final CharSequence
partialCall)
{
final CharSequence onSuccessScript = getSuccessScript();
final CharSequence onFailureScript = getFailureScript();
final CharSequence precondition = getPreconditonScript();
final IAjaxCallDecorator decorator = getAjaxCallDecorator();
String indicatorId = findIndicatorId();
CharSequence success = (onSuccessScript == null) ? "" :
onSuccessScript;
CharSequence failure = (onFailureScript == null) ? "" :
onFailureScript;
if (decorator != null)
{
success = decorator.decorateOnSuccessScript(success);
}
if (!Strings.isEmpty(indicatorId))
{
String hide = ";wicketHide('" + indicatorId + "');";
success = success + hide;
failure = failure + hide;
}
if (decorator != null)
{
failure = decorator.decorateOnFailureScript(failure);
}
AppendingStringBuffer buff = new AppendingStringBuffer(256);
buff.append("var ").append(IAjaxCallDecorator.WICKET_CALL_RESULT_VAR
).append("=");
buff.append(partialCall).append(", function() { ").append(success);
buff.append("}.bind(this), function() {
").append(failure).append("}.bind(this)");
if (precondition != null)
{
buff.append(", function() {");
buff.append(precondition);
buff.append("}");
}
buff.append(");");
CharSequence call = buff;
if (!Strings.isEmpty(indicatorId))
{
call = new
AppendingStringBuffer("wicketShow('").append(indicatorId).append("');")
.append(call);
}
if (decorator != null)
{
call = decorator.decorateScript(call);
}
return call;
}
/**
*
* @return String
*/
private String findIndicatorId()
{
if (getComponent() instanceof IAjaxIndicatorAware)
{
return
((IAjaxIndicatorAware)getComponent()).getAjaxIndicatorMarkupId();
}
if (this instanceof IAjaxIndicatorAware)
{
return ((IAjaxIndicatorAware)this).getAjaxIndicatorMarkupId();
}
Component parent = getComponent().getParent();
while (parent != null)
{
if (parent instanceof IAjaxIndicatorAware)
{
return
((IAjaxIndicatorAware)parent).getAjaxIndicatorMarkupId();
}
parent = parent.getParent();
}
return null;
}
/**
* @see wicket.behavior.IBehaviorListener#onRequest()
*/
public final void onRequest()
{
AjaxRequestTarget target = new AjaxRequestTarget();
RequestCycle.get().setRequestTarget(target);
respond(target);
}
/**
* @param target
* The AJAX target
*/
// TODO rename this to onEvent or something? respond is mostly the same
as
// onRender
// this is not the case this is still the event handling period. respond
is
// called
// in the RequestCycle on the AjaxRequestTarget..
protected abstract void respond(AjaxRequestTarget target);
/**
* Wraps the provided javascript with a throttled block. Throttled
behaviors
* only execute once within the given delay even though they are
triggered
* multiple times.
* <p>
* For example, this is useful when attaching an event behavior to the
* onkeypress event. It is not desirable to have an ajax call made every
* time the user types so we throttle that call to a desirable delay,
such
* as once per second. This gives us a near real time ability to provide
* feedback without overloading the server with ajax calls.
*
* @param script
* javascript to be throttled
* @param throttleId
* the id of the throttle to be used. Usually this should
remain
* constant for the same javascript block.
* @param throttleDelay
* time span within which the javascript block will only
execute
* once
* @return wrapped javascript
*/
public static final CharSequence throttleScript(CharSequence script,
String throttleId,
Duration throttleDelay)
{
if (Strings.isEmpty(script))
{
throw new IllegalArgumentException("script cannot be empty");
}
if (Strings.isEmpty(throttleId))
{
throw new IllegalArgumentException("throttleId cannot be
empty");
}
if (throttleDelay == null)
{
throw new IllegalArgumentException("throttleDelay cannot be
null");
}
return new AppendingStringBuffer("wicketThrottler.throttle(
'").append(throttleId).append(
"', ").append(throttleDelay.getMilliseconds()).append(",
function() { ").append(
script).append("});");
}
}
On 6/7/07, Johan Compagner <[EMAIL PROTECTED]> wrote:
Hi,
i like to do a bit of refactor on the AbstractDefaultAjaxBehavior class
because i find the api very messy and what to implement or what to call is
very hard to understand
First what we have now:
AbstractAjaxBehavior had 2 methods:
getCallBackUrl() and getCallBackUrl(boolean)
thats all fine, now i move to the subclass
AbstractDefaultAjaxBehavior does have a getCallbackScript() and
getCallbackScript(boolean)
which is sort of the same thing as getCallBackUrl() but they encapsulate
it
until now its still good but.
besides that we have in AbstractDefaultAjaxBehavior:
protected CharSequence getCallbackScript(final CharSequence partialCall,
final CharSequence onSuccessScript, final CharSequence onFailureScript)
and
protected CharSequence getCallbackScript(final CharSequence
partialCall,final CharSequence onSuccessScript, final CharSequence
onFailureScript,final CharSequence precondition)
and thats a bit funny,what does have the boolean one in common with the
other 2??
and what method do i have to implement to get a precondition in the right
place?
what do i need to call then afterwards.
i think this would be much better:
this stays the same:
protected CharSequence getCallbackScript()
{
return getCallbackScript(false);
}
this:
protected CharSequence getCallbackScript(boolean onlyTargetActivePage)
{
return getCallbackScript("wicketAjaxGet('"+
getCallbackUrl(onlyTargetActivePage) + "'", null, null);
}
should become:
protected CharSequence getCallbackScript(boolean onlyTargetActivePage)
{
return getCallbackScript("wicketAjaxGet('"+
getCallbackUrl(onlyTargetActivePage) + "'", getSuccessScript(),
getFailureScript(),getPreconditonScript());
}
This one can be removed then:
protected CharSequence getCallbackScript(final CharSequence
partialCall,final CharSequence onSuccessScript, final CharSequence
onFailureScript)
And this one can (doesn't have to be but i guess api wise it is a bit
better) be made final
protected CharSequence getCallbackScript(final CharSequence
partialCall,final CharSequence onSuccessScript, final CharSequence
onFailureScript, final CharSequence precondition)
and i would like to rename it to generateCallBackScript(xxxx)
because it is not really in line with the 2 other once (no param and
boolean param)
and we add 4 extra methods:
getSuccessScript(), getFailureScript(),getPreconditonScript()
that by default just return null;
Then if you want a successscript or precondition script you just implement
those getters.
johan