Come popolare le opzioni di h: selectOneMenu dal database?

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.


Esempio di base

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.

    

Oggetti complessi come oggetti disponibili

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) 

Oggetto complesso come object selezionato

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

Oggetti complessi con un convertitore generico

Vai a questa risposta: implementa convertitori per quadro con Java Generics .


Oggetti complessi senza un convertitore personalizzato

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 .

    

Guarda anche:

  • La nostra pagina wiki

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.

Convertitore generico roll-your-own per oggetti complessi come elemento selezionato

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:

  1. I modelli sono ViewScoped

  2. 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.