Note that there are some explanatory texts on larger screens.

plurals
  1. POUsing GWT Editors with a complex usecase
    primarykey
    data
    text
    <p>I'm trying to create a page which is very similar to the Google Form creation page.</p> <p><img src="https://i.stack.imgur.com/17cy5.png" alt="enter image description here"></p> <p>This is how I am attempting to model it using the GWT MVP framework (Places and Activities), and Editors.</p> <p><strong>CreateFormActivity</strong> (Activity and presenter)</p> <p><strong>CreateFormView</strong> (interface for view, with nested Presenter interface)</p> <p><strong>CreateFormViewImpl</strong> (implements CreateFormView and Editor&lt; FormProxy ></p> <p>CreateFormViewImpl has the following sub-editors:</p> <ul> <li>TextBox title</li> <li>TextBox description</li> <li>QuestionListEditor questionList</li> </ul> <p><strong>QuestionListEditor</strong> implements IsEditor&lt; ListEditor&lt; QuestionProxy, QuestionEditor>></p> <p><strong>QuestionEditor</strong> implements Editor &lt; QuestionProxy> QuestionEditor has the following sub-editors:</p> <ul> <li>TextBox questionTitle</li> <li>TextBox helpText</li> <li>ValueListBox questionType</li> <li><strong>An optional subeditor for each question type below.</strong></li> </ul> <p>An editor for each question type:</p> <p><strong>TextQuestionEditor</strong></p> <p><strong>ParagraphTextQuestionEditor</strong></p> <p><strong>MultipleChoiceQuestionEditor</strong></p> <p><strong>CheckboxesQuestionEditor</strong></p> <p><strong>ListQuestionEditor</strong></p> <p><strong>ScaleQuestionEditor</strong></p> <p><strong>GridQuestionEditor</strong></p> <hr> <h2>Specific Questions:</h2> <ol> <li>What is the correct way to add / remove questions from the form. <em>(see <a href="https://stackoverflow.com/questions/7095567/saving-collections-and-polymorphic-types-with-requestfactory-and-editors-null-v">follow up question</a>)</em></li> <li>How should I go about creating the Editor for each question type? I attempted to listen to the questionType value changes, I'm not sure what to do after. <em>(answered by BobV)</em></li> <li>Should each question-type-specific editor be wrapper with an optionalFieldEditor? Since only one of can be used at a time. <em>(answered by BobV)</em></li> <li>How to best manage creating/removing objects deep in the object hierarchy. Ex) Specifying answers for a question number 3 which is of type multiple choice question. <em>(see <a href="https://stackoverflow.com/questions/7095567/saving-collections-and-polymorphic-types-with-requestfactory-and-editors-null-v">follow up question</a>)</em></li> <li>Can OptionalFieldEditor editor be used to wrap a ListEditor? <em>(answered by BobV)</em></li> </ol> <hr> <h2>Implementation based on Answer</h2> <p>The Question Editor</p> <pre><code>public class QuestionDataEditor extends Composite implements CompositeEditor&lt;QuestionDataProxy, QuestionDataProxy, Editor&lt;QuestionDataProxy&gt;&gt;, LeafValueEditor&lt;QuestionDataProxy&gt;, HasRequestContext&lt;QuestionDataProxy&gt; { interface Binder extends UiBinder&lt;Widget, QuestionDataEditor&gt; {} private CompositeEditor.EditorChain&lt;QuestionDataProxy, Editor&lt;QuestionDataProxy&gt;&gt; chain; private QuestionBaseDataEditor subEditor = null; private QuestionDataProxy currentValue = null; @UiField SimplePanel container; @UiField(provided = true) @Path("dataType") ValueListBox&lt;QuestionType&gt; dataType = new ValueListBox&lt;QuestionType&gt;(new Renderer&lt;QuestionType&gt;() { @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.&lt;Binder&gt; 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&lt;QuestionType&gt;() { @Override public void onValueChange(final ValueChangeEvent&lt;QuestionType&gt; 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&lt;QuestionDataProxy&gt; 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&lt;QuestionDataProxy&gt; subEditor) { return ""; } @Override public QuestionDataProxy getValue() { return currentValue; } @Override public void onPropertyChange(final String... paths) { } @Override public void setDelegate(final EditorDelegate&lt;QuestionDataProxy&gt; delegate) { } @Override public void setEditorChain(final EditorChain&lt;QuestionDataProxy, Editor&lt;QuestionDataProxy&gt;&gt; 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 &amp;&amp; value == null) { chain.detach(subEditor); // } QuestionType type = null; if (value instanceof QuestionMultiChoiceDataProxy) { if (((QuestionMultiChoiceDataProxy) value).getCustomList() == null) { ((QuestionMultiChoiceDataProxy) value).setCustomList(new ArrayList&lt;CustomListItemProxy&gt;()); } 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); } } } </code></pre> <p>Question Base Data Editor</p> <pre><code>public interface QuestionBaseDataEditor extends HasRequestContext&lt;QuestionDataProxy&gt;, IsWidget { } </code></pre> <p>Example Subtype</p> <pre><code>public class BooleanQuestionDataEditor extends Composite implements QuestionBaseDataEditor { interface Binder extends UiBinder&lt;Widget, BooleanQuestionDataEditor&gt; {} @Path("prompt") @UiField TextBox prompt = new TextBox(); public QuestionNumberDataEditor() { initWidget(GWT.&lt;Binder&gt; create(Binder.class).createAndBindUi(this)); } @Override public void setRequestContext(final RequestContext ctx) { } } </code></pre> <p>The only issue left is that QuestionData subtype specific data isn't being displayed, or flushed. I think it has to do with the Editor setup I'm using. </p> <p>For example, The value for prompt in the <code>BooleanQuestionDataEditor</code> is neither set nor flushed, and is null in the rpc payload. </p> <p>My guess is: Since the QuestionDataEditor implements LeafValueEditor, the driver will not visit the subeditor, even though it has been attached.</p> <p><strong>Big thanks</strong> to anyone who can help!!!</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload