2014-06-23 9 views
5

Diese Frage kann mehr vom Typ "konzeptionelle" oder "Ich verstehe JSF nicht".Unzulässige Syntax für Set-Operation: Wie JSF zu sagen Ich will nicht ein Setter

Mein Szenario: Ich habe eine JSF-Seite (index.xhtml), wo ich ein verwenden p:accordionPanel (aber ich glaube nicht, dass es wichtig ist, was Komponente ist). Was ich tun möchte, ist die activeIndexes davon einzustellen.

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

Und der (vereinfachte) -Methode in der Backing Bean:

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

Jetzt funktioniert das ganz gut auf einer normalen Seite zu laden.

Aber wenn ich auf einem klicken p:commandButton (mit ajax=false) (oder irgendetwas anderes, das "sendet" Daten an den Server zurück I guess) - ich die folgende Ausnahme erhalten:

/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 

Nach einigen googeln/Lese Die Fehlermeldung Ich habe festgestellt, dass ich eine setter brauche.

Zuallererst: Ich möchte keinen Setter - brauche ich wirklich einen oder gibt es eine Möglichkeit, JSF zu sagen, dass ich dieses "Verhalten" nicht will.

Zweitens erkannte ich, dass es nicht so einfach ist, einen Setter bereitzustellen, weil meine Methode einen Parameter hat (so public void setActiveIndexesForSections(String name, String activeIndexes) oder public void setActiveIndexesForSections(String name) wird nicht funktionieren). Was ich mit am Ende kam ist:

erstellen (allgemein) "Pseudo-Objekt-Klasse":

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

Ändern Sie die Bean-Methode:

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

Und es nennen vom index.xhtml:

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

ist das funktioniert aber offensichtlich eine hässliche Hack/wo rkaround.

Wie ist der richtige Umgang mit einer solchen Situation? Oder ist das, was ich mache, einfach völlig falsch?

Antwort

17

Der Setter muss sich an die aktiven Indizes erinnern, so wie sie beim Übergeben des Formulars waren. Grundsätzlich müssen Sie es als Wertausdruck (mit einer Eigenschaft), nicht als Methodenausdruck (wie eine Aktionsmethode) oder als nicht änderbare Sammlung (wie activeIndex="#{param.tab}") binden. Genau wie bei Eingabewerten. Technisch gesehen macht man es ja "einfach ganz falsch";)

Die Anforderung wird jedoch verstanden. Da Sie an den geänderten aktiven Indizes nicht interessiert sind und diese daher bei jeder Formularübergabe auf die Standardwerte zurücksetzen möchten, können Sie sie umgehen, indem Sie das Ergebnis mit Hilfe von <c:set> als Anforderungsattribut speichern. Auf diese Weise werden Sie EL täuschen, um es in der Anforderungsattribut-Zuordnung statt der geplanten Bean-Eigenschaft festzulegen.

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

unter der Decke, wird es tut im Grunde externalContext.getRequestMap().put("activeIndex", value) als Setter Operation, die offensichtlich nur funktionieren wird.


aktualisieren: auf die source code of AccordionPanel component Inspektion, sah ich eine andere Abhilfe angesichts der Tatsache, dass die activeIndex nicht eingestellt werden, wenn das Attribut renderedfalse auswertet. Ändern Sie also einfach das rendered Attribut, um genau das zu tun: evaluieren Sie false während der Aktualisierung der Modellwertphase (der 4. Phase).

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

Vielen Dank für die Gnade mit mir;) Würden Sie einen anderen Weg empfehlen, es zu tun - da es "einfach" falsch ist? (Was ich nicht glauben kann, ist, dass dieser Anwendungsfall so selten ist, dass niemand (erwartet mich) jemals dies tun wollte - also frage ich mich, ob ich das große Bild missverstanden habe ...) - Auch mein 'AkkordeonPanel' s werden innerhalb eines Tags generiert, wobei 'was auch immer' als Parameter übergeben wird - kann ich den Parameter sogar als 'var' des 'c: set' und dann als' activeIndex' des 'accordionPanel' verwenden? Wenn das so ist, wie? –

+1

Der richtige Weg in diesem speziellen Fall wäre die Rückgabe einer benutzerdefinierten Map-Implementierung. Und nein, Sie können EL nicht im Attribut 'var' verwenden, daher wäre eine Tagdatei sehr unangenehm. Nachdem ich den Quellcode von '' inspiziert habe, habe ich eine andere Problemumgehung gesehen, die besser in tagfiles wiederverwendbar ist. Siehe das Antwortupdate. – BalusC

+0

Danke. Werde dies und auch die "benutzerdefinierte" Map morgen ausprobieren. Und wenn es klappt, nimm + "Kopfgeld" dir :) –