关于jsf 2:h:commandButton一旦将其package在<h:panelGroup rendering>中就无法正常工作

h:commandButton is not working once I wrap it in a <h:panelGroup rendered>

该主题具有"种类"的原因是因为我在JSF 2.2中有一个示例,在该示例中,我使用commandButton并两次调用bean函数(取决于url)。它基本上是相同的代码,仅在一个示例中执行。

以下是在代码下带有"错误"描述的代码:

用户bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@ManagedBean
@RequestScoped
public class User {

private String name;
private String surname;
private int age;
private int id;

public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

public String getSurname() {
    return surname;
}
public void setSurname(String surname) {
    this.surname = surname;
}

public int getAge() {
    return age;
}
public void setAge(int age) {
    this.age = age;
}

public int getId() {
    return id;
}
public void setId(int id) {
    this.id = id;
}

public User(String name, String surname, int age, int id) {
    super();
    this.name = name;
    this.surname = surname;
    this.age = age;
    this.id = id;
}

public User(){}

}

UsersBean bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@ManagedBean
@SessionScoped
public class UsersBean {

private List<User> listOfUsers = new ArrayList<User>();
private String passedParameter;

public UsersBean() {
    listOfUsers.add(new User("Tywin","Lannister", 60, 1));
    listOfUsers.add(new User("Tyrion","Lannister", 30, 2));
    listOfUsers.add(new User("Jaime","Lannister", 31, 3));
    listOfUsers.add(new User("Cercei","Lannister", 29, 4));
    listOfUsers.add(new User("John","Snow", 31, 5));
}

public List<User> getAll() {

    System.out.println("getAall is called.");
    return listOfUsers;
}

public User getDetails() {
    passedParameter = (String) FacesContext.getCurrentInstance()
            .getExternalContext().getRequestParameterMap().get("userID");
    int id = Integer.parseInt(passedParameter);
    User selected = null;
    for (User u : listOfUsers) {
        if (u.getId() == id) {
            selected = u;
        }
    }
    return selected;
}

public String addUser(User u) {
    System.out.println("addUser is called.");
    if (u.getId() != 0) {
        for (User edit : listOfUsers) {
            if (edit.getId() == u.getId()) {
                System.out.println("Found it!");
                edit.setAge(u.getAge());
                edit.setName(u.getName());
                edit.setSurname(u.getSurname());
            }
        }

    } else {
        u.setId(listOfUsers.size() + 1);
        listOfUsers.add(u);
    }

    return"";
}
}

users.xhtml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    <f:view>
        <!-- http://stackoverflow.com/questions/8083469/method-must-have-signature-string-method-etc-but-has-signature-void -->
        <h:dataTable value="#{usersBean.all}" var="u">

            <h:column>
                <f:facet name="header">
                    User ID
                </f:facet>
                    #{u.id}
            </h:column>

            <h:column>
                <f:facet name="header">
                    Name
                </f:facet>
                    #{u.name}
            </h:column>

            <h:column>
                <f:facet name="header">
                    Details
                </f:facet>

                <h:link outcome="users" value="edit user">
                    <f:param name="userID" value="#{u.id}"></f:param>
                    <f:param name="action" value="edit"></f:param>
                </h:link>

               

                <h:link outcome="usersDetails" value="get details">
                    <f:param name="userID" value="#{u.id}"></f:param>
                </h:link>                  
            </h:column>

        </h:dataTable>

        <h:panelGroup rendered="#{param['action'] == 'edit'}">
            Edit!
            <h:form>
                <ui:param name="editUser" value="#{usersBean.details}"></ui:param>
                <h:outputText value="Name"></h:outputText>
                <h:inputText value="#{editUser.name}"></h:inputText> <br />

                <h:outputText value="Surname"></h:outputText>
                <h:inputText value="#{editUser.surname}"></h:inputText> <br />

                <h:outputText value="Age"></h:outputText>
                <h:inputText value="#{editUser.age}"></h:inputText> <br />

                <h:commandButton action="#{usersBean.addUser(editUser)}" value="Edit" type="submit">    </h:commandButton>
            </h:form>
    </h:panelGroup>

        <h:panelGroup rendered="#{empty param['action']}">
            Add!
            <h:form>
                <h:outputText value="Name"></h:outputText>
                <h:inputText value="#{user.name}"></h:inputText>
                <h:outputText value="Surname"></h:outputText>
                <h:inputText value="#{user.surname}"></h:inputText>
                <h:outputText value="Age"></h:outputText>
                <h:inputText value="#{user.age}"></h:inputText>
                <h:commandButton action="#{usersBean.addUser(user)}" value="Add" type="submit"></h:commandButton>
            </h:form>
        </h:panelGroup>    

    </f:view>

好的,所以一切正常。 usersBean.addUser添加用户。如果为ID添加另一个inputText并放入现有ID,则相应的函数将更新这些值。因此,addUser函数按预期方式工作。

问题是在

的情况下

1
<h:panelGroup rendered="#{param['action'] == 'edit'}">

正如您在上面的xhtml中看到的那样,

代码基本相同,唯一的例外是我填写了所选用户的数据。这可行,我将适当的数据输入到输入字段中,但是当我更改它们并单击"编辑"时,什么也没有发生。该函数未调用!而在添加的情况下,该函数将被调用并起作用。
看起来好像没有在编辑时定义任何操作,它仅重新加载页面(提交),而没有实际操作addUser。

这是怎么引起的,我该如何解决?


这是因为#{param['action'] == 'edit'}在处理表单提交过程中未评估true,因此在处理表单提交过程中基本上不呈现<h:panelGroup>,包括其中的<h:commandButton>。表单提交即没有将该参数传递回JSF。这样,JSF将看不到<h:commandButton>,因此永远无法解码其动作事件并将其排队。您需要确保在处理表单提交时,所有rendered条件的评估结果与显示表单时的评估结果相同(这是JSF防范篡改/被黑客窃取的保护措施的一部分,否则最终用户将能够执行未提交的命令按钮,例如仅管理员按钮)。

因此,您基本上也需要在回发期间保留该参数,以使rendered表达式在处理表单提交期间仍对true求值。这可以通过几种方式实现:

  • 将其添加为相关的<h:commandButton>中的<f:param>

    1
    2
    3
    <h:commandButton action="#{usersBean.addUser(editUser)}" value="Edit" type="submit">
        <f:param name="action" value="#{param.action}" />
    </h:commandButton>

    (注意:param['action']param.action是等效的;这些括号仅在'action'包含句点或表示另一个变量时才有用)

  • 通过<f:viewParam>将其设置为bean属性。要求是bean必须在视野范围或更广的范围内。您已经在会话范围中拥有了它,这还可以(但是IMO的范围太广了,但是就此而言):

    1
    2
    3
    4
    5
    6
    7
    <f:metadata>
        <f:viewParam name="action" value="#{usersBean.action}" />
    </f:metadata>
    ...
    <h:panelGroup rendered="#{usersBean.action}">
        ...
    </h:panelGroup>
  • 如果您已经在使用JSF实用程序库OmniFaces,请使用其<o:form>而不是<h:form>,这使您可以提交到当前请求URI而不是当前JSF视图ID。这样,GET请求字符串将自动保留,并且您无需像#1中那样在所有命令按钮上复制<f:param>粘贴:

    1
    2
    3
    <o:form useRequestURI="true">
        ...
    </o:form>

  • 也可以看看:

    • 未调用commandButton / commandLink / ajax操作/侦听器方法或未设置/更新输入值-第6点