簡體   English   中英

如何在 Spring Roo/Dojo 中做一個非強制性的下拉框?

[英]How to do a not mandatory Drop Down Box in Spring Roo/Dojo?

在 Spring Roo (1.1.5) 中,我有一個實體“Book”,它可以引用實體“Publisher”。

class Book {
   @ManyToOne(optional=true)
   Publisher publisher
}

現在我有了 Roo 生成的 Controller 和 JSPX 文件。 在用於創建和更新 Book 的 GUI 中有 Roo 生成的下拉框(由dijit.form.FilteringSelect裝飾)到 select 發布者。 但是用戶必須 select 是發布者; 沒有“空”字段!

我的第一次嘗試只是簡單地將null值添加到代表下拉框選項的列表中。 但那失敗了。 java.lang.IllegalArgumentException: Bean object must not be null )——所以這可能是錯誤的方法。

所以在我嘗試自己擴展select.tagx文件之前,我想問一下是否有人已經解決了這個問題(有一個帶有 Spring Roo/Dojo 的可選下拉框),或者我完全錯了,它應該可以工作沒有實施新事物的正常情況?

我找到了一個解決方案,但這只是因為我的應用程序不再是標准的 Roo 應用程序。 無論如何,我會解釋我的解決方案,也許有人會找到一種方法來適應標准 Roo 應用程序。

這個想法是當required屬性為false時,在下拉框中添加一個空選擇。 主要問題是如果下拉框中有一個選項沒有value ,則 dijti/dojo 擴展將無法正常工作。 所以我的解決方案是給他們例如value "null"<option value="null></option> )。在服務器端,必須更改將數據庫 id(即正常值)轉換為實體(通過從數據庫加載)一點,以便將字符串"null"轉換為null而不是實體。

但這就是 spring Roo 的問題。 Roo 使用自動注冊的org.springframework.core.convert.support.IdToEntityConverter (未記錄https://jira.springsource.org/browse/SPR-7461 )並將嘗試將每個 object 轉換為實體,如果該實體class 作為 static 查找器方法。 我發現沒有辦法修改它的行為。

但我個人有很多運氣,因為前段時間我更改了我的應用程序,它沒有那個 static finder,所以我有自己的通用 Id 到實體轉換器,很容易更改。 轉換器將字符串轉換為實體。 如果字符串為“null”,則返回 null,否則將字符串轉換為數字並按此數字/ID 加載實體。

對於視圖,似乎必須擴展select.tagx文件。

select.tagx文件包含 12 種不同的方式來填充 select 框。

  • 其中 6 個用於多個 select,因此它們可以保持原樣。
  • 如果不是多個用於禁用表單綁定的 2 個,則必須在 select 標記之后添加此塊

第 75、130 行,

<c:if test="${not required}">
    <option value="null"></option>
</c:if>
  • 其他4個有點復雜

...

<form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" />
 ...
 <form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemLabel="${sec_itemLabel}"/>
 ...
<form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemValue="${fn:escapeXml(itemValue)}" />
 ...
<form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/>

他們需要將完整的標簽替換為(我只會在這 4 個中的最后一個進行演示,但另一個類似,除了必須刪除 itemVlaue 和/或 itemLabel 參數

<form:select id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}">                          
    <c:if test="${not required}">                               
       <option value="null"></option>
    </c:if>
    <form:options items="${items}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/>
</form:select>

現在它應該可以工作了。


但它有一個小缺陷。 如果有一本書沒有出版商,那么空下拉選項將沒有 select 屬性。 這還不錯,因為它是最頂層的選項,如果沒有選擇其他選項,就會顯示出來。

If someone can not accept this flaw, then one way to handle this problem is to write an own jsp tag extending org.springframework.web.servlet.tags.form.Option (the class that does the spring option tag). 真正需要改變的只有兩件事:

1)如果綁定狀態是null,方法isSelected(Object resolvedValue)必須返回true(所以這個方法變得非常簡單)

private boolean isSelected(Object resolvedValue) {
  BindStatus bindStatus = getBindStatus();  
  return bindStatus == null || bindStatus.getValue() == null || bindStatus.getActualValue() == null;
}

2) 如果標簽在沒有或空主體的情況下呈現(方法renderDefaultContent ),則呈現的 html option的內容應該是空的,而不是value 所以 renderOption(SpecialWay) 方法的第二個參數必須設置為空字符串。

@Override
protected void renderDefaultContent(TagWriter tagWriter) throws JspException {
  Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME);    
  renderOptionSpecialWay(value, "", tagWriter);
}

但是因為isSelected方法是私有的並且不能被覆蓋,所以必須復制renderOption (可以重命名它)並且必須更改它,以便它調用“新的”isSelected 方法。 必須對兩個方法renderDefaultContentrenderFromBodyContent做同樣的事情,因為renderOption也是私有的。

於是有人想出了這個 class:

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;
  }
}

接下來要做的是將此 class 添加到標簽庫定義中,以便可以在select.tagx中使用

 <form:select id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}">                         
   <c:if test="${not required}">                                
     <formExtension:nulloption value="null"></formExtension:nulloption>
   </c:if>
   <form:options items="${items}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/>
</form:select>

還有另一種解決這個問題的方法。 我的解決方案不需要更改select.tagx文件。

我對兩個地方進行了更改。

首先,在BookController中,我為這個特定的 object,例如Publisher覆蓋了填充方法。

@ModelAttribute("publisher")
public Collection<Publisher> populatePublisher() {
    Collection<Publisher> result = new ArrayList<Publisher>();

    // Add an empty item
    Publishertmp = new Publisher();
    tmp.setId(0L);
    result.add(tmp);
    result.addAll(Publisher.findAllPublisher());

    return result ;
}

將Id設置為0很重要。如果Id不為0,則不會顯示第一個空項目。

然后我覆蓋了ApplicationConversionServiceFactoryBean

protected void installFormatters(FormatterRegistry registry) {

    registry.addConverter(new PublisherConverter());

    super.installFormatters(registry);
    // Register application converters and formatters 
}

static class PublisherConverter implements Converter<Publisher, String> {
    public String convert(Publisher publisher) {
        if (publisher.getId().intValue() == 0) {
            return "-- Not Selected --";
        }
        return new StringBuilder().append(publisher.toString());
    }
}

通過更改轉換器,您可以在第一項上看到-- Not Selected -- 當表單提交到 controller 時,您只需添加代碼以識別是否選擇了空發布者並根據您的邏輯進行調整。 我相信就是這樣。

您可以根據required = "true" ,它仍然可以工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM