Вызвать f:viewAction условно на основе f:viewParam

У нас есть настройка, в которой у нас есть различные необязательные параметры просмотра, передаваемые на страницы JSF, и последующие действия просмотра, которые должны обрабатываться после установки параметров. Очень простой пример показан ниже:

page.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://xmlns.jcp.org/jsf/core">

<f:view>
    <f:metadata>
        <f:viewParam name="a" value="#{page.a}"/>
        <f:viewAction action="#{page.populateA()}" if="#{not empty page.a}"/>

        <f:viewParam name="b" value="#{page.b}"/>
        <f:viewAction action="#{page.populateB()}"/>
    </f:metadata>

    <h:outputLabel value="#{page.message}"/>
</f:view>
</html>

страница

import javax.faces.view.ViewScoped;
import javax.inject.Named;

@ViewScoped
@Named
public class Page {

    private String a;

    private String b;

    private String message;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }

    public String getMessage() {
        return message;
    }

    public void populateA() {
        this.message = "Param a given: " + this.a;
    }

    public void populateB() {
        if (this.b != null) {
            this.message = "Param b given: " + this.b;
        }
    }
}

Теперь разница в том, что обработка a не работает (page.xhtml?a=123), в то время как обработка b работает как шарм (page.xhtml?b=123) - хотя я думал, что просто перенес нулевую проверку с Java на JSF. Что касается читабельности, я бы предпочел пропустить дополнительные проверки нуля в Java и полностью обработать параметры представления в JSF, но как я могу настроить код, чтобы первый сценарий работал?

РЕДАКТИРОВАТЬ В соответствии с принятым ответом Какова цель визуализируемого атрибута в f: viewAction?, if работает как таковой, поэтому я подозреваю, что неправильный порядок выполнения (сначала оцените условие действия, затем примените значения к модели).

1 ответ

Решение

<f:viewAction if> атрибут в основном переименован <h:xxx rendered> атрибут (я оставлю в середине, будет ли это более ясным или нет, как следствие, он имеет по крайней мере ошибку в автоматически сгенерированной документации; rendered вместо if) и, таким образом, имеет точно такой же жизненный цикл во время фазы " Применить значения запроса", как и у всех других компонентов пользовательского интерфейса. Эта фаза называется UIComponent#decode() всех компонентов пользовательского интерфейса в представлении. Для UIViewAction, класс компонента пользовательского интерфейса позади <f:viewAction> тег, decode() описывается следующим образом:

Не предпринимайте никаких действий, если выполняется любое из следующих условий:

  • Текущий запрос является обратной передачей, а экземпляр настроен так, чтобы не выполнять обратную передачу. Увидеть isOnPostback(),

  • Условие указано в if свойство оценивается как ложное. Увидеть isRendered()

Так что, да, вы действительно правы относительно "неправильного порядка исполнения". На этапе применения значений запроса он проверяет if атрибут и если он оценивает false тогда декодирование не произойдет, а событие не будет поставлено в очередь. Смотрите также строку 644 UIViewAction исходный код, где он тестирует isRendered() insice decode() и не продолжается, если false, В вашем случае #{page.a}, который вы проверяете в <f:viewAction if>, доступен только на этапе обновления значений модели, который идет после этапа применения значений запроса.

Вы хотите протестировать что-то еще, что гарантированно будет доступно на этапе применения значений запроса. Лучшим кандидатом будет сам фактический параметр запроса. Все "простые" параметры запроса находятся в области видимости EL, доступной для неявного объекта EL #{param} который ссылается на Map<String, String> с именем параметра в качестве ключа.

Итак, в общем, это должно сделать:

<f:viewParam name="a" value="#{page.a}"/>
<f:viewAction action="#{page.populateA}" if="#{not empty param.a}"/>

Обновление, чтобы решить не интуитивное поведение <f:viewAction if> Библиотека утилит JSF OmniFaces будет начиная с версии 2.2 предлагать <o:viewAction> чья if оценивается только на этапе вызова приложения, непосредственно перед трансляцией события действия. Этот трюк был сделан простым расширением UIViewAction компонент и переопределение его broadcast() а также isRendered() как показано ниже:

@Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
    if (super.isRendered()) {
        super.broadcast(event);
    }
}

@Override
public boolean isRendered() {
    return !isImmediate() || super.isRendered();
}

Это позволяет вам использовать тег более интуитивно:

<f:viewParam name="a" value="#{page.a}"/>
<o:viewAction action="#{page.populateA}" if="#{not empty page.a}"/>
Другие вопросы по тегам