2016-02-05 28 views
0

轉換器:java.math.BigDecimal中在號碼:selectOneMenu用於

@FacesConverter("bigDecimalConverter") 
public class BigDecimalConverter implements Converter { 

    private static final int SCALE = 2; 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 

     if (value == null || value.isEmpty()) { 
      return null; 
     } 

     try { 
      return new BigDecimal(value); 
     } catch (NumberFormatException e) { 
      throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Message"), e); 
     } 
    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 

     if (value == null) { 
      return ""; 
     } 

     BigDecimal newValue; 

     if (value instanceof Long) { 
      newValue = BigDecimal.valueOf((Long) value); 
     } else if (value instanceof Double) { 
      newValue = BigDecimal.valueOf((Double) value); 
     } else if (!(value instanceof BigDecimal)) { 
      throw new ConverterException("Message"); 
     } else { 
      newValue = (BigDecimal) value; 
     } 

     DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance(); 
     formatter.setGroupingUsed(false); 
     formatter.setMinimumFractionDigits(SCALE); 
     formatter.setMaximumFractionDigits(SCALE); 
     return formatter.format(newValue); 
    } 
} 

列表:

<p:selectOneMenu id="list" value="#{bean.value}"> 
    <f:selectItems var="row" value="#{bean.list}" itemLabel="#{row}" itemValue="#{row}"/> 
    <f:converter converterId="bigDecimalConverter"/> 
</p:selectOneMenu> 

<p:message id="msg" for="list"/> 
<p:commandButton value="Submit" update="list msg" actionListener="#{bean.action}"/> 

託管bean由上述<p:selectOneMenu>備份:

@ManagedBean 
@ViewScoped 
public class Bean implements Serializable { 

    private List<BigDecimal> list; // Getter only. 
    private BigDecimal value; // Getter & setter. 
    private static final long serialVersionUID = 1L; 

    public Bean() {} 

    @PostConstruct 
    private void init() { 
     list = new ArrayList<BigDecimal>(){{ 
      add(BigDecimal.valueOf(10)); 
      add(BigDecimal.valueOf(20.11)); 
      add(BigDecimal.valueOf(30)); 
      add(BigDecimal.valueOf(40)); 
      add(BigDecimal.valueOf(50)); 
     }}; 
    } 

    public void action() { 
     System.out.println("action() called : " + value); 
    } 
} 

驗證消息,提交表單時出現「驗證錯誤:值無效」。在提交表單時,getAsObject()方法不會引發異常。

如果在列表中選擇了一個類似20.11的值,則驗證通過。看起來java.math.BigDecimal類中的equals()方法很腥,例如,只有當兩個對象的值和比例相等時,才考慮兩個對象相等,因此10.0 != 10.00需要compareTo()使它們相等。

什麼建議嗎?

+0

除了技術問題,本身似乎沒有轉換器在這種情況下是有用的。它只適用於項目值,而不適用於項目標籤。所以最終用戶看不到它的'getAsString()'效果。 JSF內建的'BigDecimalConverter'或者''究竟有多不足? – BalusC

+0

該轉換器旨在用於貨幣和其他地方的分組,除了將值湊整到指定的小數位數。 – Tiny

+0

我明白了,但是現在你所擁有的轉換器似乎只能用於輸出,而不能用於輸入。我想你會希望純輸出的情況下更好地擴展標準的'NumberConverter',以防你想要格式化默認值,而不需要一遍又一遍地重複它們。巧合的是我昨天發佈了類似的答案:http:// stackoverflow。com/questions/35180537/composite-component-with-fconvertnumber-wont-work/ – BalusC

回答

1

轉換爲字符串時會丟失信息。默認的JSF BigDecimalConverter會這樣做,它在getAsString中使用BigDecimal#toString。該BigDecimal#toString的javadoc中說:

There is a one-to-one mapping between the distinguishable BigDecimal values and the result of this conversion. That is, every distinguishable BigDecimal value (unscaled value and scale) has a unique string representation as a result of using toString. If that string representation is converted back to a BigDecimal using the BigDecimal(String) constructor, then the original value will be recovered.

這正是你需要的。不要將轉換器視爲轉換爲字符串時必須產生用戶可讀寫結果的轉換器。他們不能,也經常不會。它們產生對象的字符串表示或對象引用。而已。在這種情況下,selectItems'itemLabel定義了用戶可讀的表示。我假設你不希望用戶在這裏寫入值,你確實有一個固定的值列表供用戶選擇。

如果您確實意味着此數據必須始終爲2,並且您需要用戶可寫值,那麼最好在驗證程序中檢查該值,並且可以用p:inputMask幫助用戶輸入。

最後,讓我們放下一個事實,即你的轉換器不是最好的。它說「數據必須具有2的比例」。那麼你應該在你的selectItems提供符合的數據。更一般地說,服務器定義的值必須符合相關的轉換器和驗證器。例如。當使用DateTimeConverter"dd.MM.yyyy",但將缺省值設置爲new Date()而沒有擺脫時間部分時,您可能會有同樣的問題。

參見(約轉換器更一般的概念):https://stackoverflow.com/a/30529976/1341535

+0

如何顯示並提交像'$ 10.00'這樣的用戶可讀貨幣和如'11,111,111.00'這樣的分組值? 'getAsObject()'方法在將它們提交給一個較低的抽象層之前應分別將它們轉換爲'10.00'和'11111111.00',並且'getAsString()'方法應該在呈現之前分別將它們轉換回$ 10.00和11,111,111.00他們對最終用戶(貨幣可能有可變的小數位數)。我認爲這是任何轉換器的一致任務,因爲'toString()'產生的結果通常對最終用戶沒有用處。 – Tiny

+0

這些值確實可以使用自定義轉換器。我只是說,轉換器通常沒有義務提供有用的端到端用戶代表,並且在原始問題的情況下,由itemLabel完全沒有必要。請參閱JSF 2.2 Spec 3.3.1(轉換模型概述):「轉換內部數據表示的責任」。它是轉換器+演示視圖的組合,實現了最終用戶的有用性。使用轉換器+ inputText可以完全有效地解決用戶可寫值的情況。 –

相關問題