【问题标题】:p:selectOneMenu not calling setter on form submitp:selectOneMenu 未在表单提交时调用 setter
【发布时间】:2013-09-24 08:36:48
【问题描述】:

我对 JSF 和 primefaces 有点陌生,但遇到了一个非常烦人的问题。

我正在制作一个非常基本的应用程序来了解更多关于 primefaces 的信息。我有一个简单的表单,它有几个文本输入字段、两个日期选择器和一个下拉菜单(selectOneMenu)。

一切正常,当我提交表单时,所有值都放在支持 bean 中,下拉菜单中的值除外。该项目的设置器永远不会被调用。并且应用程序不会按照命令按钮上的定义调用控制器上的public void saveNewActivity(ActionEvent evt) 方法。然而,当我在 html 中删除或注释掉下拉菜单时,它确实调用了该方法(但下拉菜单的字段显然是 null)。

我已经尝试了将近两天,但仍然无法正常工作。

我有以下代码(sn-ps):

我的 html/jsf 代码

<div id="newActivitycontent">
            <h:form id="newActivityForm">
                <h:messages id="messages"/>
                <table>
                    <tr>
                        <td>Gebruiker:</td>
                        <td><p:selectOneMenu value="#{plannedActivityController.newActivity.organiser}}"
                                             converter="#{userConverter}">
                            <f:selectItem itemLabel="Kies een gebruiker" itemValue=""/>
                            <f:selectItems value="#{plannedActivityController.users}" var="user"
                                           itemLabel="#{user.firstname} #{user.lastname}" itemValue="#{user}"/>
                        </p:selectOneMenu></td>
                    </tr>
                    <tr>
                        <td>Titel:</td>
                        <td><p:inputText value="#{plannedActivityController.newActivity.name}"/></td>
                    </tr>
                    <tr>
                        <td>Beschrijving:</td>
                        <td><p:inputText value="#{plannedActivityController.newActivity.desctription}"/></td>
                    </tr>
                    <tr>
                        <td>Startdatum:</td>
                        <td><p:calendar value="#{plannedActivityController.newActivity.startDateDate}"/></td>
                    </tr>
                    <tr>
                        <td>Einddatum:</td>
                        <td><p:calendar value="#{plannedActivityController.newActivity.endDateDate}"/></td>
                    </tr>
                </table>
                <p:commandButton id="btnSaveNewActivity" value="Opslaan"
                                 actionListener="#{plannedActivityController.saveNewActivity}"
                                 update=":overviewForm:activityTable messages"/>
                <p:commandButton id="btnCancelNewActivity" value="Annuleren"
                                 actionListener="#{plannedActivityController.cancelNewActivity}" onclick="hideAddNewUI()"
                                 update=":overviewForm:activityTable" type="reset" immediate="true"/>
            </h:form>
        </div>

该代码使用的控制器:

@Named
@SessionScoped
public class PlannedActivityController implements Serializable {

    @Inject
    private ApplicationModel appModel;

    @Inject
    private SessionModel sessionModel;

    @Inject
    private ActivityMapper activityMapper;

    @Inject
    private UserMapper userMapper;

    private ActivityBean newActivity;

    private ActivityBean selectedActivity;

    private List<ActivityBean> activities;

    private List<UserBean> users;

    public PlannedActivityController() {
    }

    @PostConstruct
    public void onCreated() {
        convertActivities();
        onNewActivity();

        users = userMapper.mapToValueObjects(appModel.getUsers());
    }

    public void convertActivities() {
        List<PlannedActivity> originalActivities = appModel.getActivities();
        this.activities = activityMapper.mapToValueObjects(originalActivities);
    }

    public void onRowEditComplete(RowEditEvent event) {
        System.out.println("row edited : " + event.getObject());
        //TODO: save changes back to db!
    }

    public void onRowSelectionMade(SelectEvent event) {
        System.out.println("row selected : " + event.getObject());
        selectedActivity = (ActivityBean)event.getObject();
    }

    //Activity crud methods
    public void onNewActivity() {
        newActivity = new ActivityBean();
        newActivity.setId(new Date().getTime());
    }

    public void saveNewActivity(ActionEvent evt) {
        PlannedActivity newAct = activityMapper.mapToEntity(newActivity);
        if(newAct != null) {
            appModel.getActivities().add(newAct);
        }
        convertActivities();
    }

    public void cancelNewActivity() {
        //TODO: cleanup.
    }

    public void deleteSelectedActivity() {
        if(selectedActivity != null) {
            activities.remove(selectedActivity);
            appModel.setActivities(activityMapper.mapToEntities(activities));
            convertActivities();
        } else {
            //TODO: show error or information dialog, that delete cannot be done when nothing has been selected!
        }
    }

    //Getters & Setters
    public ApplicationModel getAppModel() {
        return appModel;
    }

    public void setAppModel(ApplicationModel appModel) {
        this.appModel = appModel;
    }

    public SessionModel getSessionModel() {
        return sessionModel;
    }

    public void setSessionModel(SessionModel sessionModel) {
        this.sessionModel = sessionModel;
    }

    public ActivityMapper getActivityMapper() {
        return activityMapper;
    }

    public void setActivityMapper(ActivityMapper activityMapper) {
        this.activityMapper = activityMapper;
    }

    public UserMapper getUserMapper() {
        return userMapper;
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public ActivityBean getNewActivity() {
        return newActivity;
    }

    public void setNewActivity(ActivityBean newActivity) {
        this.newActivity = newActivity;
    }

    public ActivityBean getSelectedActivity() {
        return selectedActivity;
    }

    public void setSelectedActivity(ActivityBean selectedActivity) {
        this.selectedActivity = selectedActivity;
    }

    public List<ActivityBean> getActivities() {
        return activities;
    }

    public void setActivities(List<ActivityBean> activities) {
        this.activities = activities;
    }

    public List<UserBean> getUsers() {
        return users;
    }

    public void setUsers(List<UserBean> users) {
        this.users = users;
    }
}

我的活动豆:

public class ActivityBean implements Serializable {

    private Long id = 0L;

    private String name;

    private String desctription;

    private UserBean organiser;

    private Calendar startDate;

    private Calendar endDate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getDesctription() {
        return desctription;
    }

    public void setDesctription(String desctription) {
        this.desctription = desctription;
    }

    public UserBean getOrganiser() {
        return organiser;
    }

    public void setOrganiser(UserBean organiser) {
        this.organiser = organiser;
    }

    public Calendar getStartDate() {
        return startDate;
    }

    public void setStartDate(Calendar startDate) {
        this.startDate = startDate;
    }

    public Date getStartDateDate() {
        if(this.startDate == null) {
            return null;
        }
        return this.endDate.getTime();
    }

    public void setStartDateDate(Date startDate) {
        if(this.startDate == null) {
            this.startDate = new GregorianCalendar();
        }
        this.startDate.setTime(startDate);
    }

    public String getStartDateString() {
        if(this.startDate == null) {
            return null;
        }
        return startDate.get(Calendar.DAY_OF_MONTH) + "/" + startDate.get(Calendar.MONTH) + "/" + startDate.get(Calendar.YEAR) + "";
    }

    public Calendar getEndDate() {
        return endDate;
    }

    public void setEndDate(Calendar endDate) {
        this.endDate = endDate;
    }

    public Date getEndDateDate() {
        if(this.endDate == null) {
            return null;
        }
        return endDate.getTime();
    }

    public void setEndDateDate(Date endDate) {
        if(this.endDate == null) {
            this.endDate = new GregorianCalendar();
        }
        this.endDate.setTime(endDate);
    }

    public String getEndDateString() {
        if(this.endDate == null) {
            return null;
        }
        return endDate.get(Calendar.DAY_OF_MONTH) + "/" + endDate.get(Calendar.MONTH) + "/" + endDate.get(Calendar.YEAR) + "";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ActivityBean that = (ActivityBean) o;

        if (id != null ? !id.equals(that.id) : that.id != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return id != null ? id.hashCode() : 0;
    }
}

我的用户bean:

public class UserBean {

    private Long id;

    private String username;

    private String firstname;

    private String lastname;

    private String email;

    private String phone;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        UserBean userBean = (UserBean) o;

        if (id != null ? !id.equals(userBean.id) : userBean.id != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return id != null ? id.hashCode() : 0;
    }
}

还有selectOneMenu使用的转换器:

@Named
public class userConverter implements Converter{

    @Inject
    private PlannedActivityController activityController;

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s) {
        for (UserBean user : activityController.getUsers()) {
            if(user.getId().toString().equals(s)) {
                return user;
            }
        }
        return null;
    }

    @Override
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o) {
        if(o instanceof UserBean) {
            UserBean user = (UserBean)o;
            return user.getId().toString();
        }
        return "";
    }
}

【问题讨论】:

  • 您有一个 value="#{...}},这是无效的 EL 语法。这是真正的代码还是只是在准备问题时粗心?您没有在服务器日志(或至少在 ajax 响应正文中)看到任何 EL 异常吗?
  • 这是在代码中,我仍然无法相信我怎么会错过这个!你拯救了我的一天!
  • 理论上应该抛出一个PropertyNotWritableException。你没在 ajax 响应正文中看到吗?
  • 不,但是它在我的转换器中传递了一个字符串“}”,我想知道它来自哪里。
  • 我只是试图在这里重现它,它确实抛出了那个异常(Mojarra 2.1.26 + PrimeFaces 3.5)。你真的看过 ajax 响应体吗?你明白那是什么意思吗?在浏览器中打开页面,在 Chrome/Firefox>=23/IE>=9 中按 F12 并打开 Network 选项卡,然后填写/提交表单,然后单击 网络选项卡,然后单击响应选项卡。例外应该在那里。你看到了吗?就我而言,它在服务器日志中也非常清晰。

标签: jsf-2 primefaces cdi


【解决方案1】:

问题就在这里。仔细看。这是无效的 EL 语法。

value="#{plannedActivityController.newActivity.organiser}}"

然而,这应该在提交类似这样的内容时抛出 PropertyNotWritableException

javax.el.PropertyNotWritableException: /test.xhtml @25,39 value="#{plannedActivityController.newActivity.organiser}}": Illegal Syntax for Set Operation
    at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:136)
    at javax.faces.component.UIInput.updateModel(UIInput.java:822)
    at javax.faces.component.UIInput.processUpdates(UIInput.java:739)
    at javax.faces.component.UIForm.processUpdates(UIForm.java:281)
    at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1244)
    at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1244)
    at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1223)
    at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)

这个异常应该被记录到服务器日志中。然而,默认情况下,这不会最终出现在最终用户的错误页面中,因为 JSF/PrimeFaces 默认情况下没有任何形式的反馈给最终用户,以防在 ajax 请求期间抛出异常。但是,您应该能够在 webbrowser 的内置 HTTP 流量监视器的实际 ajax 响应正文中看到它。

JSF 实用程序库OmniFaces 提供了FullAjaxExceptionHandler 来解决在ajax 请求期间完全没有异常反馈的问题。您可能会发现它很有用。当我尝试重新创建您的问题时,我看到了一个清晰的错误页面,因此我不需要在服务器日志或 HTTP 流量监视器中挖掘线索。

【讨论】:

  • 在浏览器调试器中可以看到ajax异常!非常感谢!
  • 不客气。那东西不完全是调试器。它只是一个 HTTP 流量监视器。
猜你喜欢
  • 1970-01-01
  • 2012-02-17
  • 2021-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多