Permalink
Browse files

Checkpoint within the change to use a Modalbox-hosted form to add new…

… values to a MultipleSelect
  • Loading branch information...
1 parent cd65a3a commit 56c3d23e7ef889e195d8da17263faf390c475cc7 @hlship committed Mar 25, 2011
@@ -19,19 +19,27 @@
import java.util.Set;
import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.Block;
import org.apache.tapestry5.ComponentAction;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Field;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
+import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.SupportsInformalParameters;
+import org.apache.tapestry5.beaneditor.BeanModel;
import org.apache.tapestry5.corelib.components.Palette;
+import org.apache.tapestry5.corelib.components.Zone;
import org.apache.tapestry5.func.F;
import org.apache.tapestry5.func.Flow;
import org.apache.tapestry5.func.Mapper;
+import org.apache.tapestry5.internal.bindings.AbstractBinding;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONArray;
import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.services.BeanModelSource;
import org.apache.tapestry5.services.ComponentDefaultProvider;
import org.apache.tapestry5.services.FormSupport;
import org.apache.tapestry5.services.Request;
@@ -41,10 +49,15 @@
/**
* Both a simplification of the Tapestry {@link Palette} component, and an extension in that it supports
- * adding new values on the fly on the client side.
+ * adding new values on the fly, using a form located inside a Modalbox modal dialog. Specically
+ * limited to editing a <em>Set</em> of values: element order is immaterial, and the UI keeps
+ * the values sorted in alphabetical order by {@linkplain MultipleSelectModel#toLabel(Object) label}.
+ * <p>
+ * TODO: Rename this to SetEditor and delete the current SetEditor.
*/
@Import(stack = "tapx-core")
@SuppressWarnings("rawtypes")
+@SupportsInformalParameters
public class MultipleSelect implements Field
{
/**
@@ -59,8 +72,19 @@
private MultipleSelectModel model;
/**
+ * Used when creating a form to allow a new value to be created. If not supplied, a default model
+ * is generated from the {@linkplain BeanModelSource#createEditModel(Class, org.apache.tapestry5.ioc.Messages) bean
+ * model source} using the class of the {@linkplain MultipleSelectModel#createEmptyInstance() new instance}.
+ */
+ @Property
+ @Parameter(allowNull = false)
+ private BeanModel beanModel;
+
+ /**
* Additional CSS class(es), beyond the mandatory default of "tx-multiselect".
+ * The CSS class(es) will also be used by the form used to gather data for a new field.
*/
+ @Property
@Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL)
private String className;
@@ -89,6 +113,39 @@
private String clientId, controlName;
+ @Property
+ private Object newValue;
+
+ @InjectComponent
+ private Zone newValueEditor;
+
+ @Inject
+ private Block editor;
+
+ @Inject
+ private BeanModelSource beanModelSource;
+
+ Object defaultBeanModel()
+ {
+ return new AbstractBinding()
+ {
+ @Override
+ public boolean isInvariant()
+ {
+ return false;
+ }
+
+ @Override
+ public Object get()
+ {
+ if (newValue == null)
+ return null;
+
+ return beanModelSource.createEditModel(newValue.getClass(), resources.getContainerMessages());
+ }
+ };
+ }
+
private static class Pair implements Comparable<Pair>
{
final String label;
@@ -189,7 +246,10 @@ void setupRender()
formSupport.store(this, new ProcessSubmission(controlName));
- JSONObject spec = new JSONObject("clientId", clientId);
+ JSONObject spec = new JSONObject("clientId", clientId,
+
+ "newValueURL", createEventURL("newValue"));
+
for (Object value : values)
{
String clientValue = model.toClient(value);
@@ -207,6 +267,11 @@ void setupRender()
jss.addInitializerCall("tapxMultipleSelect", spec);
}
+ private String createEventURL(String eventName)
+ {
+ return resources.createEventLink(eventName, clientId).toURI();
+ }
+
@SuppressWarnings("unchecked")
protected void processSubmission(String name)
{
@@ -218,7 +283,7 @@ protected void processSubmission(String name)
// First the values that have a server-side id.
- for (String clientValue : toStrings(selected, 0))
+ for (String clientValue : toStrings(selected))
{
Object serverValue = model.toValue(clientValue);
@@ -228,24 +293,10 @@ protected void processSubmission(String name)
values.add(serverValue);
}
-
- for (String label : toStrings(selected, 1))
- {
- Object serverValue = model.createValue(label);
-
- if (serverValue == null)
- throw new RuntimeException(String.format("Model returned null when creating value for label '%s'.",
- label));
-
- values.add(serverValue);
- }
-
}
- private List<String> toStrings(JSONArray selected, int index)
+ private List<String> toStrings(JSONArray values)
{
- JSONArray values = selected.getJSONArray(index);
-
List<String> result = new ArrayList<String>(values.length());
for (int i = 0; i < values.length(); i++)
@@ -255,4 +306,32 @@ protected void processSubmission(String name)
return result;
}
+
+ Object onNewValue(String clientId) throws Exception
+ {
+ // Thread.sleep(5000);
+
+ this.clientId = clientId;
+
+ return editor;
+ }
+
+ void onPrepareForSubmitFromNewValue(String clientId)
+ {
+ this.clientId = clientId;
+ }
+
+ void onPrepareFromNewValue()
+ {
+ newValue = model.createEmptyInstance();
+ }
+
+ void onSuccessFromNewValue()
+ {
+ }
+
+ Object onFailureFromNewValue()
+ {
+ return newValueEditor.getBody();
+ }
}
@@ -16,7 +16,8 @@
public interface MultipleSelectModel<T> extends ValueEncoder<T>
{
/**
- * Returns the values that may be selected by the client, in presentation order (typically, alphabetical order).
+ * Returns the values that may be selected by the client. These will be presented to the user
+ * in sorted order by {@linkplain #toLabel(Object) label}.
*/
Set<T> getAvailableValues();
@@ -27,12 +28,12 @@
*/
String toLabel(T value);
+ /** Creates a new, empty instance of the object ready to have its properties filled in as necessary. */
+ T createEmptyInstance();
+
/**
- * Used in the case where the client adds a new value on the client side; as part of the form submission,
- * a new instance of the value type will be created from the label (as entered on the client side).
- *
- * @param label
- * @return
+ * Once the values of the instance are filled in, the instance is then persisted. After persisting the value,
+ * its id and {@linkplain #toLabel(Object) label} will be provided to the client side.
*/
- T createValue(String label);
+ void persistNewInstance(T newInstance);
}
@@ -15,10 +15,33 @@
</div>
</div>
<div class="tx-input">
- <label>
- Add:
- <input type="text" size="40"/>
- <span class="tx-error"/>
- </label>
+ <button>Add New ${label}</button>
</div>
+
+ <t:block id="editor">
+
+ <t:zone t:id="newValueEditor">
+ <t:form class="${className}" zone="^" t:id="newValue" context="clientId">
+
+ <t:errors/>
+
+ <t:remove>
+ Intentionally omitting the
+ <div class="t-beaneditor-row">
+ </div>
+ around t:beaneditor; the Tapestry default colors
+ are a distraction inside the Modalbox. However,
+ t-beaneditor-row lays out the rows nicely.
+ </t:remove>
+
+ <t:beaneditor object="newValue" model="beanModel" overrides="componentResources"/>
+ <div class="t-beaneditor-row">
+
+ <input type="submit" value="${message:submit-label}"/>
+ </div>
+ </t:form>
+ </t:zone>
+
+ </t:block>
+
</div>
@@ -176,15 +176,6 @@ DIV.tx-multiselect .tx-input {
padding-top: 5px;
}
-DIV.tx-multiselect .tx-error {
- clear: left;
- display: inline-block;
- font-weight: bold;
- font-color: red;
- padding: 2px 18px 2px 2px;
- margin-right: 16px;
- background-color: ead38c;
- background-image: url(silk/cancel.png);
- background-repeat: no-repeat;
- background-position: right center;
+DIV.t-error-popup {
+ z-index: 20000;
}
Oops, something went wrong.

0 comments on commit 56c3d23

Please sign in to comment.