2013-06-11 74 views
23

我正從數據庫填充<p:selectOneMenu/>,如下所示。使用「請選擇」f:selectItem在p:selectOneMenu中使用null /空值

<p:selectOneMenu id="cmbCountry" 
       value="#{bean.country}" 
       required="true" 
       converter="#{countryConverter}"> 

    <f:selectItem itemLabel="Select" itemValue="#{null}"/> 

    <f:selectItems var="country" 
        value="#{bean.countries}" 
        itemLabel="#{country.countryName}" 
        itemValue="#{country}"/> 

    <p:ajax update="anotherMenu" listener=/> 
</p:selectOneMenu> 

<p:message for="cmbCountry"/> 

默認選擇的選項,當該頁面被加載是,

<f:selectItem itemLabel="Select" itemValue="#{null}"/> 

變換器:

@ManagedBean 
@ApplicationScoped 
public final class CountryConverter implements Converter { 

    @EJB 
    private final Service service = null; 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 
     try { 
      //Returns the item label of <f:selectItem> 
      System.out.println("value = " + value); 

      if (!StringUtils.isNotBlank(value)) { 
       return null; 
      } // Makes no difference, if removed. 

      long parsedValue = Long.parseLong(value); 

      if (parsedValue <= 0) { 
       throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Message")); 
      } 

      Country entity = service.findCountryById(parsedValue); 

      if (entity == null) { 
       throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "", "Message")); 
      } 

      return entity; 
     } catch (NumberFormatException e) { 
      throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Message"), e); 
     } 
    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 
     return value instanceof Country ? ((Country) value).getCountryId().toString() : null; 
    } 
} 

當選擇從由<f:selectItem>表示的菜單中的第一項和所述然後提交表格,getAsObject()方法中獲得的valueSelect,這是的標籤- 列表中的第一個項目,根本沒有預料到。

<f:selectItem>itemValue屬性設置爲空字符串,然後,它在即使異常精確捕捉和ConverterException註冊getAsObject()方法拋出java.lang.NumberFormatException: For input string: ""

這在某種程度上似乎是工作,當getAsString()return語句從

return value instanceof Country?((Country)value).getCountryId().toString():null; 

改爲

​​

null由一個空字符串替換,但是當對象返回一個空字符串問題是null,反過來又引發另一個問題,如演示here

如何使這種轉換器正常工作?

也嘗試與org.omnifaces.converter.SelectItemsConverter但它沒有區別。

+1

你有沒有考慮過這個''? –

+0

在這篇文章之前 - 一年前,我曾嘗試使用'noSelectionOption =「true」',但它似乎也沒有任何區別。 – Tiny

回答

24

當選擇的項目值爲null,那麼JSF將不會呈現<option value>,但只有<option>。因此,瀏覽器會提交選項的標籤。這在HTML specification(重點煤礦)明確規定:

value = cdata [CS]

此屬性指定控制的初始值。 如果未設置此屬性,則將初始值設置爲OPTION元素的內容。

您也可以通過查看HTTP流量監視器來確認這一點。您應該看到正在提交的選項標籤。

您需要將選擇項值設置爲空字符串。然後JSF將呈現<option value="">。如果您使用的是轉換器,那麼當值爲null時,實際上應該從轉換器返回空字符串""。這也清楚地Converter#getAsString()的javadoc(重點煤礦)規定:

getAsString

...

返回:一個零長度字符串,如果值爲null,在其他方面的結果轉換

因此,如果您使用<f:selectItem itemValue="#{null}">結合這種轉換器,然後<option value="">將被呈現並且瀏覽器將僅提交一個空字符串而不是選項標籤。

至於處理空字符串提交值(或null),你應該讓你的轉換器將這個責任委託給required="true"屬性。所以,當傳入的valuenull或一個空字符串,那麼你應該立即返回null基本上你的實體轉換器應該等實現如下:

@Override 
public String getAsString(FacesContext context, UIComponent component, Object value) { 
    if (value == null) { 
     return ""; // Required by spec. 
    } 

    if (!(value instanceof SomeEntity)) { 
     throw new ConverterException("Value is not a valid instance of SomeEntity."); 
    } 

    Long id = ((SomeEntity) value).getId(); 
    return (id != null) ? id.toString() : ""; 
} 

@Override 
public Object getAsObject(FacesContext context, UIComponent component, String value) { 
    if (value == null || value.isEmpty()) { 
     return null; // Let required="true" do its job on this. 
    } 

    if (!Utils.isNumber(value)) { 
     throw new ConverterException("Value is not a valid ID of SomeEntity."); 
    } 

    Long id = Long.valueOf(value); 
    return someService.find(id); 
} 

至於你有這方面的問題,

但是當有問題的對象爲空,又即被返回一個空字符串另一個問題如演示here

作爲回答那邊,這是在鑽嘴魚科一個錯誤,因爲OmniFaces 1.8 <o:viewParam>旁路。所以如果你至少升級到OmniFaces 1.8.3並使用它的<o:viewParam>而不是<f:viewParam>,那麼你就不應該受到這個bug的影響。

OmniFaces SelectItemsConverter也應該在這種情況下工作。它爲null返回一個空字符串。

+0

與此轉換器一起,我將OmniFaces升級到[1.8.1](http://central.maven.org/maven2/org/omnifaces/omnifaces/1.8.1/),其​​中''也可以直觀地工作。然而,我總是害怕一件事 - 如問題中所示,在應用程序作用域bean中注入EJB是否有害? – Tiny

+0

不是。 EJB(和CDI託管的bean,但不是** JSF託管的bean)使用代理模式。實際被注入的實例只是一個代理。它除了在每個方法調用的基礎上定位當前可用的實例外沒有別的。假設你熟悉JSF,你可以想象它在內部使用'FacesContext.getCurrentInstance()',然後從所需的範圍映射中獲取bean,調用它的方法並最終返回它的結果(如果有的話)。這樣的代理實例完全可以是應用程序範圍的。它是由容器使用反射自動生成的。 – BalusC

+0

請注意,EJB和CDI實際上並沒有這樣使用'FacesContext',但有自己的EJB和CDI上下文(分別是'EJBContext'和'BeanManager')。無論如何,你現在應該得到這張照片。 – BalusC

2

你混合了一些東西,這是不完全清楚,我要達到什麼樣的,但是我們可以嘗試

這顯然會導致java.lang.NumberFormatException在拋出 其轉換器。

它沒什麼明顯的。如果值爲空或空字符串,則不檢入轉換器,您應該。在這種情況下,轉換器應該返回null。

爲什麼它呈現Select(itemLabel)作爲它的值而不是空的 string(itemValue)?

select必須選擇一些東西。如果你沒有提供空值,列表中的第一個元素將被選中,這不是你所期望的。

剛剛修復轉換器,空/空字符串工作,並讓JSF反應返回null不會允許值。首先調用轉換,然後進行驗證。

我希望能回答你的問題。

+0

在'getAsObject()'方法內部,我改變了'return'語句,比如'return StringUtils.isNotBlank(value)&& StringUtils.isNumeric(value)?sharableService.find(Long.parseLong(value)):null;其中'org.apache.commons.lang.StringUtils'類由外部庫「Apache Common Lang」保存。它在這個改變中工作得很好,'required'約束也可以工作,但爲什麼''render'Select'作爲它的值而不是渲染一個空字符串?它是否呈現'itemLabel'而不是呈現'itemValue'? – Tiny

+0

不,我不希望在轉換器中獲得'itemLabel'(而不是'itemValue')。我在'getAsObject()'方法中聲明瞭一個語句,''System.out.println(「value =」+ value)''。它顯示'value = Select'(即'itemLabel')。這是正確的嗎?我不能相信這一點。這隻發生在嵌入的''而不是''的地方。 – Tiny

+0

好吧,我沒有寫清楚,你在轉換器中的itemLabel和itemValue之間進行轉換。所以在一個方向上你變成了itemValue,並且必須爲它指定itemLabel,而在另一個方向上你必須找到itemLabel的itemValue。 –

1

除了不完備,這個答案是不推薦的,因爲我使用Spring這個帖子的時間:

我已經修改了轉換器的getAsString()方法返回一個空字符串,而不是返回null,當沒有Country找到對象像(除了一些其它的變化),

@Controller 
@Scope("request") 
public final class CountryConverter implements Converter { 

    @Autowired 
    private final transient Service service = null; 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 
     try { 
      long parsedValue = Long.parseLong(value); 

      if (parsedValue <= 0) { 
       throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "The id cannot be zero or negative.")); 
      } 

      Country country = service.findCountryById(parsedValue); 

      if (country == null) { 
       throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "", "The supplied id doesn't exist.")); 
      } 

      return country; 
     } catch (NumberFormatException e) { 
      throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Conversion error : Incorrect id."), e); 
     } 
    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 
     return value instanceof Country ? ((Country) value).getCountryId().toString() : ""; //<--- Returns an empty string, when no Country is found. 
    } 
} 

而且<f:selectItem>itemValue到如下接受null值。

<p:selectOneMenu id="cmbCountry" 
       value="#{stateManagedBean.selectedItem}" 
       required="true"> 

    <f:selectItem itemLabel="Select" itemValue="#{null}"/> 

    <f:selectItems var="country" 
        converter="#{countryConverter}" 
        value="#{stateManagedBean.selectedItems}" 
        itemLabel="#{country.countryName}" 
        itemValue="${country}"/> 
</p:selectOneMenu> 

<p:message for="cmbCountry"/> 

這將生成以下HTML。

<select id="form:cmbCountry_input" name="form:cmbCountry_input"> 
    <option value="" selected="selected">Select</option> 
    <option value="56">Country1</option> 
    <option value="55">Country2</option> 
</select> 

早些時候,生成的HTML看起來像,

<select id="form:cmbCountry_input" name="form:cmbCountry_input"> 
    <option selected="selected">Select</option> 
    <option value="56">Country1</option> 
    <option value="55">Country2</option> 
</select> 

通知第一<option>沒有value屬性。

當第一個選項被選中時(儘管require設置爲false),這可以按預期的方式繞過轉換器。當itemValue更改爲null以外時,它的行爲不可預測(我不明白這一點)。

如果設置爲非空值並且轉換器中收到的項目始終爲空字符串(即使選擇了其他選項),也不能選擇列表中的其他項目。

此外,當這個空字符串中的轉換器進行解析,以Long,這是NumberFormatException後引起ConverterException拋出不報告在UIViewRoot錯誤(至少是這樣的話)。相反,可以在服務器控制檯上看到完整的異常堆棧跟蹤。

如果有人可以揭示這一點,我會接受答案,如果它給出。

-1

這是完全正常工作對我說:

<p:selectOneMenu id="cmbCountry" 
       value="#{bean.country}" 
       required="true" 
       converter="#{countryConverter}"> 

    <f:selectItem itemLabel="Select"/> 

    <f:selectItems var="country" 
        value="#{bean.countries}" 
        itemLabel="#{country.countryName}" 
        itemValue="#{country}"/> 

    <p:ajax update="anotherMenu" listener=/> 
</p:selectOneMenu> 

轉換器

@Controller 
@Scope("request") 
public final class CountryConverter implements Converter { 

    @Autowired 
    private final transient Service service = null; 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 
     if (value == null || value.trim().equals("")) { 
      return null; 
     } 
     //.... 
     // No change 
    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 
     return value == null ? null : value instanceof Country ? ((Country) value).getCountryId().toString() : null; 
     //**** Returns an empty string, when no Country is found ---> wrong should return null, don't care about the rendering. 
    } 
} 
3
  • 如果你想避免你的選擇組件空值,最優雅的方式是使用noSelectionOption

noSelectionOption="true",轉換器甚至不會嘗試處理該值。

此外,當您將其與<p:selectOneMenu required="true">結合使用時,您將收到驗證錯誤,當用戶嘗試選擇該選項時。

最後一個觸摸,您可以使用itemDisabled屬性來向用戶說明他不能使用此選項。

<p:selectOneMenu id="cmbCountry" 
       value="#{bean.country}" 
       required="true" 
       converter="#{countryConverter}"> 

    <f:selectItem itemLabel="Select" 
        noSelectionOption="true" 
        itemDisabled="true"/> 

    <f:selectItems var="country" 
        value="#{bean.countries}" 
        itemLabel="#{country.countryName}" 
        itemValue="#{country}"/> 

    <p:ajax update="anotherMenu" listener=/> 
</p:selectOneMenu> 

<p:message for="cmbCountry"/> 
  • 現在,如果你想能夠設置爲空值,你可以「騙」轉換器返回空值,通過使用

    <f:selectItem itemLabel="Select" itemValue="" /> 
    

更多閱讀hereherehere

-3
public void limparSelecao(AjaxBehaviorEvent evt) { 
    Object submittedValue = ((UIInput)evt.getSource()).getSubmittedValue(); 

    if (submittedValue != null) { 
     getPojo().setTipoCaixa(null); 
    } 
} 
<p:selectOneMenu id="tipo" 
       value="#{cadastroCaixaMonitoramento.pojo.tipoCaixa}" 
       immediate="true" 
       required="true" 
       valueChangeListener="#{cadastroCaixaMonitoramento.selecionarTipoCaixa}"> 

    <f:selectItem itemLabel="Selecione" itemValue="SELECIONE" noSelectionOption="false"/> 

    <f:selectItems value="#{cadastroCaixaMonitoramento.tiposCaixa}" 
        var="tipo" itemValue="#{tipo}" 
        itemLabel="#{tipo.descricao}" /> 

    <p:ajax process="tipo" 
      update="iten_monitorado" 
      event="change" listener="#{cadastroCaixaMonitoramento.limparSelecao}" /> 
</p:selectOneMenu> 
相關問題