GWT CompositeEditor - 动态切换的编辑器未正确添加到链中

时间:2016-07-28 12:26:11

标签: java gwt uibinder gwt-editors

我有一个值类型Commission,它是更大的编辑器图形的一部分:

public class Commission implements Serializable
{
    private CommissionType commissionType; // enum
    private Money value; // custom type, backed by BigDecimal
    private Integer multiplier;
    private Integer contractMonths;

    // ...
}

我想要实现的是最初只显示commissionType枚举的下拉列表,然后根据所选值选择适当的子编辑器来编辑其余字段:

CommissionType.UNIT_RATE selected

CommissionType.STANDING_CHARGE selected

我以前使用AbstractSubTypeEditor实现了多个子类型编辑器(see question here),但这种情况略有不同,因为我没有编辑子类,它们都编辑相同的基本Commission类型,由于某种原因,同样的方法似乎不适用于编辑相同具体类型的多个编辑器。

我目前有两个子编辑(通过自定义界面Editor<Commission>实现IsWidgetIsCommissionEditorWidget),但它们本身有不同的子编辑器,Money可以是便士或磅,乘数可以代表天或月,以及其他变化。

CommissionEditor

我查看了this question中的类似问题并尝试创建CompositeEditor<Commission, Commission, Editor<Commission>>

这是我到目前为止所做的事情(请注意,通过实施LeafValueEditor<Commission>并手动调用setValue()和{{1,注释掉的部分是获取我想要的功能的黑客方式在getValue()amountmultiplier):

contractMonths

这是public class CommissionEditor extends Composite implements CompositeEditor<Commission, Commission, Editor<Commission>>, //LeafValueEditor<Commission> { interface Binder extends UiBinder<HTMLPanel, CommissionEditor> { } private static Binder uiBinder = GWT.create(Binder.class); @UiField CommissionTypeEditor commissionType; @UiField Panel subEditorPanel; private EditorChain<Commission, Editor<Commission>> chain; private IsCommissionEditorWidget subEditor = null; private Commission value = new Commission(); public CommissionEditor() { initWidget(uiBinder.createAndBindUi(this)); commissionType.box.addValueChangeHandler(event -> setValue(new Commission(event.getValue()))); } @Override public void setValue(Commission value) { Log.warn("CommissionEditor -> setValue(): value is " + value); chain.detach(subEditor); commissionType.setValue(value.getCommissionType()); if(value.getCommissionType() != null) { switch(value.getCommissionType()) { case UNIT_RATE: Log.info("UNIT_RATE"); subEditor = new UnitRateCommissionEditor(); break; case STANDING_CHARGE: Log.info("STANDING_CHARGE"); subEditor = new StandingChargeCommissionEditor(); break; case PER_MWH: Log.info("PER_MWH"); // TODO break; case SINGLE_PAYMENT: Log.info("SINGLE_PAYMENT"); // TODO break; } this.value = value; subEditorPanel.clear(); // subEditor.setValue(value); subEditorPanel.add(subEditor); chain.attach(value, subEditor); } } // @Override // public Commission getValue() // { // if(subEditor != null) // { // return subEditor.getValue(); // } // else // { // return value; // } // } @Override public void flush() { this.value = chain.getValue(subEditor); } @Override public void setEditorChain(EditorChain<Commission, Editor<Commission>> chain) { this.chain = chain; } @Override public Editor<Commission> createEditorForTraversal() { return subEditor; } @Override public String getPathElement(Editor<Commission> commissionEditor) { return ""; } @Override public void onPropertyChange(String... strings) { } @Override public void setDelegate(EditorDelegate<Commission> editorDelegate) { } } 接口,它定义了每个子编辑器的合同,也可以添加到面板中:

IsCommissionEditorWidget

UnitRateCommissionEditor

当用户选择public interface IsCommissionEditorWidget extends Editor<Commission>, IsWidget { } 时,我想将其添加到编辑器链中以应用于其余3个字段:

CommissionType.UNIT_RATE

StandingChargeCommissionEditor

选择public class UnitRateCommissionEditor extends Composite implements IsCommissionEditorWidget { interface Binder extends UiBinder<Widget, UnitRateCommissionEditor> { } private static Binder uiBinder = GWT.create(Binder.class); @UiField MoneyPenceBox amount; @UiField IntegerBox multiplier; @UiField IntegerBox contractMonths; public UnitRateCommissionEditor() { initWidget(uiBinder.createAndBindUi(this)); } // @Override // public void setValue(Commission commission) // { // amount.setValue(commission.getAmount()); // multiplier.setValue(commission.getMultiplier()); // contractMonths.setValue(commission.getContractMonths()); // } // // @Override // public Commission getValue() // { // return new Commission(CommissionType.UNIT_RATE, amount.getValue(), multiplier.getValue(), contractMonths.getValue()); // } } 时我想要这个(UiBinder也有点不同,但主要区别在于CommissionType.STANDING_CHARGE而不是MoneyPoundsBox):

MoneyPenceBox

目前,父类型的public class StandingChargeCommissionEditor extends Composite implements IsCommissionEditorWidget { interface Binder extends UiBinder<Widget, StandingChargeCommissionEditor> { } private static Binder uiBinder = GWT.create(Binder.class); @UiField MoneyPoundsBox amount; @UiField IntegerBox multiplier; @UiField IntegerBox contractMonths; public StandingChargeCommissionEditor() { initWidget(uiBinder.createAndBindUi(this)); } // @Override // public void setValue(Commission commission) // { // amount.setValue(commission.getAmount()); // multiplier.setValue(commission.getMultiplier()); // contractMonths.setValue(commission.getContractMonths()); // } // // @Override // public Commission getValue() // { // return new Commission(CommissionType.STANDING_CHARGE, amount.getValue(), multiplier.getValue(), contractMonths.getValue()); // } } (正在编辑的包含flush()的类型)会返回一个未定义Commissionamount和{的委员会{1}}。我可以将这些值传入和传出的唯一方法是手动编写代码(注释代码)。

  • 我的子编辑器是否正确附加到multiplier

编辑1:提议的解决方案

我决定创建一个新的中间类,它将分别包装每个子类型委托using AbstractSubTypeEditor like I did in this question

CommissionEditor

还是contractMonths,但它只有一个或零个子编辑,它们只会是EditorChain

CompositeEditor

AbstractSubTypeEditor

此课程的学分为Florent Bayle。我没想到我可以使用它而没有子类型是多态的,但似乎工作得很好。基本上允许包装子编辑器,如下面CommissionSubtypeEditor中所示。

public class CommissionEditor extends Composite implements CompositeEditor<Commission, Commission, CommissionSubtypeEditor>, LeafValueEditor<Commission>
{
    interface Binder extends UiBinder<HTMLPanel, CommissionEditor>
    {
    }

    private static Binder uiBinder = GWT.create(Binder.class);

    @UiField
    @Ignore
    CommissionTypeEditor commissionType;

    @UiField
    Panel subEditorPanel;

    private EditorChain<Commission, CommissionSubtypeEditor> chain;

    @Ignore
    @Path("")
    CommissionSubtypeEditor subEditor = new CommissionSubtypeEditor();

    private Commission value;

    public CommissionEditor()
    {
        initWidget(uiBinder.createAndBindUi(this));

        commissionType.box.addValueChangeHandler(event -> setValue(new Commission(event.getValue())));
    }

    @Override
    public void setValue(Commission value)
    {
        Log.warn("CommissionEditor -> setValue(): value is " + value);

        commissionType.setValue(value.getCommissionType());

        if(value.getCommissionType() != null)
        {
            this.value = value;
            subEditorPanel.clear();
            subEditorPanel.add(subEditor);
            chain.attach(value, subEditor);
        }

    }

    @Override
    public Commission getValue()
    {
        Log.info("CommissionEditor -> getValue: " + value);
        return value;
    }

    @Override
    public void flush()
    {
        chain.getValue(subEditor);
    }

    @Override
    public void setEditorChain(EditorChain<Commission, CommissionSubtypeEditor> chain)
    {
        this.chain = chain;
    }

    @Override
    public CommissionSubtypeEditor createEditorForTraversal()
    {
        return subEditor;
    }

    @Override
    public String getPathElement(CommissionSubtypeEditor commissionEditor)
    {
        return "";
    }

    @Override
    public void onPropertyChange(String... strings)
    {

    }

    @Override
    public void setDelegate(EditorDelegate<Commission> editorDelegate)
    {

    }

}

CommissionSubtypeEditor

CommissionSubtypeEditor

UnitRateCommissionEditor和StandingChargeCommissionEditor

简单和实施public abstract class AbstractSubTypeEditor<T, C extends T, E extends Editor<C>> implements CompositeEditor<T, C, E>, LeafValueEditor<T> { private EditorChain<C, E> chain; private T currentValue; private final E subEditor; public AbstractSubTypeEditor(E subEditor) { this.subEditor = subEditor; } @Override public E createEditorForTraversal() { return subEditor; } @Override public void flush() { currentValue = chain.getValue(subEditor); } @Override public String getPathElement(E subEditor) { return ""; } @Override public T getValue() { return currentValue; } @Override public void onPropertyChange(String... paths) { } @Override public void setDelegate(EditorDelegate<T> delegate) { } @Override public void setEditorChain(EditorChain<C, E> chain) { this.chain = chain; } public void setValue(T value, boolean instanceOf) { if(currentValue != null && value == null) { chain.detach(subEditor); } currentValue = value; if(value != null && instanceOf) { chain.attach((C) value, subEditor); } } }

public class CommissionSubtypeEditor extends Composite implements Editor<Commission>
{

    interface Binder extends UiBinder<HTMLPanel, CommissionSubtypeEditor>
    {
    }

    private static Binder uiBinder = GWT.create(Binder.class);

    @UiField
    Panel subEditorPanel;

    @Ignore
    final UnitRateCommissionEditor unitRateCommissionEditor = new UnitRateCommissionEditor();

    @Path("")
    final AbstractSubTypeEditor<Commission, Commission, UnitRateCommissionEditor> unitRateWrapper = new AbstractSubTypeEditor<Commission, Commission,
            UnitRateCommissionEditor>(unitRateCommissionEditor)
    {
        @Override
        public void setValue(Commission value)
        {
            if(value.getCommissionType() == CommissionType.UNIT_RATE)
            {
                Log.info("unitRateWrapper setValue");
                setValue(value, true);

                subEditorPanel.clear();
                subEditorPanel.add(unitRateCommissionEditor);

            }
        }
    };

    @Ignore
    final StandingChargeCommissionEditor standingChargeCommissionEditor = new StandingChargeCommissionEditor();

    @Path("")
    final AbstractSubTypeEditor<Commission, Commission, StandingChargeCommissionEditor> standingChargeWrapper = new AbstractSubTypeEditor<Commission,
            Commission, StandingChargeCommissionEditor>(standingChargeCommissionEditor)
    {
        @Override
        public void setValue(Commission value)
        {
            if(value.getCommissionType() == CommissionType.STANDING_CHARGE)
            {
                Log.info("standingChargeWrapper setValue");
                setValue(value, true);

                subEditorPanel.clear();
                subEditorPanel.add(standingChargeCommissionEditor);

            }
        }
    };

    public CommissionSubtypeEditor()
    {
        initWidget(uiBinder.createAndBindUi(this));
    }

}

差不多......

Editor<Commission>

这确实有效,类似于我在public class UnitRateCommissionEditor extends Composite implements Editor<Commission> { interface Binder extends UiBinder<Widget, UnitRateCommissionEditor> { } private static Binder uiBinder = GWT.create(Binder.class); @UiField MoneyPenceBox amount; @UiField IntegerBox multiplier; @UiField IntegerBox contractMonths; public UnitRateCommissionEditor() { initWidget(uiBinder.createAndBindUi(this)); } } 本身拥有public class StandingChargeCommissionEditor extends Composite implements Editor<Commission> { interface Binder extends UiBinder<Widget, StandingChargeCommissionEditor> { } private static Binder uiBinder = GWT.create(Binder.class); @UiField MoneyPoundsBox amount; @UiField IntegerBox multiplier; @UiField IntegerBox contractMonths; public StandingChargeCommissionEditor() { initWidget(uiBinder.createAndBindUi(this)); } } 时很早就尝试过的事情。我认为问题在于编辑无法自行调用AbstractSubtypeEditor。我是对的吗?

非常感谢评论,批评和建议。

1 个答案:

答案 0 :(得分:2)

您的复合编辑器声明为CompositeEditor<Commission, Commission, Editor<Commission>>,这意味着该链预计会提供一些任意Editor<Commission>。这是一个问题,因为编译器检查类型Editor<Commission>并且没有看到子编辑器,也没有看到其他编辑器接口(LeafValueEditorValueAwareEditor等),所以不对它进行任何绑定。

您提到您有两种不同的Editor<Commission>子类型 - 但代码生成器无法提前知道这一点,检查每个可能的子类型并构建任何可能的布线都没有意义这可能适合。无论好坏,编辑器框架都是静态的 - 在前面声明所有内容,然后生成器将只创建所需的内容。

我看到两个选项:

  • 创建两个不同的CompositeEditors,每个子编辑器类型一个,并确保您一次只附加一个,或
  • 从两个编辑器中提取一个公共超类型/接口,这些编辑器具有一致的命名和类型化方法,这些方法返回相同的编辑器类型(由于与上述相同的原因,永远不会简单地返回Editor<Foo>)。这可能会更难做到,但您可以使用ValueAwareEditor进行整理,并使用setValueflush()来详细说明。