I am using Tapestry 5.3.6 for a web application, and I want the user to edit an instance of the Java class ("bean" or POJO) using a web form (which immediately suggests using beaneditform ) - however, the editable Java class has a rather complicated structure. I am looking for the easiest way to do this in Tapestry 5.
First, let's define some utility classes, for example.
public class ModelObject { private URI uri; private boolean modified;
Now you can create fairly complex data structures, for example:
public class HumanBeing extends ModelObject { private Literal<String> name;
If you specify beaneditform in an instance of Project.class, you won’t get very far before you have to supply many custom coercers, translators, cost icons, etc. - and then you still encounter a problem, you can’t use generics when they “contribute” to the mentioned coercors, translators, denominators, etc.
Then I started writing my own components to get around these issues (e.g. ModelObjectDisplay and ModelObjectEdit ), but it would require that I understand a lot more Gobelin guts than I have time to find out ... it feels like I can to do what I want using standard components and liberal use of the "delegate", etc. Can anyone see a simple way for me to take this?
Thanks for reading this.
PS: if you're wondering why I did such things, this is because the model is related data from an RDF graphic database (also called a triple-store) - I need to remember the URI of each data bit and how it relates (links ) with other data bits (you can also suggest better ways to do this :-)
EDIT:
@uklance suggested using display and edit blocks - this is what I have already tried:
Firstly, I had the following in AppPropertyDisplayBlocks.tml ...
<t:block id="literal"> <t:delegate to="literalType" t:value="literalValue" /> </t:block> <t:block id="link"> <t:delegate to="linkType" t:value="linkValue" /> </t:block>
and in AppPropertyDisplayBlocks.java ...
public Block getLiteralType() { Literal<?> literal = (Literal<?>) context.getPropertyValue(); Class<?> valueClass = literal.getValueClass(); if (!AppModule.modelTypes.containsKey(valueClass)) return null; String blockId = AppModule.modelTypes.get(valueClass); return resources.getBlock(blockId); } public Object getLiteralValue() { Literal<?> literal = (Literal<?>) context.getPropertyValue(); return literal.getValue(); } public Block getLinkType() { Link<?> link = (Link<?>) context.getPropertyValue(); Class<?> targetClass = link.getTargetClass(); if (!AppModule.modelTypes.containsKey(targetClass)) return null; String blockId = AppModule.modelTypes.get(targetClass); return resources.getBlock(blockId); } public Object getLinkValue() { Link<?> link = (Link<?>) context.getPropertyValue(); return link.getTarget(); }
AppModule.modelTypes is a map from the java class to String that Tapestry will use, for example. Link.class → "link" and Literal.class → "literal" ... in the AppModule I had the following code ...
public static void contributeDefaultDataTypeAnalyzer( MappedConfiguration<Class<?>, String> configuration) { for (Class<?> type : modelTypes.keySet()) { String name = modelTypes.get(type); configuration.add(type, name); } } public static void contributeBeanBlockSource( Configuration<BeanBlockContribution> configuration) {
I had similar code for editing blocks ... however, none of this seemed to work for me - I think because the original object was passed to the "delegate" and not the deleted object, which was either a value stored in a letter or an object, on which the link indicates (hmm ... should be [Ll] inkTarget in the above, and not in [Ll] inkValue). I also continued to encounter errors when the Tapestry could not find a suitable “translator”, “value coder” or “coercion” ... I am under some pressure of the time, so it is difficult to follow these winding passages to get out of the maze :-)