我正在使用Tapestry 5.3.6进行Web应用程序,我希望用户使用Web表单编辑Java类的实例(“bean”或POJO)(立即建议使用{{1但是 - 要编辑的Java类具有相当复杂的结构。我正在寻找Tapestry 5中最简单的方法。
首先,让我们定义一些实用程序类,例如
beaneditform
现在您可以创建一些相当复杂的数据结构,例如:
public class ModelObject {
private URI uri;
private boolean modified;
// the usual constructors, getters and setters ...
}
public class Literal<T> extends ModelObject {
private Class<?> valueClass;
private T value;
public Literal(Class<?> valueClass) {
this.valueClass = valueClass;
}
public Literal(Class<?> valueClass, T value) {
this.valueClass = valueClass;
this.value = value;
}
// the usual getters and setters ...
}
public class Link<T extends ModelObject> extends ModelObject {
private Class<?> targetClass;
private T target;
public Link(Class<?> targetClass) {
this.targetClass = targetClass;
}
public Link(Class<?> targetClass, T target) {
this.targetClass = targetClass;
this.target = target;
}
// the usual getters and setters ...
}
如果你将public class HumanBeing extends ModelObject {
private Literal<String> name;
// ... other stuff
public HumanBeing() {
name = new Literal<String>(String.class);
}
// the usual getters and setters ...
}
public class Project extends ModelObject {
private Literal<String> projectName;
private Literal<Date> startDate;
private Literal<Date> endDate;
private Literal<Integer> someCounter;
private Link<HumanBeing> projectLeader;
private Link<HumanBeing> projectManager;
// ... other stuff, including lists of things, that may be Literals or
// Links ... e.g. (ModelObjectList is an enhanced ArrayList that remembers
// the type(s) of the objects it contains - to get around type erasure ...
private ModelObjectList<Link<HumanBeing>> projectMembers;
private ModelObjectList<Link<Project>> relatedProjects;
private ModelObjectList<Literal<String>> projectAliases;
// the usual constructors, getters and setters for all of the above ...
public Project() {
projectName = new Literal<String>(String.class);
startDate = new Literal<Date>(Date.class);
endDate = new Literal<Date>(Date.class);
someCounter = new Literal<Integer>(Integer.class);
projectLeader = new Link<HumanBeing>(HumanBeing.class);
projectManager = new Link<HumanBeing>(HumanBeing.class);
projectMembers = new ModelObjectList<Link<HumanBeing>>(Link.class, HumanBeing.class);
// ... more ...
}
}
指向Project.class的一个实例,那么在你必须提供大量的自定义coercers,翻译器,值编码器等之前你不会走得太远 - 然后你仍然会遇到问题当“贡献”表示coercers,translators,valueencoders等时,你不能使用泛型。
然后我开始编写自己的组件来解决这些问题(例如beaneditform
和ModelObjectDisplay
),但这需要我理解更多Tapestry的内容而不是我有时间学习...感觉就像我可以使用标准组件和自由使用“委托”等来做我想做的事。任何人都可以看到一条简单的路径供我使用吗?
感谢您阅读此内容。
PS:如果你想知道为什么我这样做了,那是因为模型代表来自RDF图数据库(又称三重存储)的链接数据 - 我需要记住每一位数据的URI以及如何它与其他数据相关(链接)(欢迎您提出更好的方法: - )编辑:
@uklance建议使用显示和编辑块 - 这是我已经尝试过的:
首先,我在AppPropertyDisplayBlocks.tml中有以下内容......
ModelObjectEdit
并在AppPropertyDisplayBlocks.java ...
<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>
AppModule.modelTypes是从java类到Tapestry要使用的String的映射,例如: Link.class - &gt; “link”和Literal.class - &gt; “literal”...在AppModule中我有以下代码......
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();
}
我有类似的编辑块代码...但是这些似乎都没有用 - 我认为因为原始对象被传递给“委托”而不是被引用的对象,这是存储在文字或链接所指向的对象(嗯......应该是上面的[Ll] inkTarget,而不是[Ll] inkValue)。我也一直遇到错误,Tapestry找不到合适的“翻译”,“价值编码器”或“coercer”...我在一段时间的压力下因此很难跟随这些曲折的段落以便离开迷宫: - )
答案 0 :(得分:2)
我建议在你想通过BeanEditForm编辑的对象周围构建一个瘦包装器并将它们传递给它。如下所示:
public class TapestryProject {
private Project project;
public TapestryProject(Project proj){
this.project = proj;
}
public String getName(){
this.project.getProjectName().getValue();
}
public void setName(String name){
this.project.getProjectName().setValue(name);
}
etc...
}
这样,tapestry将处理它所知道的所有类型,让你不必创建自己的版本(顺便说一句,这本身就很简单)。
答案 1 :(得分:2)
您可以提供块来显示和编辑"link"
和"literal"
数据类型。
beaneditform
,beaneditor
和beandisplay
由BeanBlockSource
服务支持。 BeanBlockSource
负责为各种数据类型提供显示和编辑块。
如果您下载了tapestry源代码并查看了以下文件:
您将看到tapestry如何贡献EditBlockContribution
和DisplayBlockContribution
来提供默认块(例如,对于"date"
数据类型。)
如果您参与BeanBlockSource
,则可以为自定义数据类型提供显示和编辑块。这将要求您在页面中按ID引用块。通过@WhitelistAccessOnly
注释,可以向用户隐藏该页面。
答案 2 :(得分:0)
以下是使用界面和代理隐藏模型中的实现细节的示例。请注意代理如何处理更新修改后的标志,并能够将Lite的数据映射到HumanBeing界面中的属性。
package com.github.uklance.triplestore;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
public class TripleStoreOrmTest {
public static class Literal<T> {
public String uri;
public boolean modified;
public Class<T> type;
public T value;
public Literal(String uri, Class<T> type, T value) {
super();
this.uri = uri;
this.type = type;
this.value = value;
}
@Override
public String toString() {
return "Literal [uri=" + uri + ", type=" + type + ", value=" + value + ", modified=" + modified + "]";
}
}
public interface HumanBeing {
public String getName();
public void setName(String name);
public int getAge();
public void setAge();
}
public interface TripleStoreProxy {
public Map<String, Literal<?>> getLiteralMap();
}
@Test
public void testMockTripleStore() {
Literal<?>[] literals = {
new Literal<String>("http://humanBeing/1/Name", String.class, "Henry"),
new Literal<Integer>("http://humanBeing/1/Age", Integer.class, 21)
};
System.out.println("Before " + Arrays.asList(literals));
HumanBeing humanBeingProxy = createProxy(literals, HumanBeing.class);
System.out.println("Before Name: " + humanBeingProxy.getName());
System.out.println("Before Age: " + humanBeingProxy.getAge());
humanBeingProxy.setName("Adam");
System.out.println("After Name: " + humanBeingProxy.getName());
System.out.println("After Age: " + humanBeingProxy.getAge());
Map<String, Literal<?>> literalMap = ((TripleStoreProxy) humanBeingProxy).getLiteralMap();
System.out.println("After " + literalMap);
}
protected <T> T createProxy(Literal<?>[] literals, Class<T> type) {
Class<?>[] proxyInterfaces = { type, TripleStoreProxy.class };
final Map<String, Literal> literalMap = new HashMap<String, Literal>();
for (Literal<?> literal : literals) {
String name = literal.uri.substring(literal.uri.lastIndexOf("/") + 1);
literalMap.put(name, literal);
}
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().equals(TripleStoreProxy.class)) {
return literalMap;
}
if (method.getName().startsWith("get")) {
String name = method.getName().substring(3);
return literalMap.get(name).value;
} else if (method.getName().startsWith("set")) {
String name = method.getName().substring(3);
Literal<Object> literal = literalMap.get(name);
literal.value = args[0];
literal.modified = true;
}
return null;
}
};
return type.cast(Proxy.newProxyInstance(getClass().getClassLoader(), proxyInterfaces, handler));
}
}