【问题标题】:selectOneMenu selected valueselectOneMenu 所选值
【发布时间】:2013-02-25 20:46:55
【问题描述】:

好吧,我需要澄清一下 selectOneMenu 标签,使用 EL 表达式来获取/设置值

        <h:selectOneMenu id="variantsMenu"
            value="#{missionHandler.selectedVariant}"
            converter="#{missionHandler.variantConverter}">
            <f:selectItem itemLabel="-- Select a Variant --" />
            <f:selectItems value="#{missionHandler.variants}" var="variant"
                itemValue="#{variant}" itemLabel="#{variant.commercialName}" />
        </h:selectOneMenu>

我有这段代码,它显示一个列表,其中包含 ("-- Select a Variant--", Variant 1, Variant 2, ... Variant n) 当我单击一个变体时,我希望 missionHandler managedBean 中的 selectedVariant 属性会自动更新。但这种情况并非如此。甚至没有调用variantConverter 转换器将字符串“variant.commercialName”转换为真正的Variant 对象。 变体对象实现hashCode()equals()

  • 我缺少什么 EL 表达式/标签的概念?
  • 我的更新问题有什么解决方案?

    编辑: managedBean(简体)

    @ManagedBean
    @ViewScoped
    public class MissionHandler implements Serializable {
    
    private static final long serialVersionUID = 2462652101518266609L;
    
    private List<FlightFeasibilityException> exceptions;
    
    @EJB
    private VariantDao variantDao;
    
    private Variant selectedVariant;
    
    private List<Variant> variants;
    
    private VariantConverter variantConverter;
    
    public MissionHandler() {
        /** Create an empty list of exceptions */
        exceptions = new ArrayList<FlightFeasibilityException>();
    }
    
    @PostConstruct
    public void init() {
        System.out.println("init done");
    }
    
    public List<FlightFeasibilityException> getExceptions() {
        return exceptions;
    }
    
    public void setExceptions(List<FlightFeasibilityException> exceptions) {
        this.exceptions = exceptions;
    }
    
    public Variant getSelectedVariant() {
        return selectedVariant;
    }
    
    public void setSelectedVariant(Variant selectedVariant) {
        this.selectedVariant = selectedVariant;
    }
    
    public List<Variant> getVariants() {
        return variants;
    }
    
    public void setVariants(List<Variant> variants) {
        this.variants = variants;
    }
    
    public VariantConverter getVariantConverter() {
        return variantConverter;
    }
    
    public void setVariantConverter(VariantConverter variantConverter) {
        this.variantConverter = variantConverter;
    }
    

    }

转换器

@FacesConverter(forClass=Variant.class)
public class VariantConverter implements Converter, Serializable {

    private static final long serialVersionUID = 7053414108213420057L;

    private VariantDao variantDao=new VariantDaoImpl();

    @Override
    public Object getAsObject(FacesContext context, UIComponent component,
            String value) {
        System.out.println("convert to Object " + value);
        Variant variant = variantDao.find(value);
        System.out.println("got variant " + variant.getCommercialName());
        return variant;
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component,
            Object value) {
        System.out.println("convert to String " + value);
        return ((Variant) value).getCommercialName();
      }

    }

和 xhtml 文件

<!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://java.sun.com/jsf/html"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:rich="http://richfaces.org/rich"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    <title>Mission Page</title>
    <link href="./css/styles.css" rel="stylesheet" type="text/css" />
</h:head>
<h:body>
    <rich:panel>
        <f:facet name="header">
            Mission Information
        </f:facet>
        <a4j:outputPanel layout="block">
            <h:form>
                <h:outputText style="font-weight: bold;" value="Mission Id: " />
                <h:inputText id="missionId" label="missionId"
                    value="#{missionHandler.mission.id}" disabled="true"
                    style=" width : 50px;">
                </h:inputText>
                <h:outputText style="font-weight: bold;" value="Mission Status: " />
                <h:inputText id="missionStatus" label="missionStatus"
                    value="#{missionHandler.mission.status}" disabled="true">
                </h:inputText>
            </h:form>
            <br />
            <h:form>
                <h:selectOneMenu value="#{missionHandler.selectedAircraftType}"
                    converter="#{missionHandler.acTypeConverter}">
                    <f:selectItem itemLabel="-- Select an A/C Type --" />
                    <f:selectItems value="#{missionHandler.aircraftTypes}" var="type"
                        itemValue="#{type}" itemLabel="#{type.typeOACI}" />
                    <f:ajax listener="#{missionHandler.changeSelectedAircraftType}"
                        render="@form" />
                </h:selectOneMenu>
                <h:selectOneMenu id="variantsMenu"
                    value="#{missionHandler.selectedVariant}"
                    converter="#{missionHandler.variantConverter}"
                    rendered="#{not empty missionHandler.selectedAircraftType}">
                    <f:selectItem itemLabel="-- Select a Variant --" />
                    <f:selectItems value="#{missionHandler.variants}" var="variant"
                        itemValue="#{variant}" itemLabel="#{variant.commercialName}" />
                </h:selectOneMenu>
            </h:form>
        </a4j:outputPanel>
    </rich:panel>
….. A LOT OF OTHER THINGS …..
</h:body>
</html>

【问题讨论】:

  • 您正在尝试自己实例化转换器。虽然我不能断然说这样做是错误的,但我可以告诉你,管理转换器是容器的责任。在此处发布托管 bean 代码以及转换器
  • 完成。请看一下
  • 您的代码看起来有效(即使您在其中做出了一些有问题的选择)。我现在对您的 xhtml 文件感兴趣。你有嵌套&lt;h:form/&gt;s 吗?单个&lt;h:form/&gt; 中有大量组件吗?
  • @kolossus :我已经添加了 xhtml 文件。与我的第一个问题平行,你能告诉我我的代码编写中有什么问题吗?我总是对学习一些最佳实践感兴趣......我并不是特别喜欢每次“converter::getAsObject”调用时的数据库访问。我试图用一个列表一劳永逸地初始化转换器(在托管 bean init 方法中),但我得到了 nullPointerExceptions。所以我就这样搁置了,直到我有时间重新考虑它。
  • 1) 在托管 bean 的构造函数中做事==bad,改用@PostConstructor。 2)在你的转换器中手动实例化DAO==糟糕的设计。理想情况下,您的 DAO 应该是可以注入转换器的托管对象(最好是 EJB)。 3)试图手动管理转换器。将转换器声明为独立组件并让容器完成它的工作

标签: jsf jakarta-ee


【解决方案1】:

关于您的代码的几点说明:

  1. 在托管 bean 的构造函数中做事==糟糕的设计,请改用 @PostConstructor

  2. 在您的转换器==错误设计中手动实例化 DAO。理想情况下,您的 DAO 应该是可以注入转换器的托管对象(最好是 EJB)。作为一个托管对象意味着它(理论上)可能是一个单例并由容器正确管理。至少这样,你不会为此感到难过。

  3. 尝试手动管理转换器。将转换器声明为独立组件并让容器完成它的工作,这样您就可以避免不必要的调试。

  4. variantsMenu 没有附加 ajax 处理程序,因此在提交整个表单之前,您不会在支持 bean 中更新值

    <h:selectOneMenu id="variantsMenu"
                value="#{missionHandler.selectedVariant}"
                converter="#{variantConverter}"
                rendered="#{not empty missionHandler.selectedAircraftType}">
                <f:ajax listener="#{missionHandler.changeVariant}"       render="@form" />
                <f:selectItem itemLabel="-- Select a Variant --" />
                <f:selectItems value="#{missionHandler.variants}" var="variant"
                    itemValue="#{variant}" itemLabel="#{variant.commercialName}" />
            </h:selectOneMenu>
    

【讨论】:

  • 1.为什么在构造函数中做事不好?如果有原因(例如 viewParam 相关的初始化逻辑),我只在@PostConstruct 中使用 2。我同意将 dao 实例化并将其作为参数传递给转换器也是我主要使用的一个选项。 3. 我自己管理大部分实体转换器,因为转换通常依赖于动态数据。
  • @djmj,这主要是一个好的设计点,因为@PostConstruct 是一个容器提供的回调。因为它是容器提供的,所以它是执行初始化操作的更安全的地方:如果方法抛出未经检查的异常,则不得将类投入使用,除非 EJB 可以处理异常,甚至从异常中恢复.--这是@PostConstruct 合同的一部分。您在构造函数中所做的任何事情都无法保证。在未来的 JEE 版本中,可以将更多安全功能内置到您的代码将要准备的注释中。
  • @djmj,不会在托管 bean 中管理转换器,从而不必要地将转换器绑定到托管 bean 的范围?可能会导致转换器的寿命超出预期?
  • 关于@PostConstruct 和异常的好点,但我认为无论如何都不会在构造函数中抛出异常。
  • @djmj for viewParam 和 "@PostConstruct" 还为时过早。为此,您需要 PreRenderView 事件处理程序。
【解决方案2】:

当您从菜单中选择一个项目时,托管 bean 中的值不会直接更新,因为没有提交表单。

要么通过&lt;h:commandButton ...&gt; 提交它,要么在菜单中使用一个ajax 事件&lt;f:ajax event="change" ...&gt;

【讨论】:

  • 我明白你的意思:#EL 表达式可用于访问 getter 和 setter(与只能访问 getter 的旧 $EL 表达式相比)但这并不意味着将调用 getter 方法每次#EL 表达式的值发生变化时?您仍然需要调用某种 ajax 或手动提交方法。
  • user1446127,JSF 生成 HTML。在浏览器中右键单击页面并执行查看源代码。 HTML 是完全愚蠢和无状态的,除非您使用 JavaScript/ajax 根据用户界面事件(键盘/鼠标)使其具有某种交互性。这个“问题”与 JSF 完全无关。手动编写 HTML 代码或使用另一种服务器端语言(如 PHP、ASP 等也生成 HTML)时,您会遇到完全相同的问题。
【解决方案3】:

除非我遗漏了什么,否则我认为您永远不会实例化您的转换器。你绑定一个 bean 来获取转换器,但是值在哪里?

我可以理解您不喜欢转换器中的数据库访问。如果您使用 OmniFaces 中的选择项转换器之一,则几乎不需要这样做。

【讨论】:

  • 好吧,我只是阅读了一些关于 OmniFaces 的资料。我会尽力让你知道的。但据我了解,我必须在某个时候从数据库中加载我的列表(让我们在我的托管 bean 的 @PostConstruct 方法中说)。我对 OmniFaces 项目转换器的不理解是:他们如何在不知道我加载的列表或数据库的情况下将字符串转换为对象?
  • 我实现了 OmniFaces 项目转换器,以及此线程中列出的其他内容,现在一切都很好。谢谢!
  • 很好用!我认为 OmniFaces 通过一些反向转换算法工作:如果 2 个项目转换为相同的字符串,它必须是相同的对象。回发后,您从中构建选择项目的对象列表必须存在。列表中的位置也会匹配。如果你使用它,你的列表不仅必须在那里,而且它需要完全相同的对象以相同的顺序。这与数据表的要求相同。视图范围将自动满足这一点。
猜你喜欢
  • 2014-10-10
  • 2012-08-21
  • 2013-01-27
  • 2011-07-17
  • 1970-01-01
  • 1970-01-01
  • 2018-04-06
  • 2015-09-03
  • 1970-01-01
相关资源
最近更新 更多