【问题标题】:JSF 2.X UI Component use of ValueExpressionJSF 2.X UI Component 使用 ValueExpression
【发布时间】:2012-08-06 15:35:21
【问题描述】:

我正在尝试构建一个简单的组件来了解 JSF 2.X 如何以及为什么会以它的方式工作。我一直在使用较新的注释,并试图拼凑出一个清晰的例子。

所以我已经构建了我的组件并将其部署在一个 xhtml 文件中,如下所示:

<kal:day value="K.Day" title="Kalendar" model="#{kalendarDay}"/>

在 UIComponent 中我做了以下事情:

ValueExpression ve      = getValueExpression("model");
if (ve != null) 
{
    System.out.println("model expression "+ve.getExpressionString());
    model = (KalendarDay) ve.getValue(getFacesContext().getELContext());
    System.out.println("model "+model);
}

表达式“#{kalendarDay}”正确显示,表明该值已在页面和组件之间成功传输。然而,表达式的计算结果为“null”。

这似乎表明此时支持 bean 不可用,尽管页面正确验证和部署。所以我 95% 确定 bean 在运行时就在那里。

所以也许这是一个阶段的事情?我应该在渲染器的解码中对此进行评估并在属性映射中设置值吗?对于实际值和值表达式的组合,我还是有点困惑。

所以我的问题是我应该在哪里获取和评估模型的 valueExpression,我应该将评估结果存储在 UIComponent 中还是应该每次都简单地评估它?

下面的 SSCCE 文件我认为这些是演示问题所需的唯一文件

Bean接口-----

/**
 * 
 */
package com.istana.kalendar.fixture;

import java.util.Date;

/**
 * @author User
 *
 */
public interface KalendarDay
{
    public      Date        getDate();
}

Bean 实现 ---

/**
 * 
 */
package com.istana.kalendar.session.wui;

import java.util.Calendar;
import java.util.Date;

import javax.ejb.Stateful;
import javax.inject.Named;

import com.istana.kalendar.fixture.KalendarDay;

/**
 * @author User
 *
 */
@Named      ("kalendarDay")
@Stateful
public class KalKalendarDay
    implements KalendarDay
{

    private     Calendar        m_date  =   Calendar.getInstance();

    /* (non-Javadoc)
     * @see com.istana.kalendar.fixture.KalendarDay#getDate()
     */
    @Override
    public      Date            getDate()
    {
        return m_date.getTime();
    }

}

UIComponent ---

/**
 * 
 */
package com.istana.kalendar.fixture.jsf;

import javax.el.ValueExpression;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIOutput;

import com.istana.kalendar.fixture.KalendarDay;

/**
 * @author User
 *
 */
@FacesComponent     (value=UIDay.COMPONENT_TYPE)
public class UIDay  extends UIOutput
{
    static      final
    public      String          COMPONENT_TYPE      =   "com.istana.kalendar.fixture.jsf.Day";

    static      final
    public      String          COMPONENT_FAMILY    =   "com.istana.kalendar.fixture.jsf.Day";

    private     KalendarDay     m_model;

    private     String          m_title;

    @Override
    public      String          getRendererType() 
    {
        return UIDayRenderer.RENDERER_TYPE;
    }

    @Override
    public      String          getFamily() 
    {
        return COMPONENT_FAMILY;
    }

    public      KalendarDay     getModel()
    {
        KalendarDay     model = (KalendarDay) getStateHelper().eval("model");

        System.out.println("model "+model);

        return model;
    }

    public      void            setModel(KalendarDay model)
    {
        getStateHelper().put("model",model);
    }

    public      String          getTitle()
    {
        return (String) getStateHelper().eval("title");
    }

    public      void            setTitle(String title)
    {
        getStateHelper().put("title",title);
    }
}

UIComponentRenderer ---

/**
 * 
 */
package com.istana.kalendar.fixture.jsf;

import java.io.IOException;

import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;
import javax.faces.render.Renderer;

import com.istana.kalendar.fixture.KalendarDay;

/**
 * @author User
 *
 */
@FacesRenderer  (componentFamily    = UIDay.COMPONENT_FAMILY
                ,rendererType       = UIDayRenderer.RENDERER_TYPE
                )
public class UIDayRenderer extends Renderer
{
    static      final
    public      String      RENDERER_TYPE   =   "com.istana.kalendar.fixture.jsf.DayRenderer";

    @Override
    public      void        encodeBegin (FacesContext context,UIComponent component) 
        throws IOException 
    {
        UIDay uic = (UIDay) component;

        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("p", uic);
        /*
         * This is the call that triggers the println
         */
        writer.write("Day - "+uic.getModel().getDate());
    }

    @Override
    public      void        encodeEnd   (FacesContext context,UIComponent component) 
            throws IOException 
    {
        ResponseWriter writer = context.getResponseWriter();
        writer.endElement("p");
        writer.flush();
    }
}

kalendar.taglib.xml ---

<facelet-taglib 
    id="kalendar"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
    version="2.0"
>

    <namespace>http://istana.com/kalendar</namespace>
    <tag>
        <tag-name>day</tag-name>
        <component>
            <component-type>com.istana.kalendar.fixture.jsf.Day</component-type>
        </component>
    </tag>

</facelet-taglib>

【问题讨论】:

  • 为了排除一个和另一个,#{kalendarDay} 是否由 var 指定的迭代 UI 组件,如 &lt;h:dataTable&gt; 左右?您试图在自定义组件的代码中究竟在哪里访问它?听起来好像它确实在一个迭代的 UI 组件中,并且您试图在组件的构造函数中的视图构建期间访问它。
  • 这在任何迭代中都是独立的。后台bean如下:

标签: jsf-2


【解决方案1】:

我不确定为什么是 null,但症状表明 #{kalendarDay} 是在视图渲染期间指定的,而您尝试在视图构建期间对其进行评估。


所以也许这是一个阶段的事情?我应该在渲染器的解码中对此进行评估并在属性映射中设置值吗?对于实际值和值表达式的组合,我还是有点困惑。

您应该使用组件的encodeXxx() 方法或关联的渲染器(如果有)根据组件的属性/属性生成 HTML。

您应该使用组件的decode() 方法或关联的渲染器(如果有)根据与 HTML 表单提交一起发送的 HTTP 请求参数设置组件的属性/属性。


所以我的问题是我应该在哪里获取和评估模型的 valueExpression,我应该将评估结果存储在 UIComponent 中还是应该每次都简单地评估它?

从 JSF 2.x 开始,建议为组件属性显式指定 getter 和 setter,然后将它们委托给 UIComponent#getStateHelper()

public String getValue() {
    return (String) getStateHelper().eval("value");
}

public void setValue(String value) {
    getStateHelper().put("value", value);
}

public String getTitle() {
    return (String) getStateHelper().eval("title");
}

public void setTitle(String title) {
    getStateHelper().put("title", title);
}

public Object getModel() {
    return getStateHelper().eval("model");
}

public void setModel(Object model) {
    getStateHelper().put("model", model);
}

这就是您所需要的一切(请注意,根据 Javabeans 规范,getter 和 setter 必须与属性名称完全匹配)。然后在encodeXxx() 方法中调用getModel() 来获取(并评估)model 属性的值。

【讨论】:

  • 我做了一些改动,并以 KalendarDay model = (KalendarDay) getStateHelper().eval("model"); 为例System.out.println("模型"+模型);返回模型;结果仍然是“空”(对格式感到抱歉)。 (我认为您对编码/解码的解释表明我应该在别处寻找解决方案)。我应该从某处显式设置诸如 setModel 之类的值还是自动发生?
  • 您还没有弄清楚您究竟在哪里尝试访问这些属性。首先,您是在组件和/或相关渲染器的encodeXxx()decode() 方法中访问它们吗?如果没有,那么您只是在错误的时刻访问它。至于显式设置值,不,您不需要这样做,JSF 透明地执行此操作。
  • 好的,抱歉。您指出的方法我已经在 UIComponent 上实现了这些方法。如果我理解你所说的话,我认为这是正确的。我之前评论中的一小段代码是我对 getModel() 方法的实现。我在 UIComponentRenderer 的 encodeBegin(context,component) 方法中调用了这个 getModel() 方法。此调用触发 println 导致“null”,因此评估再次导致 null。
  • 听起来不错。到目前为止,我没有在提供的信息中看到为什么它会是null。如果您可以准备一份 SSCCE 并将其包含在您的问题中,是否有机会?
  • 我唯一的其他想法是因为我正在使用注释,我没有在 taglib.xml 文件中定义 标记。那么也许我需要这样做以使 JSF 能够了解自定义标记属性的存在、必需和类类型特征?
猜你喜欢
  • 2012-04-29
  • 1970-01-01
  • 2011-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-02
  • 2011-07-03
  • 2021-09-14
相关资源
最近更新 更多