Передать параметр Bean на JSP [дубликат]

Инициализация двойной скобки создает анонимный класс, полученный из указанного класса (внешние фигурные скобки external ) и предоставляет блок инициализации внутри этого класса (скобки inner ). например,

new ArrayList<Integer>() {{
   add(1);
   add(2);
}};

Обратите внимание, что эффект от использования этой инициализации двойной скобки заключается в том, что вы создаете анонимные внутренние классы. Созданный класс имеет неявный указатель this к окружающему внешнему классу. Хотя обычно это не проблема, это может вызвать горе в некоторых обстоятельствах, например. при сборке или сборе мусора, и это стоит знать об этом.

65
задан BalusC 5 November 2015 в 13:52
поделиться

5 ответов

Основываясь на истории вопроса, вы используете JSF 2.x. Итак, вот целевой запрос JSF 2.x. В JSF 1.x вам придется переносить значения / метки элемента в уродливые экземпляры SelectItem . Это, к счастью, больше не требуется в JSF 2.x.


Основной пример

Чтобы ответить на ваш вопрос напрямую, просто используйте <f:selectItems> value указывает на свойство List<T>, которое вы сохраняете из БД во время построения столбца (столбца). Вот пример базового запуска, предполагающий, что T фактически представляет String.

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

с

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

Простой. Фактически, T 's toString() будет использоваться для представления как ярлыка выпадающего элемента, так и значения. Итак, когда вы вместо List<String> используете список сложных объектов, таких как List<SomeEntity>, и вы не переопределили метод класса toString(), вы увидите com.example.SomeEntity@hashcode как значения элемента. См. Следующий раздел, как правильно его решить.

Также обратите внимание, что bean для значения <f:selectItems> необязательно должен быть одним и тем же компонентом, что и bean для значения <h:selectOneMenu>. Это полезно, когда значения фактически являются общими константами, которые вам просто нужно загружать только один раз во время запуска приложения. Тогда вы могли бы просто сделать это свойством компонента с областью приложения.

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

Сложные объекты как доступные элементы

Всякий раз, когда T относится к сложному объекту (javabean ), например User, который имеет свойство String в name, тогда вы можете использовать атрибут var для получения переменной итерации, которую вы, в свою очередь, можете использовать в itemValue и / или itemLabel ] присваивает (если вы опускаете itemLabel, тогда метка становится той же, что и значение).

Пример # 1:

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

с

private String userName;
private List<User> users;

@EJB
private UserService userService;

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

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

Или когда у него есть свойство Long, которое вы хотели бы задать как значение элемента:

Пример # 2:

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

с

private Long userId;
private List<User> users;

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

Сложный объект как выделенный элемент

Всякий раз, когда вы хотите установить его в свойство T в bean-а также, а T представляет User ], вам нужно будет испечь пользовательский Converter , который преобразует между User и уникальным строковым представлением (которое может быть свойством id). Обратите внимание, что itemValue должен представлять сам сложный объект, именно тот тип, который необходимо установить в качестве компонента выделения value.

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

с

private User user;
private List<User> users;

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

и

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

}

(обратите внимание, что Converter немного взломан, чтобы иметь возможность вставлять @EJB в конвертер JSF, обычно он должен был бы аннотировать его как @FacesConverter(forClass=User.class), , но, к сожалению, не позволяет @EJB инъекции )

Не забудьте убедиться, что класс сложного объекта имеет equals() и hashCode() должным образом реализованы , в противном случае JSF во время рендеринга не сможет отобразить предварительно выбранный элемент (ы), и вы отправите face Ошибка проверки: значение недействительно .

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

}

Сложные объекты с общим конвертером

Голосовать до этого ответа: Реализовывать преобразователи для объектов с Java Generics .

< hr>

Сложные объекты без пользовательского конвертера

Библиотека утилиты JSF OmniFaces предлагает специальный конвертер из окна, который позволяет y ou использовать сложные объекты в <h:selectOneMenu> без необходимости создания настраиваемого конвертера. SelectItemsConverter просто выполнит преобразование на основе легко доступных элементов в <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>

См. Также:

163
ответ дан Community 19 August 2018 в 13:02
поделиться
  • 1
    Очень хорошее объяснение! Преобразователи, не являющиеся целями инъекции, являются сложными в JSF. Я полагаю, что портативное расширение шва 3 исправит это. Это также касается дорожной карты для JSF 2.2 / Java EE 7. – Mike Braun 31 July 2011 в 15:40
  • 2
    +10 очень приятное объяснение – sns 16 November 2012 в 12:30
  • 3
    @Makky: Я понятия не имею, о чем вы говорите. Я нигде явно не инициализировал список сам. Этот пример предполагает стандартный стек Java EE с EJB и JPA. По-видимому, это не так. Нажмите Ask Question, чтобы задать четкий и конкретный вопрос. – BalusC 17 April 2013 в 15:20
  • 4
    Привет BalusC Я поднял вопрос, пожалуйста, пожалуйста, помогите stackoverflow.com/questions/16062703/… – Makky 17 April 2013 в 15:28
  • 5
    Раздел @ZiMtyth Чтение «Комплексный объект как выбранный элемент» сверху вниз. Для будущего "ничего не происходит" проблемы, перейдите к stackoverflow.com/q/2118656 – BalusC 5 September 2017 в 10:07

Создайте собственный универсальный конвертер для сложных объектов в качестве выбранного элемента

. Balusc дает очень полезный обзорный обзор по этому вопросу. Но есть одна альтернатива, которую он не представляет: Roll-your-общий универсальный конвертер, который обрабатывает сложные объекты как выбранный элемент. Это очень сложно сделать, если вы хотите обрабатывать все случаи, но довольно просто для простых случаев.

В приведенном ниже коде содержится пример такого конвертера. Он работает в том же духе, что и OmniFaces SelectItemsConverter , поскольку он просматривает дочерние элементы компонента для объектов, содержащихся в UISelectItem(s). Разница в том, что он обрабатывает привязки только к простым коллекциям объектов сущности или к строкам. Он не обрабатывает группы элементов, коллекции SelectItem s, массивы и, возможно, много других вещей.

Сущности, к которым должен привязываться компонент, должны реализовать интерфейс IdObject. (Это можно решить другим способом, например, с помощью toString.)

Обратите внимание, что сущности должны реализовать equals таким образом, чтобы два объекта с одинаковым идентификатором сравнивались равными.

Единственное, что вам нужно сделать, чтобы использовать его, - это указать его как конвертер на компоненте select, привязать его к свойству сущности и списку возможных объектов:

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

Конвертер:

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

}
2
ответ дан Lii 19 August 2018 в 13:02
поделиться

View-Page

<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

Чтобы отобразить определенную выбранную запись, это должно быть одно из значений в списке.

7
ответ дан Nayan Wadekar 19 August 2018 в 13:02
поделиться
  • 1
    Что такое valueObject в вашем коде? это список записей, которые будут указаны в выпадающем списке? – Illep 28 July 2011 в 06:45
  • 2
    Его объект, который будет установлен в компоненте при выборе одного из параметров (здесь selectedName). value (valueObject) может представлять собой некоторый идентификатор, который вы хотите использовать в фоновом режиме, но должен отображать имя на экране с помощью метки. – Nayan Wadekar 28 July 2011 в 18:56

Назовите меня ленивым, но кодирование конвертера похоже на много ненужной работы. Я использую Primefaces и, не использовав простой список валиний JSF2 или выпадающее меню раньше, я просто предположил (ленивый), что виджет может обрабатывать сложные объекты, т. Е. Передавать выбранный объект так же, как и его соответствующий геттер / сеттер многие другие виджеты. Я был разочарован тем, что (после нескольких часов царапин на голове) эта возможность не существует для этого типа виджетов без конвертера. На самом деле, если вы поставляете сеттер для сложного объекта, а не для строки, он терпит неудачу (просто не вызывает сеттер, исключение, отсутствие ошибки JS), и я потратил массу времени, пройдя через BalusC отличный инструмент устранения неполадок , чтобы найти причину, безрезультатно, поскольку ни одно из этих предложений не применялось. Мой вывод: виджет listbox / menu нуждается в адаптации к тому, что другие виджеты JSF2 этого не делают.

В конце концов я сопротивлялся кодированию конвертера и обнаружил через пробную версию и ошибку, что, если вы установите значение виджета на сложный объект, например:

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

... когда пользователь выбирает элемент, виджет может вызывать установщик String для этого объекта, например setSelectedThing(String thingString) {...}, а переданная строка - это строка JSON, представляющая объект Thing. Я могу проанализировать его, чтобы определить, какой объект был выбран. Это немного похоже на хак, но меньше на взломе, чем на конвертер.

0
ответ дан snakedog 19 August 2018 в 13:02
поделиться
  • 1
    Что вы понимаете под & quot; Заключение: виджет списка / меню нуждается в адаптации, чем другие виджеты JSF2. & Quot; ? Тот факт, что ему нужны конвертеры? Простая h:inputText делает тоже, если вы создали свои собственные строго типизированные объекты вместо использования строки. – Kukeltje 24 October 2017 в 15:00
  • 2
    Я имею в виду, что другие виджеты могут обрабатывать (получать переданные и заданные на бэк-компоненте) сложные объекты, в то время как список / меню не может. Подумав об этом, String на самом деле является сложным объектом, поэтому можно подумать, что эти виджеты смогут обрабатывать любой сложный объект. Мне кажется, что h:inputText, или даже его более изощренный брат p:inputText обрабатывают только строки по своей природе. listbox / menu кажется, что он должен иметь возможность обрабатывать любой объект, хотя, конечно, этот объект может быть представлен только строкой в ​​пользовательском интерфейсе. – snakedog 24 October 2017 в 15:14
  • 3
    h:inputText и p:inputText могут обрабатывать номера тоже и многое другое. Это связано с тем, что они основаны на известных типах java и преобразователях предоставляются jsf и неявно применяются. Для любого другого типа ему также нужен конвертер, например. пользовательский строго типизированный GTIN , который фактически является числом (в этой реализации они используют String). И да, преобразование в и из представления «строка на стороне клиента» - это то, что преобразователи заботятся ... – Kukeltje 24 October 2017 в 16:34
  • 4
    То, что вы кодировали, звучит как сериализатор / десериализатор, который в конце концов выступает в качестве конвертера (который может быть даже подвержен обработке на стороне клиента). Я подозреваю, что вы либо закодировали это в сущности, либо в бэк-компоненте (контроллере), оба из которых не должны знать об этой «конвертирующей вещи» между клиентом и сервером, поэтому это звучит для меня скорее как взлома. Тем более, что Omnifaces имеет такие вещи, как showcase.omnifaces.org/converters/SelectItemsConverter . И имейте в виду, что если у вас есть меню выбора, которое работает с списками строк, вы также используете встроенные преобразователи. – Kukeltje 24 October 2017 в 17:19
  • 5
    Это совершенно другая перспектива, спасибо. Будучи пользователем Primefaces, я ожидал, что эти виджеты будут «просто работать». как и другие виджеты, которые я использовал. Я не знал, что некоторые виджеты JSF имеют встроенные преобразователи, а некоторые нет. В будущем я буду рассматривать встроенные преобразователи как удобство, которое обеспечивает мне инфраструктура, а не ожидаемая функция. Тем не менее, я не знаю, что предоставление преобразования в getter / setter существенно отличается от предоставления его в отдельном классе, и это кажется более простым. – snakedog 24 October 2017 в 17:54

Я делаю это так:

  1. Модели - это ViewScoped
  2. конвертер:
    @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);
            }
    }
    

и привязка к Компонент с:

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

Если вы будете использовать идентификатор объекта, а не hashCode, вы можете столкнуться с конфликтом - если у вас мало списков на одной странице для разных объектов (классов) с тем же идентификатором

0
ответ дан user 19 August 2018 в 13:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: