如何使用ListEditor作为另一个编辑器的子项? [英] How must one use a ListEditor as a child of another Editor?

查看:89
本文介绍了如何使用ListEditor作为另一个编辑器的子项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用GWT 2.5.0



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



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



从下面的代码中,我创建了一个由另一个编辑器 GroupListEditor 组成的 ParentBeanEditor

GroupListEditor 实现 IsEditor< ListEditor< Group,GroupEditor>>

然后, GroupEditor 包含一个 GroupListEditor 子编辑器和一个 ChildBeanEditor $ b

我用 ParentBean ParentBeanEditor $ c>其中包含 Group 对象的列表,但没有 GroupEditor 曾被构造用于任何对象。
将断点放在 EditorSource< GroupEditor> .create(int)方法中,以验证 GroupEditor s正在为 ParentBean 中的每个 Group 创建,但是断点从未被命中(ListEditor没有构造编辑器)。

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



示例



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



数据模型



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

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

public interface ChildBean {
// ChildType是一个枚举
ChildType getChildType();
}



编辑



ParentBean编辑器

  public class ParentBeanEditor扩展了Composite implements ValueAwareEditor< ParentBean> {

接口ParentBeanEditorUiBinder扩展了UiBinder< Widget,ParentBeanEditor> {
}

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

@Path(groups)
@UiField
GroupListEditor groupsEditor;

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

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

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

@Override
public void setValue(ParentBean value){

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


$ / code>

GroupListEditor

  public class GroupListEditor扩展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;

$ b @Override
public GroupEditor create(int index){
GroupEditor subEditor = new GroupEditor();
GroupListEditor.getGroupsContainer()。insert(subEditor,index);
返回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(){
返回编辑器;


GroupEditor

  public class GroupEditor extends Composite implements ValueAwareEditor< Group> {

接口GroupEditorUiBinder扩展了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));

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

@Override
public void flush() {}

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

@Override
public void setValue(Group value){
//当设置该值时,更新FieldSet标题文本
groupField.setHeadingText(value.getLabel());
groupsEditor.asEditor()。setValue(value.getGroups());
childBeansEditor.asEditor()。setValue(value.getChildBeans());




$ b ChildBeanListEditor 将使用多态编辑器方法提到此处。也就是说,根据 ChildBean.getType()枚举的值,将特定的编辑器附加到编辑器链中。但是,我没有显示该代码,因为我无法获取 GroupListEditor 来正确初始化。

解决方案

为什么> ParentBeanEditor.setValue 为您的代码提供数据它的孩子?由此看来,这是解决 GroupListEditor 没有获取数据这一事实的一种方法。这不应该是必要的,并且可能在时间到来之前通过连接子编辑器而导致您的NPE。

然后,假设这一点,似乎遵循<$ $
$ b

c $ c> GroupListEditor 没有获取数据链。缺乏这些表明编辑框架没有意识到它。所有的基本接线看起来都是正确的,除了一件事:你的EditorDriver在哪里?

如果你试图通过调用来使用编辑器框架, parentBeanEditor.setValue 并且没有驱动程序,因此缺少了此工具的大部分主要功能。您应该能够让驱动程序为您完成这项工作,而不是在整个树中调用您自己的setValue方法。



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



首先,尝试驱动程序本身:



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



驱动程序在两件事上是通用的:您想要编辑的bean类型以及将对其负责的编辑器类型。它使用这些泛型参数来遍历这两个对象并生成绑定数据所需的代码。你可能看起来像这样:

  public interface Driver扩展
SimpleBeanEditorDriver< ParentBean,ParentBeanEditor> {}

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



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

  ParentBeanEditor编辑器= ...; 
驱动程序驱动程序= GWT.create(Driver.class);
driver.initialize(editor);

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

  driver.edit(parentInstance) ; 

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

  ParentBean instance = driver.flush(); 

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



假设这包含所有到目前为止有意义,有一些可以完成的清理 - ParentBeanEditor实际上并没有使用ValueAwareEditor方法,因此它们可以被删除:

  public class ParentBeanEditor扩展了Composite implements Editor< ParentBean> {

接口ParentBeanEditorUiBinder扩展了UiBinder< Widget,ParentBeanEditor> {
}

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

@Path(groups)
@UiField
GroupListEditor groupsEditor;

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


$ / code>

观察到我们仍然执行 Editor< ParentBean> - 这允许驱动程序泛型有意义,并且声明我们有可能自己是子编辑器的字段。另外:事实证明,这里 @Path 注释是不必要的 - 任何与属性名称相同的字段/方法( getGroups() code> / setGroups() ==> groups )或者属性名称加上'Editor' ( groupsEditor )。如果编辑器包含的字段是编辑器,但没有映射到bean中的属性,则会出现错误。如果你真的是故意这样做的(比如搜索文本框,而不是数据输入),你可以用 @Ignore 来标记它。


I am using GWT 2.5.0

My intent was to create an editor hierarchy which binds to a ParentBean object. The ParentBean contains a List<Group>, and the Group bean has a List<ChildBean> and List<Group>. From the Editor tutorials I have found, it seemed simple enough to create an editor which contains a ListEditor as one of its sub-editors. But the parent editor never seems to properly initialize the sub ListEditor.

Here is an explanation of how I attempted to do this.

From the code below, I created a ParentBeanEditor which is composed of one other editor, GroupListEditor.
The GroupListEditor implements IsEditor<ListEditor<Group, GroupEditor>>.
Then, the GroupEditor contains a GroupListEditor subeditor and a ChildBeanEditor.

I initialized the ParentBeanEditor with a ParentBean which contained a list of Group objects, but no GroupEditor was ever constructed for any of the Group objects.
I put break points in the EditorSource<GroupEditor>.create(int) method to verify that GroupEditors were being created for each Group in the ParentBean, but the break point was never hit (the ListEditor was not constructing editors).

I expected that the GroupListEditor would be initialized since it was a subeditor of ParentBeanEditor. Neither the list nor the editor chain was set in the GroupListEditor. I tried to set the list of the GroupListEditor subeditor directly in ParentBeanEditor by having it extend ValueAwareEditor<ParentBean>. Doing this, the break point I mentioned above was hit, and the GroupListEditor tried to attach a GroupEditor to the editor chain. But the editor chain was never set, and a NPE is thrown in ListEditorWrapper line 95.

Example

Here is the example where the GroupListEditor is not initializing as expected. The EditorChain is never set, and this results in a NPE being thrown in ListEditorWrapper line 95.

Data Model

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

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

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

Editors

The ParentBean Editor

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());
    }
}

The ChildBeanListEditor will be using the polymorphic editor methodology mention here. Meaning that a specific leafeditor is attached to the editor chain based off the value of the ChildBean.getType() enum. However, I am not showing that code since I am unable to get the GroupListEditor to properly initialize.

解决方案

Two concerns about your code:

Why is ParentBeanEditor.setValue feeding data to its child? It appears from this that this was a way to work around the fact that the GroupListEditor was not getting data. This should not be necessary, and may be causing your NPE by wiring up a subeditor before it is time.

Then, assuming this, it seems to follow that the GroupListEditor isn't getting data or a chain. The lack of these suggests that the Editor Framework isn't aware of it. All the basic wiring looks correct, except for one thing: Where is your EditorDriver?

If you are trying to use the editor framework by just invoking parentBeanEditor.setValue and do not have a driver, you are missing most of the key features of this tool. You should be able to ask the driver to do this work for you, and not not to call your own setValue methods throughout the tree.

A quick test - try breaking something in such a way that shouldn't compile. This would include changing the @Path annotation to something like @Path("doesnt.exist"), and trying to run the app. You should get a rebind error, as there is no such path. If you do not get this, you definitely need to be creating and user a driver.

First, try driver itself:

It isn't quite clear from your code what kind of models you are using, so I'll assume that the SimpleBeanEditorDriver will suffice for you - the other main option is the RequestFactoryEditorDriver, but it isn't actually necessary to use the RequestFactoryEditorDriver even if you use RequestFactory.

The Driver is generic on two things: The bean type you intend to edit, and the editor type that will be responsible for it. It uses these generic arguments to traverse both objects and generate code required to bind the data. Yours will likely look like this:

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

We declare these just like UiBinder interfaces - just enough details to let the code generator look around and wire up essentials. Now that we have the type, we create an instance. This might be created in your view, but may still be owned and controlled by some presenter logic. Note that this is not like uibinder - we cannot keep a static instance, since each one is wired directly to a specific editor instance.

Two steps here - create the driver, and initialize it to a given editor instance (and all sub-editors, which will be automatic):

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

Next we bind data by passing it to the driver - it is its responsibility to pass sub-objects to each sub-editor's setValue method, as well as wiring up the editor chain required by the ListEditor.

driver.edit(parentInstance);

Now the user can view or edit the object, as your application requirement works. When editing is complete (say they click the Save button), we can flush all changes from the editors back into the instance (and note that we are still using the same driver instance, still holding that specific editor instance):

ParentBean instance = driver.flush();

Note that we also could have just invoked driver.flush() and reused the earlier reference to parentInstance - its the same thing.

Assuming this has all made sense so far, there is some cleanup that can be done - ParentBeanEditor isn't really using the ValueAwareEditor methods, so they can be removed:

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));
    }
}

Observe that we still implement Editor<ParentBean> - this allows the driver generics to make sense, and declares that we have fields that might themselves be sub-editors to be wired up. Also: it turns out that the @Path annotation here is unnecessary - any field/method with the same name as the property (getGroups()/setGroups() ==> groups) or the name of the property plus 'Editor' (groupsEditor). If the editor contains a field that is an editor but doesn't map to a property in the bean, you'll get an error. If you actually did this on purpose (say, a text box for searching, not for data entry), you can tag it with @Ignore.

这篇关于如何使用ListEditor作为另一个编辑器的子项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆