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
>>> 
>>> 
> 

Reply via email to