Как передать выбранную строку в commandLink внутри dataTable или ui: repeat?

Я использую Primefaces в приложении JSF 2. У меня есть <p:dataTable>, и вместо выбора строк я хочу, чтобы пользователь мог напрямую выполнять различные действия с отдельными строками. Для этого у меня есть несколько <p:commandLink> в последнем столбце.

Моя проблема: как передать идентификатор строки действию, запускаемому ссылкой на команду, чтобы я знал, с какой строкой действовать? Я пробовал использовать <f:attribute>:

<p:dataTable value="#{bean.items}" var="item">
    ...
    <p:column>
        <p:commandLink actionListener="#{bean.insert}" value="insert">
            <f:attribute name="id" value="#{item.id}" />
        </p:commandLink>
    </p:column>
</p:dataTable>

Но он всегда дает 0 - очевидно, строковая переменная f недоступна при рендеринге атрибута (работает, когда я использую фиксированное значение).

У кого-нибудь есть альтернативное решение?


person Michael Borgwardt    schedule 14.02.2011    source источник


Ответы (4)


Что касается причины, <f:attribute> относится к самому компоненту (заполняется во время построения представления), а не к повторяющейся строке (заполняется во время визуализации представления).

Есть несколько способов выполнить это требование.

  1. Если ваш servletcontainer поддерживает как минимум Servlet 3.0 / EL 2.2, то просто передайте его как аргумент метода действия / слушателя компонента UICommand или тега AjaxBehavior. Например.

     <h:commandLink action="#{bean.insert(item.id)}" value="insert" />
    

    В комбинации с:

     public void insert(Long id) {
         // ...
     }
    

    Это требует только, чтобы модель данных сохранялась для запроса на отправку формы. Лучше всего поместить компонент в область просмотра с помощью @ViewScoped.

    Вы даже можете передать объект элемента целиком:

     <h:commandLink action="#{bean.insert(item)}" value="insert" />
    

    с участием:

     public void insert(Item item) {
         // ...
     }
    

    В контейнерах Servlet 2.5 это также возможно, если вы предоставите реализацию EL, которая поддерживает это, например, JBoss EL. Подробные сведения о конфигурации см. В этом ответе.


  2. Используйте <f:param> в UICommand компоненте. Он добавляет параметр запроса.

     <h:commandLink action="#{bean.insert}" value="insert">
         <f:param name="id" value="#{item.id}" />
     </h:commandLink>
    

    Если ваш bean-компонент имеет область действия запроса, разрешите JSF установить его с помощью @ManagedProperty

     @ManagedProperty(value="#{param.id}")
     private Long id; // +setter
    

    Или, если ваш bean-компонент имеет более широкую область применения или вам нужна более детальная проверка / преобразование, используйте _ 14_ в целевом представлении, см. также f: viewParam vs @ManagedProperty:

     <f:viewParam name="id" value="#{bean.id}" required="true" />
    

    В любом случае это имеет то преимущество, что модель данных не обязательно должна быть сохранена для отправки формы (для случая, когда ваш bean-компонент имеет область действия запроса).


  3. Используйте <f:setPropertyActionListener> в UICommand компоненте. Преимущество состоит в том, что это устраняет необходимость доступа к карте параметров запроса, когда компонент имеет более широкую область действия, чем область действия запроса.

     <h:commandLink action="#{bean.insert}" value="insert">
         <f:setPropertyActionListener target="#{bean.id}" value="#{item.id}" />
     </h:commandLink>
    

    В комбинации с

     private Long id; // +setter
    

    Он будет доступен только свойством id в методе действия. Это требует только, чтобы модель данных сохранялась для запроса на отправку формы. Лучше всего поместить компонент в область просмотра до @ViewScoped.


  4. Привяжите значение данных к DataModel<E> вместо этого, в свою очередь заворачивает предметы.

     <h:dataTable value="#{bean.model}" var="item">
    

    с участием

     private transient DataModel<Item> model;
    
     public DataModel<Item> getModel() {
         if (model == null) {
             model = new ListDataModel<Item>(items);
         }
         return model;
     }
    

    (создание transient и ленивое создание экземпляра в получателе является обязательным, если вы используете это в bean-компоненте с областью видимости или сеанса, поскольку DataModel не реализует Serializable)

    Затем вы сможете получить доступ к текущей строке с помощью _ 28_, ничего не передавая (JSF определяет строку на основе имени параметра запроса для выбранной ссылки / кнопки команды).

     public void insert() {
         Item item = model.getRowData();
         Long id = item.getId();
         // ...
     }
    

    Это также требует, чтобы модель данных сохранялась для запроса на отправку формы. Лучше всего поместить компонент в область просмотра до @ViewScoped.


  5. Используйте _ 31_ для программной оценки текущего #{item}.

     public void insert() {
         FacesContext context = FacesContext.getCurrentInstance();
         Item item = context.getApplication().evaluateExpressionGet(context, "#{item}", Item.class);
         Long id = item.getId();
         // ...
     }
    

Какой способ выбрать, зависит от функциональных требований и от того, предлагает ли тот или другой больше преимуществ для других целей. Лично я бы выбрал №1 или, если вы хотите поддерживать контейнеры сервлета 2.5, с №2.

person BalusC    schedule 14.02.2011
comment
+1, хотя я предпочитаю №2 (если нужно поддерживать 2.5). - person Bozho; 14.02.2011
comment
Спасибо за исчерпывающий ответ. К сожалению, я должен сообщить, что №1 была единственной вещью, которая работала в отфильтрованной таблице данных с примерами (это как раз тот сценарий, для которого мне это нужно). Все остальные работали только на нефильтрованном столе. Я считаю это скорее ошибкой в ​​праймфейсах, чем вашим ответом. - person Michael Borgwardt; 15.02.2011
comment
Ограничен ли запрос или представление bean-компонента? - person BalusC; 15.02.2011
comment
Под фильтром вы имеете в виду, как в этом демонстрационном примере? Симптомы указывают на то, что действие фильтра выполняется только на стороне клиента, а модель на стороне сервера не поддерживается. Не уверен, намеренно ли это. Вы всегда можете оставить отчет о проблеме. - person BalusC; 15.02.2011
comment
Ваш пост находится между самыми полезными постами, которые я когда-либо читал. Я использовал метод 5, потому что я вынужден использовать сервлет 2.5. Теперь мой вопрос: можно ли отправить параметр с помощью commandLink (как в вашем примере), но с использованием ajax? - person Aditzu; 05.12.2014

В JSF 1.2 это делал <f:setPropertyActionListener> (в командном компоненте). В JSF 2.0 (точнее, EL 2.2, спасибо BalusC) это можно сделать так: action="${filterList.insert(f.id)}

person Bozho    schedule 14.02.2011
comment
Эта функция характерна не только для JSF 2.0 (который сам по себе может работать в контейнерах Servlet 2.5), но для EL 2.2 (который является частью Servlet 3.0). - person BalusC; 14.02.2011

На моей странице просмотра:

<p:dataTable  ...>
<p:column>
<p:commandLink actionListener="#{inquirySOController.viewDetail}" 
               process="@this" update=":mainform:dialog_content"
           oncomplete="dlg2.show()">
    <h:graphicImage library="images" name="view.png"/>
    <f:param name="trxNo" value="#{item.map['trxNo']}"/>
</p:commandLink>
</p:column>
</p:dataTable>

поддерживающий боб

 public void viewDetail(ActionEvent e) {

    String trxNo = getFacesContext().getRequestParameterMap().get("trxNo");

    for (DTO item : list) {
        if (item.get("trxNo").toString().equals(trxNo)) {
            System.out.println(trxNo);
            setSelectedItem(item);
            break;
        }
    }
}
person Arfan J    schedule 09.05.2011

Благодаря этому сайту от Mkyong, единственное решение, которое на самом деле помогло нам передать параметр, было это

<h:commandLink action="#{user.editAction}">
    <f:param name="myId" value="#{param.id}" />
</h:commandLink>

с участием

public String editAction() {

  Map<String,String> params = 
            FacesContext.getExternalContext().getRequestParameterMap();
  String idString = params.get("myId");
  long id = Long.parseLong(idString);
  ...
}

Технически, вы не можете напрямую перейти к самому методу, а к JSF request parameter map.

person EpicPandaForce    schedule 20.04.2015
comment
У вас другая проблема, чем здесь. Вы хотите сохранить параметры запроса из карты #{param} для последующих запросов, а не передавать произвольный параметр. На ваши вопросы и ответы отвечает stackoverflow.com/questions/17734230 - person BalusC; 21.04.2015