如何使用ListEditor作为另一个编辑器的子项?

时间:2012-12-06 16:54:48

标签: gwt gxt gwt-editors

我正在使用GWT 2.5.0

我的目的是创建一个绑定到ParentBean对象的编辑器层次结构。 ParentBean包含List<Group>Group bean包含List<ChildBean>List<Group>。从我发现的编辑器教程中,创建一个包含ListEditor作为其子编辑器的编辑器似乎很简单。但父编辑似乎从未正确初始化子ListEditor。

以下解释了我是如何尝试这样做的。

从下面的代码中,我创建了一个ParentBeanEditor,它由另一个编辑器GroupListEditor组成。
GroupListEditor实施IsEditor<ListEditor<Group, GroupEditor>> 然后,GroupEditor包含GroupListEditor子编辑者和ChildBeanEditor

我使用ParentBeanEditor初始化了ParentBean,其中包含Group个对象的列表,但是没有为任何GroupEditor个对象构建Group
我在EditorSource<GroupEditor>.create(int)方法中设置了断点,以验证GroupEditor中每个Group是否正在创建ParentBean,但断点从未被点击过(ListEditor没有构建编辑器。)

我预计GroupListEditor会被初始化,因为它是ParentBeanEditor的副编辑。列表和编辑器链都没有在GroupListEditor中设置。我尝试通过扩展GroupListEditor直接在ParentBeanEditor中设置ValueAwareEditor<ParentBean>子编辑器的列表。这样做,上面提到的断点被击中,GroupListEditor试图将GroupEditor附加到编辑器链。但编辑链从未设置过,NPE被引入ListEditorWrapper第95行。

实施例

以下是GroupListEditor未按预期初始化的示例。永远不会设置EditorChain,这会导致NPE被ListEditorWrapper第95行抛出。

数据模型

public interface ParentBean {
    ...
    List<Group> getGroups();
}

public interface Group {
    ...
    List<ChildBean> getChildBeans();
    List<Group> getGroups();
}

public interface ChildBean {
    // ChildType is an enum
    ChildType getChildType();
}

编辑

ParentBean编辑器

public class ParentBeanEditor extends Composite implements ValueAwareEditor<ParentBean> {

    interface ParentBeanEditorUiBinder extends UiBinder<Widget, ParentBeanEditor> {
    }

    private static ParentBeanEditorUiBinder BINDER = GWT.create(ParentBeanEditorUiBinder.class);

    @Path("groups")
    @UiField
    GroupListEditor groupsEditor;

    public ParentBeanEditor() {
        initWidget(BINDER.createAndBindUi(this));
    }

    @Override
    public void setDelegate(EditorDelegate<ParentBean> delegate) {}

    @Override
    public void flush() {}

    @Override
    public void onPropertyChange(String... paths) {}

    @Override
    public void setValue(ParentBean value) {

        groupsEditor.asEditor().setValue(value.getGroups());
    }
}

GroupListEditor

public class GroupListEditor extends Composite implements IsEditor<ListEditor<Group, GroupEditor>>{

    interface GroupListEditorUiBinder extends UiBinder<VerticalLayoutContainer, TemplateGroupListEditor> {
    }

    private static GroupListEditorUiBinder BINDER = GWT.create(GroupListEditorUiBinder.class);

    private class GroupEditorSource extends EditorSource<GroupEditor> {

        private final GroupListEditor GroupListEditor;

        public GroupEditorSource(GroupListEditor GroupListEditor) {
            this.GroupListEditor = GroupListEditor;
        }

        @Override
        public GroupEditor create(int index) {
            GroupEditor subEditor = new GroupEditor();
            GroupListEditor.getGroupsContainer().insert(subEditor, index);
            return subEditor;
        }

        @Override
        public void dispose(GroupEditor subEditor){
            subEditor.removeFromParent();
        }

        @Override
        public void setIndex(GroupEditor editor, int index){
            GroupListEditor.getGroupsContainer().insert(editor, index);
        }

    }

    private final ListEditor<Group, GroupEditor> editor = ListEditor.of(new GroupEditorSource(this));

    @UiField
    VerticalLayoutContainer groupsContainer;

    public GroupListEditor() {
       initWidget(BINDER.createAndBindUi(this));
    }

    public InsertResizeContainer getGroupsContainer() {
        return groupsContainer;
    }

    @Override
    public ListEditor<Group, GroupEditor> asEditor() {
        return editor;
    }
}

GroupEditor

public class GroupEditor extends Composite implements ValueAwareEditor<Group> {

    interface GroupEditorUiBinder extends UiBinder<Widget, GroupEditor> {}

    private static GroupEditorUiBinder BINDER = GWT.create(GroupEditorUiBinder.class);

    @Ignore
    @UiField
    FieldSet groupField;

    @UiField
    @Path("childBeans")
    ChildBeanListEditor childBeansEditor;

    @UiField
    @Path("groups")
    GroupListEditor groupsEditor;

    public GroupEditor() {
        initWidget(BINDER.createAndBindUi(this));
    }

    @Override
    public void setDelegate(EditorDelegate<Group> delegate) {}

    @Override
    public void flush() { }

    @Override
    public void onPropertyChange(String... paths) {}

    @Override
    public void setValue(Group value) {
        // When the value is set, update the FieldSet header text
        groupField.setHeadingText(value.getLabel());
        groupsEditor.asEditor().setValue(value.getGroups());
        childBeansEditor.asEditor().setValue(value.getChildBeans());
    }
}

ChildBeanListEditor将使用提及here的多态编辑器方法。这意味着特定的leafeditor基于ChildBean.getType()枚举的值附加到编辑器链。但是,我没有显示该代码,因为我无法正确初始化GroupListEditor

1 个答案:

答案 0 :(得分:0)

关于您的代码的两个问题:

为什么ParentBeanEditor.setValue向其孩子提供数据?从中可以看出,这是一种解决GroupListEditor无法获取数据这一事实的方法。这不应该是必要的,并且可能在时间之前通过连接子编辑器来引起NPE。

然后,假设这一点,似乎遵循GroupListEditor没有获取数据链。缺乏这些表明编辑框架没有意识到这一点。所有基本布线看起来都是正确的,除了一件事:您的EditorDriver在哪里?

如果您只是通过调用parentBeanEditor.setValue并且没有驱动程序来尝试使用编辑器框架,那么您将缺少此工具的大多数关键功能。您应该能够让驱动程序为您完成这项工作,而不是在整个树中调用您自己的setValue方法。

快速测试 - 尝试以不应编译的方式破坏某些东西。这包括将@Path注释更改为@Path("doesnt.exist"),并尝试运行该应用。你应该得到一个重新绑定错误,因为没有这样的路径。如果你没有得到这个,你肯定需要创建和使用驱动程序。

首先,尝试驱动程序:

从你的代码中不太清楚你正在使用什么样的模型,所以我假设SimpleBeanEditorDriver对你来说就足够了 - 另一个主要选项是RequestFactoryEditorDriver,但是即使您使用RequestFactory,也不一定需要使用RequestFactoryEditorDriver

驱动程序在两个方面是通用的:您打算编辑的bean类型以及将负责它的编辑器类型。它使用这些通用参数遍历两个对象并生成绑定数据所需的代码。你的可能会是这样的:

public interface Driver extends 
        SimpleBeanEditorDriver<ParentBean, ParentBeanEditor> { }

我们声明这些就像UiBinder接口一样 - 只需要足够的细节让代码生成器环顾四周并连接必需品。现在我们有了类型,我们创建了一个实例。这可能会在您的视图中创建,但仍可能由某些演示者逻辑拥有和控制。请注意,这是 not ,就像uibinder一样 - 我们无法保留静态实例,因为每个实例都直接连接到特定的编辑器实例。

这里有两个步骤 - 创建驱动程序,并将其初始化为给定的编辑器实例(以及所有子编辑器,这将是自动的):

ParentBeanEditor editor = ...;
Driver driver = GWT.create(Driver.class);
driver.initialize(editor);

接下来,我们通过将数据传递给驱动程序来绑定数据 - 它有责任将子对象传递给每个子编辑器的setValue方法,以及连接ListEditor所需的编辑器链。

driver.edit(parentInstance);

现在,用户可以查看或编辑对象,因为您的应用程序需求正常。编辑完成后(比如他们单击“保存”按钮),我们可以将编辑器中的所有更改刷新回实例(并注意我们仍在使用相同的驱动程序实例,仍然保留该特定编辑器实例):

ParentBean instance = driver.flush();

请注意,我们也可以刚刚调用driver.flush()并重复使用之前对parentInstance的引用 - 这是同样的事情。

假设到目前为止这一切都有意义,可以做一些清理 - ParentBeanEditor并没有真正使用ValueAwareEditor方法,所以可以删除它们:

public class ParentBeanEditor extends Composite implements Editor<ParentBean> {

    interface ParentBeanEditorUiBinder extends UiBinder<Widget, ParentBeanEditor> {
    }

    private static ParentBeanEditorUiBinder BINDER = GWT.create(ParentBeanEditorUiBinder.class);

    @Path("groups")
    @UiField
    GroupListEditor groupsEditor;

    public ParentBeanEditor() {
        initWidget(BINDER.createAndBindUi(this));
    }
}

观察我们仍然实现Editor<ParentBean> - 这允许驱动程序泛型有意义,并声明我们有可能本身是子编辑器的字段被连接起来。另外:事实证明此处的@Path注释是不必要的 - 任何与属性同名的字段/方法(getGroups() / setGroups() ==&gt; groups)或者属性的名称加上'编辑'(groupsEditor)。如果编辑器包含一个编辑器但不映射到bean中的属性的字段,则会出现错误。如果您实际上是故意这样做的(例如,用于搜索的文本框,而不是用于数据输入),则可以使用@Ignore对其进行标记。