My favorite Tapestry component has always been BeanEditor. It's versatile, simple and just about perfect as long as you like the mobile friendly output. Unfortunately not everyone appreciates the mobile esthetic and that when things fall apart. I wanted this
<t:beanEditor object="info" > <p:template> <div class="row"> <fieldSet> <legend>Name</legend> <property name="title" class="col-md-1"/> <property name="firstName" class="col-md-5"/> <property name="lastName" class="col-md-6"/> </fieldSet> </div> <div class="row"> <fieldSet> <legend>Contact</legend> <property name="email" class="col-md-4"/> <property name="phone" class="col-md-4"/> <property name="cell" class="col-md-4"/> </fieldSet> </div> <div class="row"> <property name="notes" class="col-md-10"/> </div> <div class="row"> <property name="age" class="col-md-2"/> <property name="password" class="col-md-2"/> </div> </p:template> </t:beanEditor> But never needed it bad enough to figure out how to do it until now. I thought about creating some tempting language but HTML seemed like the best choice. The problem was Tapestry wanted to turn my HTML into a block. Finally it occurred to me that was a feature. The idea is pass the block to a mixin. The mixin renders the block then applies the template in the cleanup render method. I'm still working out the features but I don't think I'll ever need to create a custom form again. Comments are welcome and I'll try and release a finished version soon. package com.trsvax.sandbox.mixins; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.tapestry5.Block; import org.apache.tapestry5.MarkupWriter; import org.apache.tapestry5.MarkupWriterListener; import org.apache.tapestry5.annotations.AfterRender; import org.apache.tapestry5.annotations.AfterRenderTemplate; import org.apache.tapestry5.annotations.BeginRender; import org.apache.tapestry5.annotations.CleanupRender; import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.dom.Element; public class BeanEditTemplate { @Parameter private Block template; private MarkupWriterListener listener; private Element container; private Element form; private List<Element> properties; private List<Element> templates; private Element firstFormGroup; private Map<String,Element> formGroups; @BeginRender void beginRender(MarkupWriter writer) { form = null; properties = new ArrayList<Element>(); templates = new ArrayList<Element>(); formGroups = new HashMap<String, Element>(); listener = new TemplateListener(); writer.addListener( listener); container = writer.element("mixin"); } @AfterRenderTemplate Object afterRenderTemplate(MarkupWriter writer) { return template; } @AfterRender void afterRender(MarkupWriter writer) { writer.end(); } @CleanupRender void cleanupRender(MarkupWriter writer) { writer.removeListener(listener); for ( Element e : templates) { e.moveBefore(firstFormGroup); } for ( Element property : properties ) { String name = property.getAttribute("name"); if ( formGroups.containsKey(name)) { Element formGroup = formGroups.get(name); formGroup.moveAfter(property); formGroup.attribute("class", property.getAttribute("class")); property.pop(); } } container.pop(); } private String keyFor(Element element) { String key = element.getAttribute("id"); int i = key.indexOf("_"); if ( i > 0 ) { key = key.substring(0,i); } return key; } private boolean isInput(Element element) { String name = element.getName(); if ( name.equals("input")) { return true; } if ( name.equals("select")) { return true; } if ( name.equals("textarea")) { return true; } return false; } private boolean isFormGroup(Element element) { if ( element.getAttribute("class") == null ) { return false; } if ( element.getAttribute("class").equals("form-group") ) { return true; } return false; } private class TemplateListener implements MarkupWriterListener { @Override public void elementDidStart(Element e) { if ( e.getName().equals("property")) { properties.add(e); } if ( isInput(e) && isFormGroup(e.getContainer()) ) { if ( firstFormGroup == null ) { firstFormGroup = e.getContainer(); } formGroups.put(keyFor(e), e.getContainer()); } if ( form != null ) { if ( e.getContainer().equals(container)) { templates.add(e); } } } @Override public void elementDidEnd(Element e) { if ( e.getName().equals("form")) { form = e; } } } } --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org