2014-06-23 10 views
5

Questa domanda potrebbe essere più del tipo "concettuale" o "Non capisco JSF".Sintassi non valida per l'operazione Set: Come dire a JSF che non "voglio" un setter

mio scenario: Ho una pagina JSF (index.xhtml), dove io uso un p:accordionPanel (ma non credo che sia importante quale componente è). Quello che voglio fare è impostare il activeIndexes di esso.

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

E il metodo (semplificato) nel backing bean:

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

Ora questo funziona bene su un carico normale pagina.

Ma se clicco su un p:commandButton (con ajax=false) (o qualsiasi altra cosa che "invia" i dati al server immagino) - ottengo la seguente eccezione:

/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 

Dopo un po 'googling/lettura il messaggio di errore ho trovato che ho bisogno di un setter.

Prima di tutto: non voglio un setter - ne ho davvero bisogno o c'è un modo per dire a JSF che non voglio questo "comportamento".

In secondo luogo mi sono reso conto che non è "facile" fornire un setter, perché il mio metodo ha un parametro (quindi public void setActiveIndexesForSections(String name, String activeIndexes) o public void setActiveIndexesForSections(String name) non funzionerà). Quello che mi è venuta alla fine è:

Creare un (generico) "Pseudo-proprietà-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) { 
    } 
} 

Cambiare il metodo di fagioli:

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

e chiamarlo dal index.xhtml:

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

Questo funziona, ma, ovviamente, è un brutto hack/wo rkaround.

Qual è il modo corretto di gestire una situazione come questa? O è quello che sto facendo semplicemente completamente sbagliato?

risposta

17

Il setter è necessario per ricordare gli indici attivi così come erano quando il modulo è stato inviato. Fondamentalmente, è necessario associarlo come espressione di valore (con una proprietà), non come un'espressione di metodo (come un metodo di azione), né a una raccolta non modificabile (come activeIndex="#{param.tab}"). Esattamente come per i valori di input. Tecnicamente, lo stai effettivamente facendo "semplicemente completamente sbagliato";)

Il requisito è comunque compreso. Dato che non sei realmente interessato agli indici attivi modificati e quindi desideri reimpostarli su valori predefiniti su ogni modulo, puoi ignorarlo memorizzando il risultato come attributo di richiesta con l'aiuto di <c:set>. In questo modo ingannerai EL per impostarlo nella mappa dell'attributo della richiesta anziché nella proprietà bean con intenti.

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

Sotto le coperte, che sarà essenzialmente fare externalContext.getRequestMap().put("activeIndex", value) come operazione setter, che ovviamente solo di lavoro.


Aggiornamento: su ispezione del source code of AccordionPanel component, ho visto un'altra soluzione in considerazione del fatto che la activeIndex non verrà impostato quando l'attributo rendered valuta false. Quindi basta modificare l'attributo rendered per comportarsi esattamente così: valutare false durante la fase dei valori del modello di aggiornamento (la 4a fase).

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

Grazie per avere pietà di me;) Consiglieresti un altro modo per farlo - dal momento che "semplicemente" è sbagliato? (Quello che non posso credere è che questo caso d'uso è così raro che nessuno (mi aspetto) abbia mai voluto farlo - quindi mi sto davvero chiedendo se ho capito male il quadro generale ...) - Anche il mio 'accordionPanel' s sono generati all'interno di un tag in cui 'qualunque cosa' viene passato come parametro - posso persino usare il parametro come 'var'of di 'c: set' e quindi come' activeIndex' di 'accordionPanel'? Se é cosi, come? –

+1

Il modo corretto in questo caso specifico sarebbe restituire un'implementazione personalizzata di 'Map'. E no, non puoi usare EL nell'attributo 'var', quindi un tagfile sarebbe cattivo. In ogni caso, dopo aver esaminato il codice sorgente di '', ho visto un'altra soluzione alternativa che è meglio riusabile nei tagfile. Vedi l'aggiornamento della risposta. – BalusC

+0

Grazie. Proveremo questo e anche la mappa 'personalizzata' di domani. E se funziona accetta + "taglia" tu :) –

Problemi correlati