I've knocked up a solution, a reusable mixin called AjaxValidate. It's loosely based on Inge's ZoneUpdater and OnEvent (many thanks, Inge).
Please post suggestions on how to improve it! Example of usage: <input t:type="TextField" t:id="firstName" t:validate="required, maxlength=10" t:mixins="ajaxValidate" size="10"/> ...and in your page or component: Object onAjaxValidateFromFirstName() { String firstName = _request.getParameter("param"); try { validateFirstName(firstName); return new JSONObject(); } catch (Exception e) { return new JSONObject().put("error", e.getMessage()); } } Here's the mixin - AjaxValidate.java: @IncludeJavaScriptLibrary("ajax_validate.js") public class AjaxValidate { // Parameters @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "default") private String prefix; // Useful bits and pieces @Inject private ComponentResources resources; @Environmental private RenderSupport renderSupport; /** * The element we attach ourselves to */ @InjectContainer private ClientElement element; // The code void afterRender() { String listenerURI = resources.createEventLink("ajaxValidate").toAbsoluteURI(); String elementId = element.getClientId(); JSONObject spec = new JSONObject(); spec.put("listenerURI", listenerURI); spec.put("elementId", elementId); renderSupport.addScript("%sAjaxValidate = new AjaxValidate(%s)", prefix, spec.toString()); } } ajax_validate.js: function addRequestParameter(name, value, url) { if (url.indexOf('?') < 0) { url += '?' } else { url += '&'; } value = escape(value); url += name + '=' + value; return url; } var AjaxValidate = Class.create({ initialize : function(spec) { this.element = $(spec.elementId); this.listenerURI = spec.listenerURI; // After client-side validators have completed normally we asynchronously validate in the server. this.element.observe(Tapestry.FIELD_VALIDATE_EVENT, this.asyncValidateInServer.bindAsEventListener(this)); }, asyncValidateInServer : function() { var value = this.element.value; var listenerURIWithParam = this.listenerURI; if (value) { listenerURIWithParam = addRequestParameter('param', value, this.listenerURI); } new Ajax.Request(listenerURIWithParam, { method: 'get', onFailure: function(t) { alert('Error communication with the server: ' + t.responseText.stripTags()); }, onException: function(t, exception) { alert('Error communication with the server: ' + exception.message); }, onSuccess: function(t) { if (t.responseJSON.error) { this.element.showValidationMessage(t.responseJSON.error); } }.bind(this) }); } }); On 23/06/2010, at 8:08 PM, Geoff Callender wrote: > Maybe it would be simpler to use a mixin, one that calls back to > showValidationMessage()? Yes, please post it. > > On 23/06/2010, at 3:49 AM, Katia Aresti Gonzalez wrote: > >> I have an example with a mixin ... it's in not very very simple, but i can >> simplify the mixin for your example... ^_^ >> >> >> 2010/6/20 Geoff Callender <geoff.callender.jumpst...@gmail.com> >> >>> Surely someone has an example of this? >>> >>> On 18/06/2010, at 12:51 PM, Geoff Callender wrote: >>> >>>> Thanks, Thiago, but it lacks AJAX. I'd like the validator to behave like >>> any other client-side validator except that it asks the server to help, eg. >>> to validate that a name has not already been used. >>>> >>>> Geoff >>>> >>>> On 18/06/2010, at 12:24 PM, Thiago H. de Paula Figueiredo wrote: >>>> >>>>> On Thu, 17 Jun 2010 22:20:45 -0300, Geoff Callender < >>> geoff.callender.jumpst...@gmail.com> wrote: >>>>> >>>>>> Hi all, >>>>> >>>>> Hi! >>>>> >>>>> This is an example of a RGB string validator that I created for my >>> Tapestry courses (basic and advanced): >>>>> >>>>> public class RGBValidator extends AbstractValidator<Void, String> { >>>>> >>>>> final private static Pattern PATTERN = >>>>> Pattern.compile("[0-9A-Fa-f]{6}"); >>>>> >>>>> final private RenderSupport renderSupport; >>>>> >>>>> final private Asset javascript; >>>>> >>>>> public RGBValidator( >>>>> AssetSource assetSource, RenderSupport >>> renderSupport) { >>>>> >>>>> super(null, String.class, "rgb-validation"); >>>>> this.renderSupport = renderSupport; >>>>> javascript = assetSource.getClasspathAsset( >>>>> >>> "br/com/arsmachina/cursotapestry/validacao/rgb.js"); >>>>> >>>>> } >>>>> >>>>> public void render(Field field, Void constraintValue, >>>>> MessageFormatter formatter, MarkupWriter writer, >>>>> FormSupport formSupport) { >>>>> >>>>> String message = buildMessage(field, formatter); >>>>> >>>>> // adds JavaScript validation >>>>> formSupport.addValidation(field, "rgb", message, null); >>>>> >>>>> // includes rgb.js in the page >>>>> renderSupport.addScriptLink(javascript); >>>>> >>>>> // adds a hint to the user. just an example of generating >>>>> // HTML inside a validator. >>>>> writer.element("span", "class", "hint"); >>>>> writer.write("Must be filled with 6 digits or letters from >>> A to F."); >>>>> writer.end(); >>>>> >>>>> } >>>>> >>>>> @Override >>>>> public void validate(Field field, Void constraintValue, >>>>> MessageFormatter formatter, String value) >>>>> throws ValidationException { >>>>> >>>>> if (PATTERN.matcher(value).matches() == false) { >>>>> String message = buildMessage(field, formatter); >>>>> throw new ValidationException(mensagem); >>>>> } >>>>> >>>>> } >>>>> >>>>> private String buildMesssage(Field field, MessageFormatter >>> formatter) { >>>>> return formatter.format(field.getLabel()); >>>>> } >>>>> >>>>> } >>>>> >>>>> rgb.js: >>>>> >>>>> Tapestry.Validator.rgb = function(field, message) { >>>>> var regexp = /[0-9A-F]{6}/i; >>>>> field.addValidator(function(value) { >>>>> if (regexp.test(value) == false && value.length > 0) { >>>>> throw message; >>>>> } >>>>> }); >>>>> }; >>>>> >>>>> -- >>>>> Thiago H. de Paula Figueiredo >>>>> Independent Java, Apache Tapestry 5 and Hibernate consultant, developer, >>> and instructor >>>>> Owner, Ars Machina Tecnologia da Informação Ltda. >>>>> http://www.arsmachina.com.br >>>>> >>>>> --------------------------------------------------------------------- >>>>> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >>>>> For additional commands, e-mail: users-h...@tapestry.apache.org >>>>> >>>> >>> >>> >>> --------------------------------------------------------------------- >>> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >>> For additional commands, e-mail: users-h...@tapestry.apache.org >>> >>> >