Я пытаюсь создать страницу, очень похожую на страницу создания формы Google.
Вот как я пытаюсь смоделировать это, используя структуру GWT MVP (места и действия) и редакторы.
CreateFormActivity (активность и ведущий)
CreateFormView (интерфейс для просмотра с вложенным интерфейсом Presenter)
CreateFormViewImpl (реализует CreateFormView и Editor‹ FormProxy >
CreateFormViewImpl имеет следующие подредакторы:
- Заголовок текстового поля
- Описание текстового поля
- ВопросСписокРедактор вопросовСписок
QuestionListEditor реализует IsEditor‹ ListEditor‹ QuestionProxy, QuestionEditor>>
QuestionEditor реализует Editor ‹ QuestionProxy> QuestionEditor имеет следующие подредакторы:
- TextBox вопросЗаголовок
- TextBox helpText
- ValueListBox тип вопроса
- Необязательный вспомогательный редактор для каждого типа вопроса ниже.
Редактор для каждого типа вопросов:
Редактор текстовых вопросов
Редактор вопросов абзаца и текста
Редактор вопросов с множественным выбором
Редактор вопросов с флажками
Редактор списка вопросов
Масштабный редактор вопросов
Редактор вопросов сетки
Конкретные вопросы:
- Как правильно добавлять/удалять вопросы из формы. (см. дополнительный вопрос а>)
- Как мне создать редактор для каждого типа вопроса? Я попытался прослушать изменения значения questionType, я не уверен, что делать дальше. (ответил BobV)
- Должен ли каждый редактор, специфичный для типа вопроса, быть оболочкой с необязательным полем редактирования? Так как одновременно можно использовать только один из. (ответил BobV)
- Как лучше управлять созданием/удалением объектов глубоко в иерархии объектов. Пример) Уточнение ответов на вопрос номер 3, который относится к типу вопросов с несколькими вариантами ответов. (см. дополнительный вопрос а>)
- Можно ли использовать редактор 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);
}
}
}
Редактор базы данных вопросов
public interface QuestionBaseDataEditor extends HasRequestContext<QuestionDataProxy>, IsWidget {
}
Пример подтипа
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) {
}
}
Единственная оставшаяся проблема заключается в том, что конкретные данные подтипа QuestionData не отображаются или не сбрасываются. Я думаю, что это связано с настройкой редактора, который я использую.
Например, значение для приглашения в BooleanQuestionDataEditor
не задано и не сбрасывается, а в полезной нагрузке rpc равно null.
Мое предположение таково: поскольку QuestionDataEditor реализует LeafValueEditor, драйвер не будет посещать подредактор, даже если он подключен.
Большое спасибо всем, кто может помочь!!!