Qual é a diferença entre action
and actionListener
, e quando devo usar action
versus actionListener
?
Qual é a diferença entre action
and actionListener
, e quando devo usar action
versus actionListener
?
Respostas:
Use actionListener
se você deseja obter um gancho antes que a ação real do negócio seja executada, por exemplo, registrá-la e / ou definir uma propriedade adicional (por <f:setPropertyActionListener>
) e / ou ter acesso ao componente que chamou a ação (disponível por ActionEvent
argumento). Portanto, puramente para fins de preparação antes que a ação real do negócio seja invocada.
O actionListener
método possui por padrão a seguinte assinatura:
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
E deve ser declarado da seguinte forma, sem nenhum parênteses de método:
<h:commandXxx ... actionListener="#{bean.actionListener}" />
Observe que você não pode passar argumentos adicionais pelo EL 2.2. No entanto, você pode substituir ActionEvent
completamente o argumento passando e especificando argumentos personalizados. Os seguintes exemplos são válidos:
<h:commandXxx ... actionListener="#{bean.methodWithoutArguments()}" />
<h:commandXxx ... actionListener="#{bean.methodWithOneArgument(arg1)}" />
<h:commandXxx ... actionListener="#{bean.methodWithTwoArguments(arg1, arg2)}" />
public void methodWithoutArguments() {}
public void methodWithOneArgument(Object arg1) {}
public void methodWithTwoArguments(Object arg1, Object arg2) {}
Observe a importância dos parênteses na expressão do método sem argumento. Se eles estivessem ausentes, o JSF ainda esperaria um método com ActionEvent
argumento.
Se você usa o EL 2.2+, pode declarar vários métodos de ouvinte de ação por meio de <f:actionListener binding>
.
<h:commandXxx ... actionListener="#{bean.actionListener1}">
<f:actionListener binding="#{bean.actionListener2()}" />
<f:actionListener binding="#{bean.actionListener3()}" />
</h:commandXxx>
public void actionListener1(ActionEvent event) {}
public void actionListener2() {}
public void actionListener3() {}
Observe a importância dos parênteses no binding
atributo Se eles estivessem ausentes, o EL confundiria um a javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
, porque o binding
atributo é por padrão interpretado como uma expressão de valor, não como uma expressão de método. A adição de parênteses do estilo EL 2.2+ transforma transparentemente uma expressão de valor em uma expressão de método. Consulte também ao Por que eu sou capaz de vincular <f: actionListener> a um método arbitrário se ele não é suportado pelo JSF?
Use action
se você deseja executar uma ação comercial e, se necessário, manipular a navegação. O action
método pode (portanto, não deve) retornar um String
que será usado como resultado do caso de navegação (a exibição de destino). Um valor de retorno null
ou void
permitirá retornar à mesma página e manter vivo o escopo da visualização atual. Um valor de retorno de uma cadeia vazia ou o mesmo ID da visualização também retornará à mesma página, mas recriará o escopo da visualização e, portanto, destruirá quaisquer beans com escopo da visualização atualmente ativos e, se aplicável, os recriará.
O action
método pode ser válido MethodExpression
, também aquele que usa argumentos do EL 2.2, como abaixo:
<h:commandXxx value="submit" action="#{bean.edit(item)}" />
Com este método:
public void edit(Item item) {
// ...
}
Observe que quando seu método de ação retorna apenas uma sequência, você também pode especificar exatamente essa sequência no action
atributo Portanto, isso é totalmente desajeitado:
<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
Com esse método sem sentido, retornando uma string codificada:
public String goToNextpage() {
return "nextpage";
}
Em vez disso, basta colocar essa string codificada diretamente no atributo:
<h:commandLink value="Go to next page" action="nextpage" />
Observe que isso, por sua vez, indica um design incorreto: navegar pelo POST. Este não é um usuário nem um SEO amigável. Isso tudo é explicado em Quando devo usar h: outputLink em vez de h: commandLink? e deve ser resolvido como
<h:link value="Go to next page" outcome="nextpage" />
Veja também Como navegar no JSF? Como fazer o URL refletir a página atual (e não a anterior) .
Desde o JSF 2.x existe uma terceira maneira, o <f:ajax listener>
.
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
O ajaxListener
método possui por padrão a seguinte assinatura:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
Em Mojarra, o AjaxBehaviorEvent
argumento é opcional, abaixo funciona tão bem.
public void ajaxListener() {
// ...
}
Mas no MyFaces, seria lançado a MethodNotFoundException
. Abaixo, funciona nas duas implementações JSF quando você deseja omitir o argumento.
<h:commandXxx ...>
<f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>
Os ouvintes do Ajax não são realmente úteis nos componentes de comando. Eles são mais úteis na entrada e selecione componentes <h:inputXxx>
/ <h:selectXxx>
. Nos componentes de comando, apenas atenha-se ae action
/ ou actionListener
para maior clareza e melhor código de auto-documentação. Além disso, actionListener
o f:ajax listener
não suporta o retorno de um resultado de navegação.
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
Para obter explicações execute
e render
atributos, vá para Noções básicas sobre processo / atualização do PrimeFaces e atributos de execução / renderização do JSF f: ajax .
Os actionListener
sempre são invocados antes doaction
na mesma ordem em que foram declarados na visualização e anexados ao componente. O f:ajax listener
sempre é chamado antes de qualquer ouvinte de ação. Então, o seguinte exemplo:
<h:commandButton value="submit" actionListener="#{bean.actionListener}" action="#{bean.action}">
<f:actionListener type="com.example.ActionListenerType" />
<f:actionListener binding="#{bean.actionListenerBinding()}" />
<f:setPropertyActionListener target="#{bean.property}" value="some" />
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandButton>
Irá invocar os métodos na seguinte ordem:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
O actionListener
suporta uma exceção especial:AbortProcessingException
. Se essa exceção for lançada de um actionListener
método, o JSF ignorará todos os listeners de ação restantes e o método de ação e continuará a renderizar a resposta diretamente. Você não verá uma página de erro / exceção, mas o JSF a registrará. Isso também será implicitamente feito sempre que qualquer outra exceção for lançada de um actionListener
. Portanto, se você pretende bloquear a página por uma página de erro como resultado de uma exceção comercial, você definitivamente deve executar o trabalho no action
método
Se o único motivo para usar um actionListener
é ter um void
método retornando à mesma página, isso é ruim. Os action
métodos também podem retornar perfeitamente void
, ao contrário do que alguns IDEs permitem que você acredite através da validação EL. Observe que o exemplos de apresentação PrimeFaces estão repletos de esse tipo de actionListener
s em todos os lugares. Isso está realmente errado. Não use isso como desculpa para também fazer isso sozinho.
Em solicitações ajax, no entanto, é necessário um manipulador de exceção especial. Isso independentemente de você usar ou não o listener
atributo <f:ajax>
. Para obter explicações e um exemplo, vá para Manipulação de exceções em solicitações JSF ajax .
actionListener
, mas isso ainda não é uma boa desculpa para abusar actionListener
de ações de negócios .
action
corresponde a isso. actionListener
é para coisas secundárias. Apenas queria esclarecer que as exceções de actionListener
s podem ser propagadas se assim for exigido;)
actionListener
atributo e também deve ser public
. O processAction
nome é obrigatório apenas quando você está usando <f:actionListener type>
, simplesmente porque o tipo precisa implementar uma ActionListener
interface que tenha exatamente esse nome de método processAction
definido.
<f:ajax>
, no caso de componentes de comando, você prefere usar o action
atributo para ações de negócios. Por exemplo <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
.
Como o BalusC indicou, o actionListener
padrão engole exceções, mas no JSF 2.0 há um pouco mais disso. Ou seja, ele não apenas engole e registra, mas na verdade publica a exceção.
Isso acontece através de uma chamada como esta:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
O ouvinte padrão para este evento é o ExceptionHandler
que para Mojarra está definido comocom.sun.faces.context.ExceptionHandlerImpl
. Essa implementação basicamente relançará qualquer exceção, exceto quando se trata de uma AbortProcessingException, que é registrada. Os ActionListeners quebram a exceção lançada pelo código do cliente em uma AbortProcessingException, que explica por que eles sempre são registrados.
No ExceptionHandler
entanto, isso pode ser substituído no faces-config.xml com uma implementação customizada:
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
Em vez de ouvir globalmente, um único bean também pode ouvir esses eventos. A seguir, é uma prova de conceito disso:
@ManagedBean
@RequestScoped
public class MyBean {
public void actionMethod(ActionEvent event) {
FacesContext.getCurrentInstance().getApplication().subscribeToEvent(ExceptionQueuedEvent.class, new SystemEventListener() {
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
ExceptionQueuedEventContext content = (ExceptionQueuedEventContext)event.getSource();
throw new RuntimeException(content.getException());
}
@Override
public boolean isListenerForSource(Object source) {
return true;
}
});
throw new RuntimeException("test");
}
}
(note que não é assim que normalmente se deve codificar ouvintes, isso é apenas para fins de demonstração!)
Chamando isso de um Facelet assim:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:body>
<h:form>
<h:commandButton value="test" actionListener="#{myBean.actionMethod}"/>
</h:form>
</h:body>
</html>
Resultará na exibição de uma página de erro.
O ActionListener é acionado primeiro, com uma opção para modificar a resposta, antes que o Action seja chamado e determine o local da próxima página.
Se houver vários botões na mesma página que devem ir para o mesmo lugar, mas executar ações ligeiramente diferentes, você poderá usar a mesma ação para cada botão, mas usar um ActionListener diferente para lidar com funcionalidades ligeiramente diferentes.
Aqui está um link que descreve o relacionamento:
TL; DR :
Os ActionListener
s (podem existir múltiplos) são executados na ordem em que foram registrados ANTES doaction
Resposta longa :
Um negócio action
normalmente invoca um serviço EJB e, se necessário também define o resultado final e / ou navega para uma vista diferente se isso não é o que você está fazendo um actionListener
é isto é mais apropriado para quando o usuário interage com os componentes, tais como h:commandButton
ou h:link
o que podem seja tratado passando o nome do método do bean gerenciado no actionListener
atributo de um Componente da UI ou para implementar uma ActionListener
interface e passe o nome da classe de implementação ao actionListener
atributo de um Componente da UI.