2011-08-12 66 views
4

在Spring Roo(1.1.5)中,我有一個Entity「Book」,它可以引用實體「Publisher」。如何在Spring Roo/Dojo中執行非強制性下拉框?

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

現在我有Roo生成的Controller和JSPX文件。 在用於創建和更新書籍的GUI中,Roo生成的下拉框(由dijit.form.FilteringSelect裝飾)來選擇發佈者。 但用戶必須選擇一個發佈者;沒有「空」字段!

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

所以之前我試着通過我自己的select.tagx文件擴展,我想問如果有人已經解決了這個問題(具有的Spring Roo /道場一個可選擇的跌落downbox),還是我的東西完全錯誤和它應該在正常情況下工作,而不是提出新的東西?

+0

「@ManyToOne(optinal = true)」對不對?它應該是「可選」的權利? ;) – bhagyas

+0

@bhagyas你是對的這是一個錯字,我糾正了這個問題 – Ralph

回答

3

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

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

但這是春天Roo的問題。 Roo使用自動註冊爲 (未記錄https://jira.springsource.org/browse/SPR-7461)的org.springframework.core.convert.support.IdToEntityConverter,並嘗試將每個對象轉換爲實體(如果實體類爲靜態查找方法)。 我找不到修改其行爲的方法。

但我個人有很多運氣,因爲前段時間我改變了我的應用程序,它沒有那個靜態查找器,所以我有我自己的通用Id到實體轉換器,很容易改變。 轉換器將字符串轉換爲實體。如果字符串爲「null」,則返回null,否則將字符串轉換爲數字並通過此數字/ ID加載實體。

對於視圖,它接縫,必須擴展select.tagx文件。

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

  • 其中6人是多重選擇,所以他們可以保持他們的身份。
  • 2的,如果不是多者爲殘疾形式結合,有一個必須選擇標籤

線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> 

現在它應該工作。


但它有一個小缺陷。如果有沒有發佈者的圖書,則空白下拉選項將不具有選擇屬性。 這並不壞,因爲它是最上面的選項,如果沒有選擇其他選項,它將顯示。

如果有人不能接受這個缺陷,那麼解決這個問題的一種方法是編寫一個自己的jsp標籤,擴展org.springframework.web.servlet.tags.form.Option(做spring選項標籤的類)。 只有兩件事情,一個真正需要改變:

1)的方法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也是私有的。

所以一個想出了這個類:

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

接下來要做的就是這一類添加到一個標記庫的定義,以便它可以在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> 
+0

偉大的答案,很好的細節和一個有趣的研究。我希望這會幫助很多正在進行Roo開發的人。請考慮將您的建議修正和代碼提供給Spring Roo源代碼。在Roo問題跟蹤器中針對此問題提出錯誤,以便在將來的版本中使用您的建議代碼解決這個問題,供所有人使用。 :)乾杯! – bhagyas

1

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

我對兩個地方做了修改。

首先,在BookController處,我已覆蓋此特定對象的填充方法,例如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 --的第一個項目。 將表單提交給控制器時,您只需添加代碼以確定是否選擇了空的發佈者並根據您的邏輯進行調整。我相信就是這樣。

您可以將發佈者下拉框保留爲required = "true",它仍然可以使用。