Basierend auf Ihrem Fragenverlauf verwenden Sie JSF 2.x. Hier ist eine gezielte Antwort auf JSF 2.x. In JSF 1.x wären Sie gezwungen, Elementwerte / Beschriftungen in hässlichen SelectItem
Fällen zu verpacken . Dies wird in JSF 2.x glücklicherweise nicht mehr benötigt.
Grundlegendes Beispiel
Um Ihre Frage direkt zu beantworten, verwenden Sie einfach <f:selectItems>
deren value
Punkte für eine List<T>
Eigenschaft, die Sie während der (Post-) Konstruktion von Bean aus der DB beibehalten. Hier ist ein grundlegendes Kickoff-Beispiel unter der Annahme, dass es sich T
tatsächlich um a handelt String
.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{bean.names}" />
</h:selectOneMenu>
mit
@ManagedBean
@RequestScoped
public class Bean {
private String name;
private List<String> names;
@EJB
private NameService nameService;
@PostConstruct
public void init() {
names = nameService.list();
}
}
So einfach ist das. Tatsächlich werden die T
's toString()
verwendet, um sowohl die Dropdown-Elementbezeichnung als auch den Wert darzustellen. Wenn Sie also List<String>
keine Liste komplexer Objekte wie verwenden List<SomeEntity>
und die toString()
Methode der Klasse nicht überschrieben haben , werden sie com.example.SomeEntity@hashcode
als Elementwerte angezeigt. Siehe nächster Abschnitt, wie man es richtig löst.
Beachten Sie auch, dass die Bean für den <f:selectItems>
Wert nicht unbedingt dieselbe Bean sein muss wie die Bean für den <h:selectOneMenu>
Wert. Dies ist immer dann nützlich, wenn es sich bei den Werten tatsächlich um anwendungsweite Konstanten handelt, die Sie beim Start der Anwendung nur einmal laden müssen. Sie können es dann einfach zu einer Eigenschaft einer Bean mit Anwendungsbereich machen.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{data.names}" />
</h:selectOneMenu>
Komplexe Objekte als verfügbare Elemente
Wenn es sich T
um ein komplexes Objekt (eine Javabean) handelt, das beispielsweise User
die String
Eigenschaft hat name
, können Sie das var
Attribut verwenden, um die Iterationsvariable abzurufen, die Sie wiederum in itemValue
und / oder itemLabel
attribtues verwenden können (wenn Sie die weglassen itemLabel
, dann die label wird gleich dem Wert).
Beispiel 1:
<h:selectOneMenu value="#{bean.userName}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.name}" />
</h:selectOneMenu>
mit
private String userName;
private List<User> users;
@EJB
private UserService userService;
@PostConstruct
public void init() {
users = userService.list();
}
Oder wenn es eine Long
Eigenschaft hat, id
die Sie lieber als Elementwert festlegen möchten:
Beispiel 2:
<h:selectOneMenu value="#{bean.userId}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.id}" itemLabel="#{user.name}" />
</h:selectOneMenu>
mit
private Long userId;
private List<User> users;
Komplexes Objekt als ausgewähltes Element
Wann immer Sie es auch auf eine T
Eigenschaft in der Bean setzen möchten und T
eine darstellen User
möchten, müssen Sie einen benutzerdefinierten Converter
Wert backen , der zwischen User
einer eindeutigen Zeichenfolgendarstellung konvertiert (die die id
Eigenschaft sein kann). Beachten Sie, dass das itemValue
muss das komplexe Objekt selbst darstellen, genau den Typ, der als Auswahlkomponente festgelegt werden muss value
.
<h:selectOneMenu value="#{bean.user}" converter="#{userConverter}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
mit
private User user;
private List<User> users;
und
@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);
}
}
}
(Bitte beachten Sie, dass das Converter
etwas hackig ist, um einen @EJB
in einen JSF-Konverter einspeisen zu können ; normalerweise hätte man es als kommentiert @FacesConverter(forClass=User.class)
, aber das erlaubt leider keine @EJB
Einspritzungen )
Vergessen Sie nicht , um sicher zu stellen , dass die komplexe Objektklasse hat equals()
und hashCode()
ordnungsgemäß umgesetzt werden , sonst JSF wird während machen nicht vorher ausgewählte Artikel zeigen (s), und Sie werden auf Gesicht einreichen Validierungsfehler: Wert nicht gültig ist .
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();
}
}
Komplexe Objekte mit einem generischen Konverter
Gehen Sie zu dieser Antwort: Implementieren Sie Konverter für Entitäten mit Java Generics .
Komplexe Objekte ohne benutzerdefinierten Konverter
Die JSF-Dienstprogrammbibliothek OmniFaces bietet einen speziellen Konverter, mit dem Sie komplexe Objekte verwenden können, <h:selectOneMenu>
ohne einen benutzerdefinierten Konverter erstellen zu müssen. Der SelectItemsConverter
wird einfach die Konvertierung basierend auf leicht verfügbaren Artikeln in durchführen <f:selectItem(s)>
.
<h:selectOneMenu value="#{bean.user}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
Siehe auch:
Ask Question
, um eine klare und konkrete Frage zu stellen.Ansichtsseite
<h:selectOneMenu id="selectOneCB" value="#{page.selectedName}"> <f:selectItems value="#{page.names}"/> </h:selectOneMenu>
Backing-Bean
List<SelectItem> names = new ArrayList<SelectItem>(); //-- Populate list from database names.add(new SelectItem(valueObject,"label")); //-- setter/getter accessor methods for list
Um einen bestimmten ausgewählten Datensatz anzuzeigen, muss er einer der Werte in der Liste sein.
quelle
Roll-your-own generischer Konverter für komplexe Objekte als ausgewähltes Element
Der Balusc gibt eine sehr nützliche Übersichtsantwort zu diesem Thema. Es gibt jedoch eine Alternative, die er nicht vorstellt: den generischen Roll-Your-Own-Konverter, der komplexe Objekte als ausgewähltes Element behandelt. Dies ist sehr komplex, wenn Sie alle Fälle behandeln möchten, aber für einfache Fälle ziemlich einfach.
Der folgende Code enthält ein Beispiel für einen solchen Konverter. Es funktioniert im gleichen Sinne wie der OmniFaces SelectItemsConverter, wenn es die untergeordneten Elemente einer Komponente nach
UISelectItem(s)
Objekten durchsucht . Der Unterschied besteht darin, dass nur Bindungen an einfache Sammlungen von Entitätsobjekten oder an Zeichenfolgen verarbeitet werden. Es behandelt keine Elementgruppen, Sammlungen vonSelectItem
s, Arrays und wahrscheinlich viele andere Dinge.Die Entitäten, an die die Komponente gebunden ist, müssen die
IdObject
Schnittstelle implementieren . (Dies könnte auf andere Weise gelöst werden, z. B. durch Verwendung vontoString
.)Beachten Sie, dass die Entitäten so implementiert werden müssen
equals
, dass zwei Entitäten mit derselben ID gleich sind.Das einzige, was Sie tun müssen, um es zu verwenden, ist, es als Konverter für die Auswahlkomponente anzugeben, an eine Entitätseigenschaft und eine Liste möglicher Entitäten zu binden:
<h:selectOneMenu value="#{bean.user}" converter="selectListConverter"> <f:selectItem itemValue="unselected" itemLabel="Select user..."/> <f:selectItem itemValue="empty" itemLabel="No user"/> <f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" /> </h:selectOneMenu>
Konverter:
/** * 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"); } }
quelle
Ich mache es so:
Modelle sind ViewScoped
Konverter:
@Named @ViewScoped public class ViewScopedFacesConverter implements Converter, Serializable { private static final long serialVersionUID = 1L; private Map<String, Object> 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); } }
und binden an Komponente mit:
<f:converter binding="#{viewScopedFacesConverter}" />
Wenn Sie die Entitäts-ID anstelle von hashCode verwenden, können Sie eine Kollision auslösen. Wenn Sie auf einer Seite nur wenige Listen für verschiedene Entitäten (Klassen) mit derselben ID haben
quelle
Nennen Sie mich faul, aber das Codieren eines Konverters scheint eine Menge unnötiger Arbeit zu sein. Ich verwende Primefaces und habe, nachdem ich zuvor kein einfaches Vanille-JSF2-Listenfeld oder Dropdown-Menü verwendet hatte, nur angenommen (faul), dass das Widget komplexe Objekte verarbeiten kann, dh das ausgewählte Objekt so wie es ist an den entsprechenden Getter / Setter weitergibt viele andere Widgets tun dies. Ich war enttäuscht (nach stundenlangem Kopfkratzen) festzustellen, dass diese Funktion für diesen Widget-Typ ohne Konverter nicht vorhanden ist. Wenn Sie einen Setter für das komplexe Objekt und nicht für einen String angeben, schlägt dies stillschweigend fehl (der Setter wird einfach nicht aufgerufen, keine Ausnahme, kein JS-Fehler), und ich habe eine Menge Zeit damit verbracht, BalusCs hervorragendes Tool zur Fehlerbehebung durchzugehendie Ursache zu finden, ohne Erfolg, da keiner dieser Vorschläge zutraf. Mein Fazit: Das Listenfeld- / Menü-Widget muss angepasst werden, was andere JSF2-Widgets nicht tun. Dies scheint irreführend und neigt dazu, den nicht informierten Entwickler wie mich in ein Kaninchenloch zu führen.
Am Ende habe ich mich geweigert, einen Konverter zu codieren, und durch Ausprobieren festgestellt, dass, wenn Sie den Widget-Wert auf ein komplexes Objekt setzen, z.
<p:selectOneListbox id="adminEvents" value="#{testBean.selectedEvent}">
... wenn der Benutzer ein Element auswählt, kann das Widget beispielsweise einen String-Setter für dieses Objekt aufrufen
setSelectedThing(String thingString) {...}
, und der übergebene String ist ein JSON-String, der das Thing-Objekt darstellt. Ich kann es analysieren, um festzustellen, welches Objekt ausgewählt wurde. Dies fühlt sich ein wenig wie ein Hack an, ist aber weniger ein Hack als ein Converter.quelle
h:inputText
funktioniert auch, wenn Sie Ihre eigenen stark typisierten Objekte erstellt haben, anstatt eine Zeichenfolge zu verwenden.h:inputText
oder sogar seine anspruchsvolleren Geschwisterp:inputText
Strings von Natur aus behandeln. listbox / menu scheint in der Lage zu sein, jedes Objekt zu verarbeiten, obwohl dieses Objekt natürlich nur durch einen String in der Benutzeroberfläche dargestellt werden kann.h:inputText
undp:inputText
kann auch mit Zahlen und mehr umgehen. Dies liegt daran, dass diese auf bekannten Java-Typen basieren und Konverter von jsf bereitgestellt und implizit angewendet werden. Für jeden anderen Typ wird ebenfalls ein Konverter benötigt, z. B. eine benutzerdefinierte, stark typisierte GTIN , die effektiv eine Zahl ist (in dieser Implementierung wird ein String verwendet). Und ja, die Konvertierung zur und von der 'clientseitigen' String-Darstellung ist das, worum sich Konverter kümmern ...