2014-06-23 41 views
5

To pytanie może być bardziej typu "pojęciowy" lub "Nie rozumiem JSF".Niedozwolona składnia dla operacji zestawu: Jak powiedzieć JSF Nie "chcę" ustawnika

Mój scenariusz: Mam stronę JSF (index.xhtml), gdzie używam p:accordionPanel (ale nie sądzę, aby miało to znaczenie, jaki element jest). Chcę go ustawić jako activeIndexes.

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

A (uproszczony) metoda w fasoli podkładowej:

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

Teraz to działa dobrze na normalnej stronie obciążenia.

Ale jeśli klikniesz na p:commandButton (z ajax=false) (lub cokolwiek innego, co „wysyła” dane z powrotem do serwera chyba) - pojawia się następujący wyjątek:

/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 

Po pewnym googlowania/odczyt komunikat o błędzie stwierdziłem, że potrzebuję setter.

Po pierwsze: nie chcę setera - czy naprawdę tego potrzebuję, czy jest sposób, aby powiedzieć JSF, że nie chcę tego "zachowania".

Po drugie, zdałem sobie sprawę, że nie jest to "łatwe" dostarczenie setera, ponieważ moja metoda ma parametr (więc public void setActiveIndexesForSections(String name, String activeIndexes) lub public void setActiveIndexesForSections(String name) nie zadziała). Co wymyśliłem w końcu jest:

Tworzenie (ogólne) "pseudo-property-klasa":

// 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) { 
    } 
} 

zmienić metodę fasoli:

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

i nazwać z index.xhtml:

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

to działa, ale oczywiście jest brzydki hack/wo rkaround.

Jaki jest właściwy sposób radzenia sobie z taką sytuacją? Czy to, co robię, jest po prostu całkowicie błędne?

Odpowiedz

17

Seter jest potrzebny, aby zapamiętać aktywne indeksy, tak jak wtedy, gdy formularz został przesłany. Zasadniczo musisz powiązać go jako wyrażenie wartości (z właściwością), a nie jako wyrażenie metody (jak metoda działania), ani z kolekcją niemodyfikowalną (jak activeIndex="#{param.tab}"). Dokładnie jak z wartościami wejściowymi. Technicznie rzecz biorąc, robisz to "po prostu całkowicie źle";)

Wymóg jest jednak zrozumiały. Biorąc pod uwagę, że naprawdę nie jesteś zainteresowany zmienionymi aktywnymi indeksami, a więc chcesz zresetować je do wartości domyślnych na każdym formularzu, możesz je pominąć, zapisując wynik jako atrybut żądania przy pomocy <c:set>. W ten sposób oszukasz EL, aby ustawić go w mapie atrybutów żądań zamiast z założonej właściwości fasoli.

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

pod kołdrą, to w zasadzie zrobić externalContext.getRequestMap().put("activeIndex", value) jak setter operacji, która oczywiście będzie po prostu pracować.


Aktualizacja: po inspekcji source code of AccordionPanel component, ujrzałem innego obejścia biorąc pod uwagę fakt, że activeIndex nie zostanie ustawiony, gdy atrybut rendered ocenia false. Więc po prostu zmień atrybut rendered, aby zachowywał się dokładnie tak: oceń false podczas fazy aktualizacji modelu aktualizacji (czwarta faza).

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

Dziękuję za to, że okazałeś mi łaskę; (Nie mogę uwierzyć, że ten przypadek użycia jest tak rzadki, że nikt (nie oczekiwał ode mnie) kiedykolwiek chciał to zrobić - więc tak naprawdę zastanawiam się, czy nie zrozumiałem tego wielkiego obrazu ...) - Także mój "akordeonPanel" s są generowane w tagu, gdzie "cokolwiek" jest przekazywane jako parametr - czy mogę nawet użyć parametru jako 'var' z 'c: set', a następnie jako' activeIndex' z 'accordionPanel'? Jeśli tak to jak? –

+1

Właściwym sposobem w tym konkretnym przypadku byłoby zwrócenie niestandardowej implementacji 'Map'. I nie, nie można używać EL w atrybucie "var", więc plik tagu byłby nieprzyjemny. W każdym razie, po sprawdzeniu kodu źródłowego '', zobaczyłem inne obejście, które jest lepsze do wielokrotnego użycia w tagfiles. Zobacz aktualizację odpowiedzi. – BalusC

+0

Dzięki. Spróbuję tego i jutro również "niestandardową" mapę. A jeśli to zadziała, zaakceptuj + "nagrodę" :) –