Como posso passar a linha selecionada para commandLink dentro de dataTable ou ui: repeat?


99

Estou usando Primefaces em um aplicativo JSF 2. Eu tenho um <p:dataTable>e, em vez de selecionar linhas, quero que o usuário possa executar diretamente várias ações em linhas individuais. Para isso, tenho vários <p:commandLink>s na última coluna.

Meu problema: como posso passar um ID de linha para a ação iniciada pelo link de comando para que eu saiba em qual linha atuar? Tentei usar um <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>

Mas sempre resulta em 0 - aparentemente, a variável de linha fnão está disponível quando o atributo é renderizado (funciona quando eu uso um valor fixo).

Alguém tem uma solução alternativa?

Respostas:


215

Quanto à causa, <f:attribute>é específico para o próprio componente (preenchido durante o tempo de construção da visualização), não para a linha iterada (preenchida durante o tempo de renderização da visualização).

Existem várias maneiras de atingir o requisito.

  1. Se o seu servletcontainer suporta um mínimo de Servlet 3.0 / EL 2.2, apenas passe-o como um argumento do método de ação / ouvinte do UICommand componente ou AjaxBehaviortag. Por exemplo

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

    Em combinação com:

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

    Isso requer apenas que o modelo de dados seja preservado para a solicitação de envio do formulário. O melhor é colocar o bean no escopo de visualização@ViewScoped .

    Você pode até mesmo passar todo o objeto do item:

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

    com:

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

    Em contêineres Servlet 2.5, isso também é possível se você fornecer uma implementação EL que suporte isso, como JBoss EL. Para detalhes de configuração, veja esta resposta .


  2. Use <f:param>no UICommandcomponente. Ele adiciona um parâmetro de solicitação.

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

    Se o seu bean tiver escopo de solicitação, deixe o JSF configurá-lo por @ManagedProperty

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

    Ou se o seu bean tem um escopo mais amplo ou se você deseja uma validação / conversão mais refinada, use <f:viewParam>na visualização de destino, consulte também f: viewParam vs @ManagedProperty :

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

    De qualquer forma, isso tem a vantagem de que o modelo de dados não precisa necessariamente ser preservado para o envio do formulário (para o caso de seu bean ter escopo de solicitação).


  3. Use <f:setPropertyActionListener>no UICommandcomponente. A vantagem é que isso elimina a necessidade de acessar o mapa de parâmetros da solicitação quando o bean tem um escopo mais amplo do que o escopo da solicitação.

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

    Em combinação com

     private Long id; // +setter

    Estará disponível apenas por propriedade idno método de ação. Isso requer apenas que o modelo de dados seja preservado para a solicitação de envio do formulário. O melhor é colocar o bean no escopo de visualização por @ViewScoped.


  4. Vincule o valor da tabela de dados a, DataModel<E>que por sua vez envolve os itens.

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

    com

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

    (torná-lo transiente instanciá-lo preguiçosamente no getter é obrigatório quando você está usando isso em uma visão ou bean com escopo de sessão, uma vez DataModelque não implementaSerializable )

    Em seguida, você poderá acessar a linha atual DataModel#getRowData()sem passar nada (o JSF determina a linha com base no nome do parâmetro de solicitação do link / botão de comando clicado).

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

    Isso também requer que o modelo de dados seja preservado para a solicitação de envio do formulário. O melhor é colocar o bean no escopo de visualização por @ViewScoped.


  5. Use Application#evaluateExpressionGet()para avaliar programaticamente o atual #{item}.

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

O caminho a ser escolhido depende dos requisitos funcionais e se um ou outro oferece mais vantagens para outros fins. Eu pessoalmente iria em frente com o # 1 ou, quando você gostaria de oferecer suporte a containers servlet 2.5 também, com o # 2.


1
+1, embora minha preferência vá para # 2 (se 2,5 tiver que ser suportado).
— Bozho

Obrigado pela resposta exaustiva. Infelizmente, tenho que relatar que # 1 foi a única coisa que funcionou em uma tabela de dados filtrada de faces primárias (que é exatamente o cenário para o qual preciso). Todos os outros trabalharam apenas em uma mesa não filtrada. Eu vejo isso mais como um bug em faces primárias do que em sua resposta, no entanto.
— Michael Borgwardt de

A solicitação ou visualização do bean tem escopo?
— BalusC de

2
Com "filtrado" você quer dizer como neste exemplo de demonstração ? Os sintomas indicam que a ação do filtro ocorre apenas no lado do cliente e que o modelo no lado do servidor não é mantido. Não tenho certeza se isso é intencional. Você sempre pode deixar um relatório de problema.
— BalusC de

Sua postagem está entre as mais úteis que já li. Usei o método 5 porque sou forçado a usar o servlet 2.5. Minha dúvida agora é se é possível enviar um parâmetro com commandLink (como no seu exemplo), mas usando ajax?
— Aditzu

11

No JSF 1.2, isso era feito por <f:setPropertyActionListener>(dentro do componente de comando). No JSF 2.0 (EL 2.2 para ser preciso, graças ao BalusC) é possível fazer assim:action="${filterList.insert(f.id)}


6
Este recurso não é específico do JSF 2.0 (que pode ser executado sozinho em contêineres Servlet 2.5), mas do EL 2.2 (que faz parte do Servlet 3.0).
— BalusC de

11

Na minha página de visualização:

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

feijão de apoio

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

-1

Graças a este site da Mkyong , a única solução que realmente funcionou para passarmos um parâmetro foi esta

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

com

public String editAction() {

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

Tecnicamente, você não pode passar diretamente para o método em si, mas para o JSF request parameter map.


1
Você tem um problema diferente do que perguntado aqui. Você deseja reter os parâmetros de solicitação do #{param}mapa para solicitações subsequentes, não para passar um parâmetro arbitrário. Suas perguntas e respostas são abordadas em stackoverflow.com/questions/17734230
— BalusC
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.