I ended up figuring out something that works and it turned out to be both
more elegant (as it ends up being more clean and reusable than I expected)
and a bit more twisted ( I totally didn't expect that I'd have to use this
@HeartbeatDeferred approach, I expected something more straightforward)
than I expected. I'm attaching the resulting component at the end of this
email in case someone else runs into this or a similar question .

I still have a question though - obviously, currently the component doesn't
have its own template. As a result, I was able to use the
@HeartbeatDeferred updateComponent() method to update the error class after
the field with the error renders. I originally thought that my component
template would like like the following:

<div class="control-group"  xmlns:t="...">
   <t:body />
</div>

However, with this approach, I don't know how I can use the same approach
to update the "class" attribute after the input field has rendered. I
thought that I could

<t:any t:id="wrapperDiv" class="control-group"  xmlns:t="...">
   <t:body />
</t:any>

and in the component
@Component
private Any wrapperDiv

However, the Any class doesn't have any methods that I can use to update
the "class" attribute in the @HeartbeatDeferred updateComponent() method.
Alternatively, I thought that I could just return this wrapperDiv Any
component in the beginRender() method; however, I still don't know how I
could update the "class" attribute if I did that. Any tips on how I should
approach that ( that is, I would prefer having a component template as I
can add somewhat richer markup that I'd enjoy doing inside of the Java
class) ?

The other approach that I was thinking of that this could have been a
decent candidate for a mixin - as far as I understood, the mixin can be set
up to run after the component renders, so that the input field would have
rendered and the field error would be available to the mixin. Any thoughts
?

Any tips or ideas would be much appreciated.

Cheers,

Alex K
-----------------


Sample Usage:

                    <div t:type="BsControlGroup"
t:inputField="companyNameField">
                        <t:label class="control-label"
for="companyNameField">Company Name</t:label>
                        <div class="controls" >
                            <input  class="input-medium"
placeholder="Company Name" t:type="textField" t:id="companyNameField"
t:value="companyName" t:validate="required,minlength=3, maxlength=100"/>

                            <span class="help-inline">
                                <t:error for="companyNameField" />
                            </span>
                        </div>
                    </div>


/**
 *
 * @author akochnev
 */
public class BsControlGroup {

    @Parameter(defaultPrefix = BindingConstants.COMPONENT, required = true)
    private Field inputField;

    @Parameter(defaultPrefix = "literal", required = false, value = "error")
    private String errorClass;

    @Environmental(false)
    private ValidationTracker tracker;

    void beginRender(final MarkupWriter writer) {
        if (tracker == null) {
            throw new
RuntimeException(InternalMessages.encloseErrorsInForm());
        }
        Element element = writer.element("div");
        updateElement(element);
    }

    void afterRender(MarkupWriter writer) {
        writer.end();
    }

    @HeartbeatDeferred
    private void updateElement(final Element element) {
        final String error = tracker.getError(inputField);

        if (error == null) {
            element.attribute("class", "control-group");
        } else {
            element.attribute("class", "control-group " + this.errorClass);

        }
    }
}


On Tue, Jul 17, 2012 at 5:13 PM, Alex Kotchnev <akoch...@gmail.com> wrote:

> I have a very simple form and I'm trying to add a special class to a div
> inside the form which would indicate if the field that's contained inside
> the div has an error. It seems that it should be simple to do so, but I
> can't seem to do it.
>
> I'm trying to style the form using the Bootstrap form styles (
> http://twitter.github.com/bootstrap/). In summary, in order to mark a
> field as in error, I need to be able to add an extra class to otherwise
> quite regular markup, e.g. :
>
> <t:form t:id="companyForm" class="well form-horizontal" t:validate="this"
> clientValidation="false">
>     <fieldset>
>         <div class="control-group  [--- This is where I need to put an
> 'error' class if the field has an error --] ">
>             <t:label class="control-label" for="companyNameField">Company
> Name</t:label>
>             <div class="controls" >
>                 <input  class="input-medium" placeholder="Company Name"
> t:type="textField" t:id="companyNameField" t:value="companyName"
> t:validate="required,minlength=3, maxlength=100"/>
>                 <span class="help-inline">
>                     <t:error for="companyNameField" />
>                 </span>
>             </div>
>         </div>
>
>     </fieldset>
> </t:form>
>
> Sounds like it should be simple enough, but so far I have been unable to
> make it produce an error class in the place of the  [--- This is where I
> need to put an 'error' class if the field has an error --] placeholder.
> What I tried:
>
> 1. Added a method on the page "getCompanyNameFieldClass()" and added <div
> class="control-group ${getCompanyNameFieldClass()}"> . The implementation
> of the method basically is a one liner (after a bunch of null checks) that
> returns a string based on
>  "companyForm.getDefaultTracker().inError(companyNameField)" . This always
> returns false.
>
> 2. Injected @Environmental @Property private ValidationTracker into the
> page, then attempted the same on the page int he curly braces , e.g.
> ${validationTracker.isError(companyNameField)}, which is always false.
> Interestingly enough, right next to it, I have an "t:error" component which
> seems to properly render the error from the field. Similarly,
> validationTracker.getHasErrors() seems to return true when there is an
> error - just not when I pass the field in.
>
> This seems like it should be very simple; yet, I can't seem to find
> anything in the docs or an example of how this could be done. The thing
> that comes the closest seems to be the Jumpstart "No Validation Bubbles"
> example (
> http://jumpstart.doublenegative.com.au/jumpstart/examples/input/novalidationbubbles1)
> but it seems like an overkill to have to extend a component and do crazy
> things w/ the validation decorator for something this simple.
>
> I took at peek at the Tapestry bootstrap integration module, but so far I
> haven't been able to make heads or tails of where something similar might
> be happening there.
>
> Can someone point me what direction I should be looking at ? This should
> be simple, right ?
>
> Cheers,
>
> Alex K
>
>
>
>
>

Reply via email to