使用具有复杂用例的GWT编辑器

Nic*_*kis 12 gwt requestfactory gwt-editors

我正在尝试创建一个与Google表单创建页面非常相似的页面.

在此输入图像描述

这就是我尝试使用GWT MVP框架(地点和活动)和编辑器对其进行建模的方式.

CreateFormActivity(活动和演示者)

CreateFormView(用于视图的界面,具有嵌套的Presenter界面)

CreateFormViewImpl(实现CreateFormView和Editor <FormProxy>

CreateFormViewImpl具有以下子编辑器:

  • TextBox标题
  • TextBox描述
  • QuestionListEditor questionList

QuestionListEditor实现IsEditor <ListEditor <QuestionProxy,QuestionEditor >>

QuestionEditor实现编辑器<QuestionProxy> QuestionEditor具有以下子编辑器:

  • TextBox questionTitle
  • TextBox helpText
  • ValueListBox questionType
  • 以下每种问题类型的可选子编辑器.

每种问题类型的编辑器:

TextQuestionEditor

ParagraphTextQuestionEditor

MultipleChoiceQuestionEditor

CheckboxesQuestionEditor

ListQuestionEditor

ScaleQuestionEditor

GridQuestionEditor


具体问题:

  1. 从表单添加/删除问题的正确方法是什么? (见后续问题)
  2. 我应该如何为每种问题类型创建编辑器?我试图听取questionType值的变化,我不知道该怎么办.(BobV回答)
  3. 每个特定于问题类型的编辑器是否应该使用optionalFieldEditor包装?由于一次只能使用其中一种.(BobV回答)
  4. 如何最好地管理在对象层次结构深处创建/删除对象.例)指定问题编号3的答案,该问题是多项选择题的类型.(见后续问题)
  5. 可以使用OptionalFieldEditor编辑器来包装ListEditor吗?(BobV回答)

基于答案的实施

问题编辑

public class QuestionDataEditor extends Composite implements
CompositeEditor<QuestionDataProxy, QuestionDataProxy, Editor<QuestionDataProxy>>,
LeafValueEditor<QuestionDataProxy>, HasRequestContext<QuestionDataProxy> {

interface Binder extends UiBinder<Widget, QuestionDataEditor> {}

private CompositeEditor.EditorChain<QuestionDataProxy, Editor<QuestionDataProxy>> chain;

private QuestionBaseDataEditor subEditor = null;
private QuestionDataProxy currentValue = null;
@UiField
SimplePanel container;

@UiField(provided = true)
@Path("dataType")
ValueListBox<QuestionType> dataType = new ValueListBox<QuestionType>(new Renderer<QuestionType>() {

    @Override
    public String render(final QuestionType object) {
        return object == null ? "" : object.toString();
    }

    @Override
    public void render(final QuestionType object, final Appendable appendable) throws IOException {
        if (object != null) {
            appendable.append(object.toString());
        }
    }
});

private RequestContext ctx;

public QuestionDataEditor() {
    initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
    dataType.setValue(QuestionType.BooleanQuestionType, true);
    dataType.setAcceptableValues(Arrays.asList(QuestionType.values()));

    /*
     * The type drop-down UI element is an implementation detail of the
     * CompositeEditor. When a question type is selected, the editor will
     * call EditorChain.attach() with an instance of a QuestionData subtype
     * and the type-specific sub-Editor.
     */
    dataType.addValueChangeHandler(new ValueChangeHandler<QuestionType>() {
        @Override
        public void onValueChange(final ValueChangeEvent<QuestionType> event) {
            QuestionDataProxy value;
            switch (event.getValue()) {

            case MultiChoiceQuestionData:
                value = ctx.create(QuestionMultiChoiceDataProxy.class);
                setValue(value);
                break;

            case BooleanQuestionData:
            default:
                final QuestionNumberDataProxy value2 = ctx.create(BooleanQuestionDataProxy.class);
                value2.setPrompt("this value doesn't show up");
                setValue(value2);
                break;

            }

        }
    });
}

/*
 * The only thing that calls createEditorForTraversal() is the PathCollector
 * which is used by RequestFactoryEditorDriver.getPaths().
 * 
 * My recommendation is to always return a trivial instance of your question
 * type editor and know that you may have to amend the value returned by
 * getPaths()
 */
@Override
public Editor<QuestionDataProxy> createEditorForTraversal() {
    return new QuestionNumberDataEditor();
}

@Override
public void flush() {
    //XXX this doesn't work, no data is returned
    currentValue = chain.getValue(subEditor);
}

/**
 * Returns an empty string because there is only ever one sub-editor used.
 */
@Override
public String getPathElement(final Editor<QuestionDataProxy> subEditor) {
    return "";
}

@Override
public QuestionDataProxy getValue() {
    return currentValue;
}

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

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

@Override
public void setEditorChain(final EditorChain<QuestionDataProxy, Editor<QuestionDataProxy>> chain) {
    this.chain = chain;
}

@Override
public void setRequestContext(final RequestContext ctx) {
    this.ctx = ctx;
}

/*
 * The implementation of CompositeEditor.setValue() just creates the
 * type-specific sub-Editor and calls EditorChain.attach().
 */
@Override
public void setValue(final QuestionDataProxy value) {

    // if (currentValue != null && value == null) {
    chain.detach(subEditor);
    // }

    QuestionType type = null;
    if (value instanceof QuestionMultiChoiceDataProxy) {
        if (((QuestionMultiChoiceDataProxy) value).getCustomList() == null) {
            ((QuestionMultiChoiceDataProxy) value).setCustomList(new ArrayList<CustomListItemProxy>());
        }
        type = QuestionType.CustomList;
        subEditor = new QuestionMultipleChoiceDataEditor();

    } else {
        type = QuestionType.BooleanQuestionType;
        subEditor = new BooleanQuestionDataEditor();
    }

    subEditor.setRequestContext(ctx);
    currentValue = value;
    container.clear();
    if (value != null) {
        dataType.setValue(type, false);
        container.add(subEditor);
        chain.attach(value, subEditor);
    }
}

}
Run Code Online (Sandbox Code Playgroud)

问题基础数据编辑器

public interface QuestionBaseDataEditor extends HasRequestContext<QuestionDataProxy>,                         IsWidget {


}
Run Code Online (Sandbox Code Playgroud)

示例子类型

public class BooleanQuestionDataEditor extends Composite implements QuestionBaseDataEditor {
interface Binder extends UiBinder<Widget, BooleanQuestionDataEditor> {}

@Path("prompt")
@UiField
TextBox prompt = new TextBox();

public QuestionNumberDataEditor() {
    initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
}

@Override
public void setRequestContext(final RequestContext ctx) {

}
}
Run Code Online (Sandbox Code Playgroud)

剩下的唯一问题是没有显示或刷新QuestionData子类型特定数据.我认为这与我正在使用的编辑器设置有关.

例如,BooleanQuestionDataEditor既不设置也不刷新的提示值,并且在rpc有效负载中为空.

我的猜测是:由于QuestionDataEditor实现了LeafValueEditor,因此驱动程序不会访问子编辑器,即使它已被附加.

非常感谢任何可以提供帮助的人!

Bob*_*obV 9

从根本上说,您希望CompositeEditor处理从编辑器层次结构中动态添加或删除对象的情况.在ListEditorOptionalFieldEditor适配器实现CompositeEditor.

如果不同类型问题所需的信息基本上是正交的,那么OptionalFieldEditor可以将多个字段用于不同的字段,每个字段对应一种问题类型.当您只有几个问题类型时,这将起作用,但将来不会很好地扩展.

一种可以更好地扩展的不同方法是使用CompositeEditor + LeafValueEditor处理多态QuestionData类型层次结构的自定义实现.类型下拉UI元素将成为该实现细节CompositeEditor.选择问题类型后,编辑器将调用子类型EditorChain.attach()的实例QuestionData和特定于类型的子编辑器.QuestionData应保留新创建的实例以实现LeafValueEditor.getValue().实现CompositeEditor.setValue()只是创建特定于类型的子编辑器和调用EditorChain.attach().

FWIW OptionalFieldEditor可以与ListEditor任何其他编辑器类型一起使用.