Sto creando un’applicazione web, in cui devi leggere un elenco di oggetti / quadro da un DB e popolarlo in un JSF . Non riesco a codificarlo. Qualcuno può mostrarmi come si fa?
So come ottenere una List
dal DB. Quello che devo sapere è come compilare questo elenco in un .
...?
In base alla cronologia delle domande, stai utilizzando JSF 2.x. Quindi, ecco una risposta mirata di JSF 2.x. In JSF 1.x saresti costretto a racchiudere valori / etichette degli articoli in brutte istanze SelectItem
. Fortunatamente non è più necessario in JSF 2.x.
Per rispondere direttamente alla tua domanda, usa
cui value
punta a una proprietà List
che conservi dal DB durante la costruzione (post) di bean. Ecco un esempio di kickoff di base assumendo che T
rappresenti effettivamente una String
.
con
@ManagedBean @RequestScoped public class Bean { private String name; private List names; @EJB private NameService nameService; @PostConstruct public void init() { names = nameService.list(); } // ... (getters, setters, etc) }
Semplice come quella. In realtà, T
‘s toString()
verrà utilizzato per rappresentare sia l’etichetta dell’elemento a discesa che il valore. Quindi, quando sei al posto di List
usando un elenco di oggetti complessi come List
e non hai sovrascritto il metodo toString()
della class, [email protected]
come elemento valori. Vedi la prossima sezione come risolverlo correttamente.
Si noti inoltre che il bean per il valore
non deve necessariamente essere lo stesso bean del bean per il valore
. Questo è utile ogni volta che i valori sono in realtà costanti di applicazione che devi caricare solo una volta durante l’avvio dell’applicazione. Potresti quindi renderlo una proprietà di un bean con scope dell’applicazione.
Ogni volta che T
riguarda un object complesso (un javabean), come User
che ha una proprietà String
di name
, allora si può usare l’attributo var
per ottenere la variabile iterativa che a sua volta può usare negli itemValue
e / o itemLabel
( se si omette l’ itemLabel
, l’etichetta diventa uguale al valore).
Esempio 1:
con
private String userName; private List users; @EJB private UserService userService; @PostConstruct public void init() { users = userService.list(); } // ... (getters, setters, etc)
O quando ha un Long
proprietà Long
che vorresti impostare come valore dell’articolo:
Esempio 2:
con
private Long userId; private List users; // ... (the same as in previous bean example)
Ogni volta che si desidera impostarlo su una proprietà T
nel bean e T
rappresenta un User
, sarà necessario impostare un Converter
personalizzato che converta tra User
e una rappresentazione di stringa univoca (che può essere la proprietà id
). Si noti che itemValue
deve rappresentare l’object complesso stesso, esattamente il tipo che deve essere impostato come value
del componente di selezione.
con
private User user; private List users; // ... (the same as in previous bean example)
e
@ManagedBean @RequestScoped public class UserConverter implements Converter { @EJB private UserService userService; @Override public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) { if (submittedValue == null || submittedValue.isEmpty()) { return null; } try { return userService.find(Long.valueOf(submittedValue)); } catch (NumberFormatException e) { throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e); } } @Override public String getAsString(FacesContext context, UIComponent component, Object modelValue) { if (modelValue == null) { return ""; } if (modelValue instanceof User) { return String.valueOf(((User) modelValue).getId()); } else { throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e); } } }
(si noti che il Converter
è un po ‘hacky per poter iniettare un @EJB
in un convertitore JSF, normalmente uno lo avrebbe annotato come @FacesConverter(forClass=User.class)
, ma sfortunatamente non consente @EJB
Iniezioni di @EJB
)
Non dimenticarti di assicurarti che la class di oggetti complessa abbia equals()
e hashCode()
correttamente implementati , altrimenti JSF durante il rendering non mostrerà gli articoli preselezionati, e dovrai inviare il messaggio Validation Error: Value is not valido
public class User { private Long id; @Override public boolean equals(Object other) { return (other != null && getClass() == other.getClass() && id != null) ? id.equals(((User) other).id) : (other == this); } @Override public int hashCode() { return (id != null) ? (getClass().hashCode() + id.hashCode()) : super.hashCode(); } }
Vai a questa risposta: implementa convertitori per quadro con Java Generics .
La libreria di utilità JSF OmniFaces offre un convertitore speciale che consente di utilizzare oggetti complessi in
senza la necessità di creare un convertitore personalizzato. SelectItemsConverter
eseguirà semplicemente la conversione in base agli elementi prontamente disponibili in
.
Visualizza-Pagina
Backing-Bean
List names = new ArrayList (); //-- Populate list from database names.add(new SelectItem(valueObject,"label")); //-- setter/getter accessor methods for list
Per visualizzare un particolare record selezionato, deve essere uno dei valori nell’elenco.
Il Balusc fornisce una risposta panoramica molto utile su questo argomento. Ma c’è un’alternativa che non presenta: il convertitore generico Roll-your-own che gestisce oggetti complessi come l’elemento selezionato. Questo è molto complesso da fare se vuoi gestire tutti i casi, ma piuttosto semplice per casi semplici.
Il codice seguente contiene un esempio di tale convertitore. Funziona nello stesso spirito di OmniFaces SelectItemsConverter mentre guarda attraverso i figli di un componente per UISelectItem(s)
contenente oggetti. La differenza consiste nel fatto che gestisce solo i collegamenti alle raccolte semplici di oggetti quadro o alle stringhe. Non gestisce gruppi di articoli, raccolte di SelectItem
, array e probabilmente molte altre cose.
Le entity framework alle quali il componente si lega devono implementare l’interfaccia IdObject
. (Questo potrebbe essere risolto in altro modo, come l’uso di toString
.)
Si noti che le quadro devono implementare gli equals
in modo tale che due entity framework con lo stesso ID siano uguali.
L’unica cosa che devi fare per usarlo è specificarlo come convertitore sul componente select, associare a una proprietà entity e un elenco di possibili quadro:
Converter:
/** * A converter for select components (those that have select items as children). * * It convertes the selected value string into one of its element entities, thus allowing * binding to complex objects. * * It only handles simple uses of select components, in which the value is a simple list of * entities. No ItemGroups, arrays or other kinds of values. * * Items it binds to can be strings or implementations of the {@link IdObject} interface. */ @FacesConverter("selectListConverter") public class SelectListConverter implements Converter { public static interface IdObject { public String getDisplayId(); } @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { if (value == null || value.isEmpty()) { return null; } return component.getChildren().stream() .flatMap(child -> getEntriesOfItem(child)) .filter(o -> value.equals(o instanceof IdObject ? ((IdObject) o).getDisplayId() : o)) .findAny().orElse(null); } /** * Gets the values stored in a {@link UISelectItem} or a {@link UISelectItems}. * For other components returns an empty stream. */ private Stream> getEntriesOfItem(UIComponent child) { if (child instanceof UISelectItem) { UISelectItem item = (UISelectItem) child; if (!item.isNoSelectionOption()) { return Stream.of(item.getValue()); } } else if (child instanceof UISelectItems) { Object value = ((UISelectItems) child).getValue(); if (value instanceof Collection) { return ((Collection>) value).stream(); } else { throw new IllegalStateException("Unsupported value of UISelectItems: " + value); } } return Stream.empty(); } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { if (value == null) return null; if (value instanceof String) return (String) value; if (value instanceof IdObject) return ((IdObject) value).getDisplayId(); throw new IllegalArgumentException("Unexpected value type"); } }
Lo sto facendo in questo modo:
I modelli sono ViewScoped
convertitore:
@Named @ViewScoped public class ViewScopedFacesConverter implements Converter, Serializable { private static final long serialVersionUID = 1L; private Map converterMap; @PostConstruct void postConstruct(){ converterMap = new HashMap<>(); } @Override public String getAsString(FacesContext context, UIComponent component, Object object) { String selectItemValue = String.valueOf( object.hashCode() ); converterMap.put( selectItemValue, object ); return selectItemValue; } @Override public Object getAsObject(FacesContext context, UIComponent component, String selectItemValue){ return converterMap.get(selectItemValue); } }
e associare al componente con:
Se userai id di entity framework piuttosto che hashCode puoi colpire una collisione- se hai poche liste su una pagina per diverse quadro (classi) con lo stesso id
Chiamami pigro ma la codifica di un convertitore sembra un lavoro inutile. Sto usando Primefaces e, non avendo usato prima una semplice lista di selezione JSF2 o un menu a discesa, ho pensato (essendo pigro) che il widget potesse gestire oggetti complessi, cioè passare l’object selezionato così com’è al suo corrispondente getter / setter come molti altri widget fanno. Sono rimasto deluso dal constatare (dopo ore di grattacapo) che questa funzionalità non esiste per questo tipo di widget senza convertitore. Infatti se fornisci un setter per l’object complesso piuttosto che per una stringa, fallisce silenziosamente (semplicemente non chiama setter, nessuna eccezione, nessun errore JS) e ho passato un sacco di tempo a passare attraverso l’eccellente strumento di risoluzione dei problemi di BalusC. per trovare la causa, senza risultato poiché nessuno di questi suggerimenti è stato applicato. La mia conclusione: il widget listbox / menu deve adattarsi a quello che gli altri widget JSF2 non hanno. Questo sembra fuorviante e incline a condurre lo sviluppatore disinformato come me in una tana di rabbitmq.
Alla fine ho resistito alla codifica di un convertitore e ho riscontrato, tramite prove ed errori, che se si imposta il valore del widget su un object complesso, ad esempio:
… quando l’utente seleziona un object, il widget può chiamare un setter String per quell’object, ad esempio setSelectedThing(String thingString) {...}
e la stringa passata è una stringa JSON che rappresenta l’object Thing. Posso analizzarlo per determinare quale object è stato selezionato. Questo sembra un po ‘come un hack, ma meno di un hack di un convertitore.