Динамический JSF (2.0) commandButtons - установить действие с аргументами

Я пытаюсь динамически добавить JSF <h:commandButtons> на свою веб-страницу, и пока они у меня отображаются, но я не могу установить действие с параметрами, как на статической странице:
action="#{bean.function(parameter)}". (это, конечно, с использованием EL-2.2)
Оглядевшись, я обнаружил, что мне нужно создать MethodExpression, но это неясно для меня, и я не смог найти много информации об этом. Если бы кто-то мог пролить свет сквозь туман и объяснить, как это можно сделать, он был бы очень признателен.

РЕДАКТИРОВАТЬ: так что теперь у меня есть это

public void displayNode( String childName ){
//lots of messy code instantiating JSF components

if( activeEmployee.getParent() != null ){
        HtmlCommandButton parent = new HtmlCommandButton();
        HtmlOutputText parentLabel = new HtmlOutputText();

        parentLabel.setId("label" + count++);  //I really hate having to use count
        parentLabel.setValue( "Parent: " );

        parent.setId("Parent" + count++); 
        String parentName = activeEmployee.getParent().getName();
        parent.setValue( parentName );
        MethodExpression expression = createMethodExpression("#{tree.displayNode('" + parentName + "')}",
                                                                null, String.class);
        parent.setActionExpression( expression );

        newDiv.getChildren().add( parentLabel );
        newDiv.getChildren().add( parent );
    }

person Jake Long    schedule 23.08.2012    source источник
comment
Не можете ли вы использовать свойство rendered для отображения/скрытия командной кнопки?   -  person RajV    schedule 23.08.2012
comment
нет, я динамически создаю узлы дерева с кнопками для отображения других связанных узлов   -  person Jake Long    schedule 23.08.2012


Ответы (1)


Используйте ExpressionFactory#createMethodExpression().

public abstract MethodExpression createMethodExpression(
                                      ELContext context,
                                      java.lang.String expression,
                                      java.lang.Class<?> expectedReturnType,
                                      java.lang.Class<?>[] expectedParamTypes)

Вот удобный метод:

public static MethodExpression createMethodExpression(String expression, Class<?> returnType, Class<?>... parameterTypes) {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    return facesContext.getApplication().getExpressionFactory().createMethodExpression(
        facesContext.getELContext(), expression, returnType, parameterTypes);
}

Следующие примеры методов действия:

public void submit1()
public String submit2()
public void submit3(String argument)
public String submit4(String argument)
public void submit5(String argument1, Long argument2)
public String submit6(Long argument1, String argument2)

затем можно создать следующим образом:

createMethodExpression("#{bean.submit1}", null);
createMethodExpression("#{bean.submit2}", String.class);
createMethodExpression("#{bean.submit3('foo')}", null, String.class);
createMethodExpression("#{bean.submit4('foo')}", String.class, String.class);
createMethodExpression("#{bean.submit5('foo', 0)}", null, String.class, Long.class);
createMethodExpression("#{bean.submit6(0, 'foo')}", String.class, Long.class, String.class);

Обратите внимание, что выражение EL точно такое же, как и в обычном файле представления.


Обновление вот SSCCE, который отлично работает для меня с Mojarra 2.1.12 на Tomcat 7.0.27.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
>
    <h:head>
        <title>SO question 12098611</title>
    </h:head>
    <h:body>
        <h:form binding="#{bean.form}">
            <h:commandButton value="add" action="#{bean.add}" />
        </h:form>
    </h:body>
</html>
@ManagedBean
@RequestScoped
public class Bean {

    private UIForm form;

    public void add() {
        String id = "button" + form.getChildCount();
        UICommand button = new HtmlCommandButton();
        button.setId(id);
        button.setValue(id);
        button.setActionExpression(createMethodExpression(String.format("#{bean.submit('%s')}", id), null, String.class));
        form.getChildren().add(button);
    }

    public void submit(String arg) {
        System.out.println("submit: " + arg);
    }

    public UIForm getForm() {
        return form;
    }

    public void setForm(UIForm form) {
        this.form = form;
    }

    public static MethodExpression createMethodExpression(String expression, Class<?> returnType, Class<?>... parameterTypes) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().getExpressionFactory().createMethodExpression(
            facesContext.getELContext(), expression, returnType, parameterTypes);
    }

}

Не связанные с конкретной проблемой, все вышеперечисленное является плохой практикой. См. также Как работает атрибут «binding» в JSF? Когда и как его следует использовать?

person BalusC    schedule 23.08.2012
comment
Еще раз, ты мужчина. Я искал эту информацию в Интернете, потому что я сделал это, но с использованием JSF 1.2. - person Luiggi Mendoza; 24.08.2012
comment
Думаю, я преждевременно предположил, что мне нужен MethodExpression, потому что я видел, как это делается до использования button.setActionExpression(...). В чем разница между использованием setActionExpression() и setAction(), помимо необходимых очевидных аргументов? - person Jake Long; 24.08.2012
comment
@Jake: setAction() — это устарел остаток JSF 1.x с использованием отложенного EL, в котором использовалась собственная реализация EL с ValueBinding, MethodBinding и т. д. (которая позже была объединена с JSP EL). Вообще не используйте устаревшие API в JSF 2.x. Они подлежат удалению. См. также stackoverflow.com/questions/4812755/ немного истории EL. - person BalusC; 24.08.2012
comment
хм, кнопка не вызывает действие, я скину какой-нибудь код - person Jake Long; 24.08.2012
comment
Код выглядит нормально. Вы подразумеваете, что метод без аргументов вызывается правильно? - person BalusC; 24.08.2012
comment
Нет, функция вообще не вызывается. Ошибок от попытки вызова displayNode без аргументов нет, просто после нажатия созданной кнопки ничего не происходит - person Jake Long; 24.08.2012
comment
Потом кнопка видимо пропала после восстановления вида. Как именно вы держите newDiv в бобах? Это должен быть bean-компонент с областью действия запроса, и это должно быть просто свойство с обычным геттером/сеттером и ничего более (также без ленивой загрузки; JSF установит его сам). Все методы действий, зависящие от управляемого дерева компонентов, должны возвращать void или null (чтобы тот же вид сохранялся для последующего запроса). - person BalusC; 24.08.2012
comment
Я просматриваю bean-компонент с областью действия, потому что я создаю дерево внутри класса tree, а затем использую эти функции для отображения узлов. newDiv — это просто элемент HtmlPanelGroup, и в конце функции он добавляется к HtmlPanelGroup topDiv (скоро изменится, должна быть таблица), который я привязал к div на странице JSF. - person Jake Long; 24.08.2012
comment
@Jake: bean-компонент с областью видимости также может, но UIComponent не сериализуем, поэтому сохранение состояния не удастся. Если вам абсолютно необходимы данные с областью просмотра, скорее разделите свойство UIComponent (и действия, зависящие от него) на новый bean-компонент с областью действия запроса и внедрите bean-компонент с областью видимости в качестве управляемого свойства, если вам нужны некоторые данные из него. - person BalusC; 24.08.2012
comment
Извините, о каком UIComponent вы говорите? - person Jake Long; 24.08.2012
comment
Любой подкласс UIComponent, а также newDiv. - person BalusC; 24.08.2012
comment
хорошо, я думаю, я понял, я должен попробовать это завтра (5:00 wahoo!) - person Jake Long; 24.08.2012
comment
Еще одним недостатком использования binding для bean-компонента с областью видимости является то, что при каждом запросе будет создаваться новый экземпляр. Так что довольно бессмысленно делать его видимым. - person BalusC; 24.08.2012
comment
Я принял это, потому что вы ответили на вопрос, который я задал (фантастически и тщательно), но я, скорее всего, скоро буду задавать связанные вопросы XP - person Jake Long; 24.08.2012
comment
Метод отправки @Balus никогда не вызывается для меня в Glassfish 4, JSF 2.2, Spring WebFlow и Primefaces. Я скопировал ваш код, как вы написали. Я попытался добавить поведение ajax, actionlistener и действие в свой проект, но результат тот же... кнопка отображается и бесполезна, когда я нажимаю. - person Rafael Ruiz Tabares; 02.12.2014
comment
Если я изменяю код @BalusC на SessionScope, то вызывается метод submit, но requestScope или viewScope не работают. - person Rafael Ruiz Tabares; 03.12.2014