Quando usar valueChangeListener ou ouvinte f: ajax?

86

Qual é a diferença entre os dois trechos de código a seguir - com relação ao listenerposicionamento?

<h:selectOneMenu ...>
    <f:selectItems ... />
    <f:ajax listener="#{bean.listener}" />
</h:selectOneMenu>

e

<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
    <f:selectItems ... />
</h:selectOneMenu>
Danijel
fonte

Respostas:

182

O valueChangeListenerserá chamado apenas quando o formulário for enviado e o valor enviado for diferente do valor inicial. Portanto, não é invocado quando apenas o changeevento HTML DOM é disparado. Se desejar enviar o formulário durante o changeevento HTML DOM , será necessário adicionar outro <f:ajax/>sem ouvinte (!) Ao componente de entrada. Isso causará um envio de formulário que processa apenas o componente atual (como em execute="@this").

<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
    <f:selectItems ... />
    <f:ajax />
</h:selectOneMenu>

Ao usar em <f:ajax listener>vez de valueChangeListener, por padrão já seria executado durante o changeevento HTML DOM . Dentro dos UICommandcomponentes e dos componentes de entrada que representam uma caixa de seleção ou botão de opção, ele seria executado por padrão apenas durante o clickevento HTML DOM .

<h:selectOneMenu value="#{bean.value}">
    <f:selectItems ... />
    <f:ajax listener="#{bean.ajaxListener}" />
</h:selectOneMenu>

Outra grande diferença é que o valueChangeListenermétodo é invocado durante o final da PROCESS_VALIDATIONSfase. Nesse momento, o valor enviado ainda não foi atualizado no modelo. Portanto, você não pode obtê-lo apenas acessando a propriedade do bean que está vinculada ao componente de entrada value. Você precisa passar por isso ValueChangeEvent#getNewValue(). O valor antigo também está disponível por ValueChangeEvent#getOldValue().

public void changeListener(ValueChangeEvent event) {
    Object oldValue = event.getOldValue();
    Object newValue = event.getNewValue();
    // ...
}

O <f:ajax listener>método é invocado durante a INVOKE_APPLICATIONfase. Nesse momento, o valor enviado já está atualizado no modelo. Você pode obtê-lo acessando diretamente a propriedade do bean que está vinculada ao componente de entrada value.

private Object value; // +getter+setter.

public void ajaxListener(AjaxBehaviorEvent event) {
    System.out.println(value); // Look, (new) value is already set.
}

Além disso, se você precisar atualizar outra propriedade com base no valor enviado, haverá falha ao usar, valueChangeListenerpois a propriedade atualizada pode ser substituída pelo valor enviado durante a UPDATE_MODEL_VALUESfase subsequente . É exatamente por isso que você vê em aplicativos / tutoriais / recursos antigos do JSF 1.x que um valueChangeListenerestá em tal construção sendo usado em combinação immediate="true"e FacesContext#renderResponse()para evitar que isso aconteça. Afinal, usando ovalueChangeListener para executar ações de negócios sempre foi, na verdade, um hack / solução alternativa.

Resumido: Use o valueChangeListenerapenas se precisar interceptar a própria mudança de valor real. Ou seja, você está realmente interessado em tanto o velho eo novo valor (por exemplo, para registrá-los).

public void changeListener(ValueChangeEvent event) {
    changeLogger.log(event.getOldValue(), event.getNewValue());
}

Use <f:ajax listener>apenas se precisar executar uma ação comercial no valor recém-alterado. Ou seja, você está realmente interessado apenas no novo valor (por exemplo, para preencher um segundo menu suspenso).

public void ajaxListener(AjaxBehaviorEvent event) {
    selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}

Se você realmente também estiver interessado no valor antigo ao executar uma ação de negócios, volte valueChangeListener, mas coloque-o na INVOKE_APPLICATIONfase.

public void changeListener(ValueChangeEvent event) {
    if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
        event.setPhaseId(PhaseId.INVOKE_APPLICATION);
        event.queue();
        return;
    }

    Object oldValue = event.getOldValue();
    Object newValue = event.getNewValue();
    System.out.println(newValue.equals(value)); // true
    // ...
}
BalusC
fonte
@BalusC, há uma razão para não obter o valor antigo do objeto de apoio no configurador antes de configurá-lo para o novo valor que é passado? Algo parecido com isto: logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Parece que você poderia usar os valores antigos e novos obtidos dessa maneira para fazer a lógica de negócios diretamente no setter, bem como um registro simples, mas não sei se isso causaria efeitos colaterais ...
Lucas
Resposta perfeita e completa! Obrigado por compartilhar o seu conhecimento!
hbobenicio
@BalusC é possível também obter o valor alterado por meio de AjaxBehaviorEvent? Eu tenho <h: selectManyListbox que está encadeado a outros componentes e gostaria de usar o alterado (adicionado / removido) para realizar alguma ação
Paullo
Obrigado @BalusC Mas eu não acho que ValueChangeListener vai caber no meu caso, pois tenho alguns valores de execução e renderização conforme mostrado <f: ajax event = "valueChange" render = "tests" execute = "@ this" listener = "# {testController. processTimeTable} "/>
Paullo
9

para o primeiro fragmento (atributo de ouvinte ajax):

O atributo "listener" de uma tag ajax é um método que é chamado no lado do servidor sempre que a função ajax ocorre no lado do cliente. Por exemplo, você pode usar este atributo para especificar uma função do lado do servidor para chamar toda vez que o usuário pressiona uma tecla

mas o segundo fragmento (valueChangeListener):

O ValueChangeListener só será chamado quando o formulário for enviado, não quando o valor da entrada for alterado

* você pode gostar de ver esta resposta útil

aur
fonte