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

Reply via email to