2014-06-23 14 views
5

Câu hỏi này có thể có nhiều loại "khái niệm" hoặc "Tôi không hiểu JSF".Cú pháp bất hợp pháp cho hoạt động thiết lập: Làm thế nào để báo cho JSF tôi không "muốn" một setter

Kịch bản của tôi: Tôi có trang JSF (index.xhtml) nơi tôi sử dụng p:accordionPanel (nhưng tôi không nghĩ nó quan trọng đối với thành phần). Những gì tôi muốn làm là đặt activeIndexes của nó.

<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever')}"> 
// bla bla... 
</p:accordionPanel> 

Và phương pháp (đơn giản) trong đậu ủng hộ:

public String getActiveIndexesForSections(String holderName){ 
    String activeSections = ""; 
    for(Section s : sectionMap.get(holderName)){ 
     if (s.isActive()) 
     //add to the string 
    } 
    return activeSections; 
} 

Bây giờ này chỉ hoạt động tốt trong một lần tải trang bình thường.

Nhưng nếu tôi bấm vào một p:commandButton (với ajax=false) (hoặc bất cứ điều gì khác mà "gửi" dữ liệu lại cho máy chủ tôi đoán) - Tôi nhận được ngoại lệ sau đây:

/WEB-INF/tags/normalTextSection.xhtml @8,112 activeIndex="#{myController.getActiveIndexesForSections(name)}": Illegal Syntax for Set Operation 
// bla.. 
Caused by: javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation 

Sau khi một số googling/đọc thông báo lỗi tôi thấy rằng tôi cần một số setter.

Trước hết: Tôi không muốn người đặt cược - tôi có thực sự cần một hay không có cách nào để nói với JSF Tôi không muốn "hành vi" này.

Thứ hai tôi nhận ra rằng không dễ dàng để cung cấp một setter, bởi vì phương pháp của tôi có một tham số (vì vậy public void setActiveIndexesForSections(String name, String activeIndexes) hoặc public void setActiveIndexesForSections(String name) sẽ không hoạt động). Những gì tôi đã đưa ra cuối cùng là:

Tạo (generic) "Pseudo-hữu-class":

// just a dummy class since the class is recreated at every request 
public class Property<T> implements Serializable { 

    private T val; 

    public Property(T val) { 
     this.val= val; 
    } 

    public T getVal() { 
     return val; 
    } 

      //no need to do anyhting 
    public void setVal(T val) { 
    } 
} 

Thay đổi phương pháp đậu:

public Property<String> getActiveIndexesForSections(String holderName){ 
    String activeSections = ""; 
    for(Section s : sectionMap.get(holderName)){ 
     if (s.isActive()) 
     //add to the string 
    } 
    return new Property<String>(activeSections); 
} 

Và gọi nó là từ số index.xhtml:

<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever').val}"> 
// bla bla... 
</p:accordionPanel> 

Điều này hoạt động nhưng rõ ràng là một hack/wo xấu xí rkaround.

Cách thích hợp để xử lý tình huống như thế này là gì? Hoặc là những gì tôi đang làm đơn giản là hoàn toàn sai?

Trả lời

17

Trình thiết lập là cần thiết để ghi nhớ các chỉ mục hoạt động giống như khi biểu mẫu được gửi. Về cơ bản, bạn cần phải ràng buộc nó như là một biểu thức giá trị (với một thuộc tính), không phải là một biểu thức phương thức (như một phương thức hành động), cũng như một bộ sưu tập không thể sửa đổi (như activeIndex="#{param.tab}"). Chính xác như với các giá trị đầu vào. Về mặt kỹ thuật, bạn thực sự đang làm điều đó "đơn giản là hoàn toàn sai";)

Tuy nhiên, yêu cầu này được hiểu rõ. Do bạn thực sự không quan tâm đến các chỉ mục đang hoạt động đã thay đổi, và do đó muốn đặt lại chúng thành mặc định trên mỗi lần gửi biểu mẫu, bạn có thể bỏ qua nó bằng cách lưu trữ kết quả dưới dạng thuộc tính yêu cầu với sự trợ giúp của <c:set>. Bằng cách này bạn sẽ đánh lừa EL để thiết lập nó trong bản đồ thuộc tính yêu cầu thay vì thuộc tính bean intented.

<c:set var="activeIndex" value="#{myController.getActiveIndexesForSections('whatever')}" scope="request" /> 
<p:accordionPanel multiple="true" activeIndex="#{activeIndex}"> 
    <!-- bla bla... --> 
</p:accordionPanel> 

Theo trang bìa, về cơ bản nó sẽ làm hoạt động setter, rõ ràng là chỉ hoạt động.


Cập nhật: khi kiểm tra việc source code of AccordionPanel component, tôi thấy cách giải quyết khác cho thực tế rằng activeIndex sẽ không được thiết lập khi các thuộc tính rendered đánh giá false. Vì vậy, chỉ cần thay đổi thuộc tính rendered để hoạt động chính xác: đánh giá false trong giai đoạn cập nhật giá trị mô hình (giai đoạn 4).

<p:accordionPanel multiple="true" 
    activeIndex="#{myController.getActiveIndexesForSections('whatever')}" 
    rendered="#{facesContext.currentPhaseId.ordinal ne 4}"> 
    <!-- bla bla... --> 
</p:accordionPanel> 
+0

Cảm ơn bạn đã thương xót tôi;) Bạn có đề xuất một cách khác để làm điều đó - vì nó "đơn giản" sai? (Điều tôi không thể tin là, trường hợp sử dụng này hiếm đến mức không ai (mong đợi tôi) muốn làm điều này - vì vậy tôi thực sự băn khoăn liệu mình có hiểu sai hình ảnh lớn ...) - Cũng là 'accordionPanel' của tôi s được tạo ra trong một thẻ mà 'bất cứ điều gì' được thông qua như một tham số - tôi thậm chí có thể sử dụng tham số như'''' của 'c: set' và sau đó là' activeIndex' của 'accordionPanel'? Nếu thế thì sao? –

+1

Cách phù hợp trong trường hợp cụ thể này sẽ trả về bản triển khai 'Bản đồ' tùy chỉnh. Và không, bạn không thể sử dụng EL trong thuộc tính 'var', vì vậy một tagfile sẽ khó chịu. Trong bất kỳ cách nào, sau khi kiểm tra mã nguồn của '', tôi thấy một cách giải quyết khác có thể sử dụng lại tốt hơn trong các tagfiles. Xem cập nhật câu trả lời. – BalusC

+0

Cảm ơn. Sẽ thử điều này và cũng là 'bản đồ' tùy chỉnh 'vào ngày mai. Và nếu nó hoạt động chấp nhận + "bounty" bạn :) –

Các vấn đề liên quan