Comment remplir les options de H: selectOneMenu de la base de données?

je crée une application web, où vous devez lire une liste d'objets / entités à partir d'une base de données et le peupler dans un JSF <h:selectOneMenu> . Je suis incapable de code. Quelqu'un peut me montrer comment faire?

je sais comment obtenir un List<User> du DB. Ce que j'ai besoin de savoir, c'est comment remplir cette liste dans un <h:selectOneMenu> .

<h:selectOneMenu value="#{bean.name}">
    ...?
</h:selectOneMenu>
66
demandé sur BalusC 2011-07-27 21:58:07

5 réponses

basé sur l'historique de vos questions, vous utilisez JSF 2.x. Donc, voici un JSF 2.x la réponse ciblée. Dans le JSF 1.x vous seriez forcé d'envelopper les valeurs/étiquettes des articles dans des instances laides SelectItem . Cela n'est heureusement plus nécessaire dans le JSF 2.x.


exemple de base

pour répondre directement à votre question, il suffit d'utiliser <f:selectItems> dont value pointe vers un List<T> propriété que vous préservez de la DB pendant la construction de bean (post). Voici un exemple de coup d'envoi de base en supposant que T représente en fait un String .

<h:selectOneMenu value="#{bean.name}">
    <f:selectItems value="#{bean.names}" />
</h:selectOneMenu>

avec

@ManagedBean
@RequestScoped
public class Bean {

    private String name;
    private List<String> names; 

    @EJB
    private NameService nameService;

    @PostConstruct
    public void init() {
        names = nameService.list();
    }

    // ... (getters, setters, etc)
}

aussi Simple que ça. En fait, le T 's toString() sera utilisé pour représenter à la fois l'étiquette de l'article et la valeur. Donc, quand vous êtes à la place de List<String> en utilisant une liste d'objets complexes comme List<SomeEntity> et vous n'avez pas dépassé la méthode de la classe toString() , alors vous verriez com.example.SomeEntity@hashcode comme valeurs d'item. Voir la section suivante comment le résoudre correctement.

il est également à noter que le haricot pour la valeur <f:selectItems> ne doit pas nécessairement être le même haricot que le haricot pour la valeur <h:selectOneMenu> . Ceci est utile lorsque les valeurs sont en fait des constantes générales de l'application que vous n'avez qu'à charger une seule fois pendant le démarrage de l'application. Vous pourriez juste faire une propriété d'une application étendue de haricot.

<h:selectOneMenu value="#{bean.name}">
    <f:selectItems value="#{data.names}" />
</h:selectOneMenu>

objets complexes-articles disponibles

chaque fois que T concerne un objet complexe (un javabean), tel que User qui a une propriété String de name , alors vous pouvez utiliser l'attribut var pour mettre la main sur la variable d'itération que vous pouvez à votre tour utiliser dans itemValue et/ou itemLabel attributions (si vous omettez le itemLabel , l'étiquette devient la même que la valeur).

exemple #1:

<h:selectOneMenu value="#{bean.userName}">
    <f:selectItems value="#{bean.users}" var="user" itemValue="#{user.name}" />
</h:selectOneMenu>

avec

private String userName;
private List<User> users;

@EJB
private UserService userService;

@PostConstruct
public void init() {
    users = userService.list();
}

// ... (getters, setters, etc)

ou quand il a un Long propriété id que vous aimeriez plutôt Définir comme valeur de l'article:

exemple #2:

<h:selectOneMenu value="#{bean.userId}">
    <f:selectItems value="#{bean.users}" var="user" itemValue="#{user.id}" itemLabel="#{user.name}" />
</h:selectOneMenu>

avec

private Long userId;
private List<User> users;

// ... (the same as in previous bean example)

objet complexe sélectionné point

chaque fois que vous souhaitez le définir à une T propriété dans la fève ainsi et T représente un User , alors vous auriez besoin de cuire une coutume Converter qui convertit entre User et une représentation de chaîne unique (qui peut être la propriété id ). Notez que le itemValue doit représenter l'objet complexe lui-même, exactement le type qui doit être défini comme sélection composant value .

<h:selectOneMenu value="#{bean.user}" converter="#{userConverter}">
    <f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>

avec

private User user;
private List<User> users;

// ... (the same as in previous bean example)

et

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

}

(veuillez noter que le Converter est un peu hacky afin de pouvoir injecter un @EJB dans un convertisseur JSF; normalement on l'aurait annoté @FacesConverter(forClass=User.class) , mais cela ne permet malheureusement pas @EJB injections ) "15191040920

N'oubliez pas de vous assurer que la classe de l'objet complexe a equals() et hashCode() correctement implémenté , sinon JSF ne montrera pas les éléments présélectionnés lors du rendu, et vous serez sur submit face erreur de Validation: la valeur n'est pas valide .

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

}

des objets Complexes avec un convertisseur générique

tête à cette réponse: mettre en œuvre convertisseurs pour les entités avec des génériques Java .


objets complexes sans convertisseur personnalisé

la bibliothèque utilitaire JSF OmniFaces offre un convertisseur spécial sur la boîte qui vous permet d'utiliser des objets complexes dans <h:selectOneMenu> sans la nécessité de créer un convertisseur personnalisé. Le SelectItemsConverter fera simplement la conversion basée sur facilement disponible articles dans <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>

Voir aussi:

165
répondu BalusC 2017-05-23 11:47:31

Afficher La Page

<h:selectOneMenu id="selectOneCB" value="#{page.selectedName}">
     <f:selectItems value="#{page.names}"/>
</h:selectOneMenu>

Back-Bean

   List<SelectItem> names = new ArrayList<SelectItem>();

   //-- Populate list from database

   names.add(new SelectItem(valueObject,"label"));

   //-- setter/getter accessor methods for list

pour afficher un enregistrement sélectionné particulier, il doit s'agir d'une des valeurs de la liste.

8
répondu Nayan Wadekar 2011-07-27 20:12:16

Roll-votre-propre convertisseur générique pour des objets complexes comme élément sélectionné

le Balusc donne une réponse d'ensemble très utile à ce sujet. Mais il y a une alternative qu'il ne présente pas: le convertisseur Générique Roll-your-own qui gère les objets complexes comme l'élément sélectionné. C'est très complexe à faire si vous voulez traiter l'ensemble des cas, mais assez simple pour les cas simples.

le code ci-dessous contient un exemple d'un tel convertisseur. Il travaille dans le même esprit que les OmniFaces SelectItemsConverter en regardant à travers les enfants d'un composant pour UISelectItem(s) contenant des objets. La différence est qu'il ne manipule que des fixations soit à des collections simples d'objets d'entité, soit à des chaînes. Il ne traite pas les groupes d'articles, collections de SelectItem s, tableaux et probablement beaucoup d'autres choses.

les entités que le composant oblige à mettre en œuvre le IdObject interface. (Ceci pourrait être résolu d'une autre manière, par exemple en utilisant toString .)

notez que les entités doivent implémenter equals de telle manière que deux entités ayant la même ID soient égales.

la seule chose que vous devez faire pour l'utiliser est de le spécifier comme convertisseur sur le composant select, lier à une propriété d'entité et une liste d'entités possibles:

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

convertisseur:

/**
 * 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");
  }

}
3
répondu Lii 2014-10-18 12:06:01

je le fais comme ça:

  1. les modèles sont vus

  2. convertisseur:

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

et lier au composant avec:

 <f:converter binding="#{viewScopedFacesConverter}" />

si vous utilisez l'ID entité plutôt que le hashCode, vous pouvez frapper une collision - si vous avez quelques listes sur une page pour différentes entités (classes) avec le même id

0
répondu 2017-10-03 16:29:36

Appelez-moi paresseux mais coder un convertisseur semble être beaucoup de travail inutile. J'utilise Primefaces et, n'ayant pas utilisé auparavant une simple liste JSF2 ou un menu déroulant, j'ai juste supposé (en étant paresseux) que le widget pouvait gérer des objets complexes, c'est-à-dire passer l'objet sélectionné tel quel à son getter/setter correspondant comme tant d'autres widgets le font. J'ai été déçu de constater (après des heures de décapage) que cette capacité n'existe pas pour ce type de widget sans convertisseur. En fait, si vous fournissez un setter pour l'objet complexe plutôt que pour une chaîne, il échoue silencieusement (il n'appelle simplement pas le setter, pas D'Exception, pas d'erreur JS), et j'ai passé une tonne de temps à parcourir l'excellent outil de dépannage de BalusC pour trouver la cause, en vain car aucune de ces suggestions appliquées. Ma conclusion: le widget listbox/menu doit être adapté pour que les autres widgets JSF2 ne le soient pas. Cela semble trompeur et enclin à mener le développeur non informé comme moi en bas d'un trou de lapin.

à la fin j'ai résisté au codage d'un convertisseur et j'ai trouvé par essai et erreur que si vous définissez la valeur du widget à un objet complexe, par exemple:

<p:selectOneListbox id="adminEvents" value="#{testBean.selectedEvent}">

... lorsque l'utilisateur sélectionne un élément, le widget peut appeler un setter de corde pour cet objet, par exemple setSelectedThing(String thingString) {...} , et la corde passée est une corde de JSON représentant l'objet de chose. Je peux l'analyser pour déterminer quel objet a été sélectionné. Ça ressemble un peu à un piratage, mais moins un hack qu'un Convertisseur.

0
répondu snakedog 2017-10-24 15:29:13