Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I have found a solution, but this works only because my application is not a standard Roo application any more. Anyway, I will explain my solution, maybe someone find an way to adapt it for standard Roo applications.</p> <p>The idea is to add an empty selection in the dropdown box when the <code>required</code> attribute is <code>false</code>. The main problem is that the dijti/dojo extension will not work correct if there is one option in the dropdown box without an <code>value</code>. So my solution was to give them for example the <code>value</code> <code>"null"</code> (<code>&lt;option value="null&gt;&lt;/option&gt;</code>). On server side one must change the converter that convert the database id (that is the normal value) to an Entity (by loading it from the database) a bit, so that it converts the String <code>"null"</code> to <code>null</code> instead of an entity.</p> <p>But that is the problem with spring Roo. Roo uses the <code>org.springframework.core.convert.support.IdToEntityConverter</code> that is automatically registered (not documented <a href="https://jira.springsource.org/browse/SPR-7461" rel="nofollow">https://jira.springsource.org/browse/SPR-7461</a>) and will try to convert every object to an entity if the entity class as a static finder method. I have found no way to modify its behaviour.</p> <p>But I personally have a lot of luck, because some time ago I changed my application that it does not have that static finder, so I have my own generic Id to entity converter that is easy to change. The converter converts String to Entity. If the String is "null" it returns null, else it converts the String to a number and load the Entity by this number/id. </p> <p>For the view, it seams that one have to extend the <code>select.tagx</code> file.</p> <p>The <code>select.tagx</code> file contains 12 different ways to fill the select box. </p> <ul> <li>6 of them are for multiple select, so they can stay like they are.</li> <li>2 of if the not multiple ones are for disabled form binding, there one must add this block right after the select tag</li> </ul> <p>line 75, 130,</p> <pre><code>&lt;c:if test="${not required}"&gt; &lt;option value="null"&gt;&lt;/option&gt; &lt;/c:if&gt; </code></pre> <ul> <li>the other 4 are a bit more complicated</li> </ul> <p>... </p> <pre><code>&lt;form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" /&gt; ... &lt;form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemLabel="${sec_itemLabel}"/&gt; ... &lt;form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemValue="${fn:escapeXml(itemValue)}" /&gt; ... &lt;form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/&gt; </code></pre> <p>They one need to replace the complete tag by (<em>I will only demonstrate it for the last of that 4, but the other are similar, except one has to remove the itemVlaue and or itemLabel parameter</em>)</p> <pre><code>&lt;form:select id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}"&gt; &lt;c:if test="${not required}"&gt; &lt;option value="null"&gt;&lt;/option&gt; &lt;/c:if&gt; &lt;form:options items="${items}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/&gt; &lt;/form:select&gt; </code></pre> <p>Now it should work.</p> <hr> <p>But it has a small flaw. If there is a Book with no Publisher, then the empty dropdown option will not have the select attribute. This is not so bad, because it is the top most option and will be displayed if no other option is selected.</p> <p>If someone can not accept this flaw, then one way to handle this problem is to write an own jsp tag extending <code>org.springframework.web.servlet.tags.form.Option</code> (the class that does the spring option tag). There are only two things that one really need to change:</p> <p>1) the method <code>isSelected(Object resolvedValue)</code> must return true if the bind status is null (so this method becomes really easy)</p> <pre><code>private boolean isSelected(Object resolvedValue) { BindStatus bindStatus = getBindStatus(); return bindStatus == null || bindStatus.getValue() == null || bindStatus.getActualValue() == null; } </code></pre> <p>2) if the tag is rendered without or empty body (method <code>renderDefaultContent</code>) the content of the rendered html <code>option</code> should be empty but not the <code>value</code>. So the second parameter of the renderOption(SpecialWay) method must be set fix to an empty string.</p> <pre><code>@Override protected void renderDefaultContent(TagWriter tagWriter) throws JspException { Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME); renderOptionSpecialWay(value, "", tagWriter); } </code></pre> <p>But because the <code>isSelected</code> method is private and can not be override, one must copy the <code>renderOption</code> (can rename it) and must change it so that it invokes the "new" isSelected method. The same must be done to the two methods <code>renderDefaultContent</code> and <code>renderFromBodyContent</code> because <code>renderOption</code> is private too.</p> <p>So one came up with this class:</p> <pre><code>public class NullOptionTag extends OptionTag { @Override protected void renderDefaultContent(TagWriter tagWriter) throws JspException { Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME); renderOptionSpecialWay(value, "", tagWriter); } @Override protected void renderFromBodyContent(BodyContent bodyContent, TagWriter tagWriter) throws JspException { Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME); String label = bodyContent.getString(); renderOptionSpecialWay(value, label, tagWriter); } private void renderOptionSpecialWay(Object value, String label, TagWriter tagWriter) throws JspException { tagWriter.startTag("option"); writeOptionalAttribute(tagWriter, "id", resolveId()); writeOptionalAttributes(tagWriter); String renderedValue = getDisplayString(value, getBindStatus().getEditor()); tagWriter.writeAttribute(OptionTag.VALUE_VARIABLE_NAME, renderedValue); if (isSelected(value)) { tagWriter.writeAttribute("selected", "selected"); } if (isDisabled()) { tagWriter.writeAttribute("disabled", "disabled"); } tagWriter.appendValue(label); tagWriter.endTag(); } private boolean isSelected(Object resolvedValue) { BindStatus bindStatus = getBindStatus(); return bindStatus == null || bindStatus.getValue() == null || bindStatus.getActualValue() == null; } } </code></pre> <p>Next thing to do is add this class to an tag lib definition so that it can be used in the <code>select.tagx</code></p> <pre><code> &lt;form:select id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}"&gt; &lt;c:if test="${not required}"&gt; &lt;formExtension:nulloption value="null"&gt;&lt;/formExtension:nulloption&gt; &lt;/c:if&gt; &lt;form:options items="${items}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/&gt; &lt;/form:select&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      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