每次使用时在复合组件中获取相同的“componentType”实例

Posted

技术标签:

【中文标题】每次使用时在复合组件中获取相同的“componentType”实例【英文标题】:Getting same instance of `componentType` in composite component on every use 【发布时间】:2011-09-23 00:37:04 【问题描述】:

嗨,有这个奇怪的问题,我正在使用我编写的 Composite Component,并且我从之前使用 CC 的支持 bean(componentType bean)中获取值

我不知道如何比仅显示代码更好地描述这一点。 我会尽量简要介绍一下并删除多余的部分: 这是Composite Component 的定义:

<cc:interface componentType="dynamicFieldGroupList">
   <cc:attribute name="coupletClass" />
   <cc:attribute name="form" default="@form"/>
   <cc:attribute name="list" type="java.util.List" required="true"/>
   <cc:attribute name="fieldNames" type="java.util.List" required="true" />
</cc:interface>

<cc:implementation>
    <h:dataTable value="#cc.model" var="currLine">
        <h:column>
            <h:outputText id="inner_control_component" value="Inner Look at currLine:#currLine"/>
        </h:column>
    </h:dataTable>
</cc:implementation>

CC bean 定义:

@FacesComponent(value = "dynamicFieldGroupList")
// To be specified in componentType attribute.
@SuppressWarnings( "rawtypes", "unchecked" )
// We don't care about the actual model item type anyway.
public class DynamicFieldGroupList extends UIComponentBase implements
        NamingContainer


    private transient DataModel model;

    @Override
    public String getFamily()
    
        return "javax.faces.NamingContainer"; // Important! Required for
                                                // composite components.
    

    public DataModel getModel()
    
        if (model == null)
        
            model = new ListDataModel(getList());
        

        return model;
    

    private List<Map<String, String>> getList()
     // Don't make this method public! Ends otherwise in an infinite loop
        // calling itself everytime.
        return (List) getAttributes().get("list");
    


以及使用代码:

<ui:repeat var="group" value="#currentContact.detailGroups">
    <h:panelGroup rendered="#not empty group.values">
        <h:outputText id="controlMsg" value=" list:#group.values" /><br/><br/>
        <utils:fieldTypeGroupList list="#group.values"
            fieldNames="#group.fields" coupletClass="utils" />
    </h:panelGroup>
</ui:repeat>

id controlMsg 的文本在#group.values 中显示正确的值,而在 id inner_control_component 的组件内的控制输出显示之前使用的值。

值第一次是正确的...

我猜这是使用 CC bean 的根本错误,否则可能是 MyFaces 2.1 的错误(我正在使用)

【问题讨论】:

【参考方案1】:

对这种行为的解释很简单:视图中只定义了一个组件。所以也只有一个支持组件和一个模型。由于模型在第一次获取时被延迟加载,因此在父迭代组件的每次迭代中都会重复使用相同的模型。

&lt;ui:repeat&gt; 不会在视图构建期间运行(就像 JSTL 那样),而是在视图渲染期间运行。因此,视图中的组件在物理上没有&lt;ui:repeat&gt; 迭代的项目那么多。如果您使用&lt;c:forEach&gt;(或在视图构建期间运行的任何其他迭代标记),那么复合组件的行为将与您预期的一样。

您想更改数据模型在支持组件中的保存方式。您希望为父迭代组件的每次迭代保留一个单独的数据模型。其中一种方法是替换model属性如下:

private Map<String, DataModel> models = new HashMap<String, DataModel>();

public DataModel getModel() 
    DataModel model = models.get(getClientId());
    if (model == null) 
        model = models.put(getClientId(), new ListDataModel(getList()));
    
    return model;

另见:

What's the view build time?

【讨论】:

谢谢!!我肯定会阅读更多内容以了解视图构建/渲染时间主题。您可以推荐任何具体的文章吗? 不客气。抱歉,没有想到具体的文章。我只能推荐《JSF 2.0: The Complete Reference》这本书来更好地从上到下理解 JSF。 对此很抱歉,但我太快了,无法将其标记为答案。实施您的解决方案后,我仍然遇到此错误。我现在正在研究这个..如果我发现会提供更多细节。再次感谢... EDIT 我似乎得到了前一个实例的值。检查是否是渲染问题。无论如何,这意味着您的解决方案是正确的,因此将其标记为答案:-) 我应该在您的解决方案中添加一个重要说明 - 将 h:datatablemodel 一起使用是有问题的,因为数据表不会导致调用 getModel 方法。这导致了一个解决方案,其中h:datatable 的值始终未更新。 h:datatable 显示上次 getModel 被另一个对象调用的结果。我通过添加访问model 的隐藏输入解决了这个问题,导致getModel 被调用。【参考方案2】:

这里描述的问题是 JSF 中一个古老的已知问题,被复合组件的使用所隐藏。它是如此重要和如此困难,所以在这里回答我在博客条目中为此创建了一个详细的答案:JSF component state per row for datatables

为了让这个答案简短,我会告诉你这不是 MyFaces 2.1 中的错误。请使用 2.1.1,因为这是 2.1.0 的快速错误修复版本。在 JSF 2.1 中,h:dataTable 有一个名为 rowStatePreserved 的新属性,这种情况只是“这个小宝贝”变得有用的一种情况。只需将 ui:repeat 替换为 h:dataTable 并添加 rowStatePreserved="true"。那会成功的。如果您需要操作模型(添加或删除行),您可以使用 tomahawk t:dataTable 和 t:dataList,但您现在必须获取快照版本。请注意,这是目前任何其他 JSF 框架都没有的新内容(2011 年 6 月)。

如果您需要更多信息,请继续关注MyFaces Team on Twitter 或通过MyFaces Users and Dev Mailing Lists 咨询专家。

【讨论】:

谢谢。我尝试升级到 2.1.1 并使用 h:datatable 没有成功。我真的不能使用战斧。 好吧,我没注意“model”变量的使用。在 JSF 2.1 中,现在存在一个名为 TransientStateHelper 的接口,它有 2 个方法:getTransient 和 putTransient。从 JSF 规范的角度来看,使用该映射和 rowStatePreserved="true" 是首选,但请注意 MyFaces 中 UIData 的 impl 使用 getClientId() 使用类似的 hack。从外部包装列表是可疑的,因为存在与 dataTable 相关的规则(如果不存在验证错误,则在 encodeBegin 之前重新获取模型)。在我看来,使用 #cc.list 而不是 #cc.model 更好。 谢谢,非常有趣。我不知道如何使用新界面。是否有可以用作参考的示例代码? (或一些好的文档?) JSF 2.1 相对较新。 Mojarra 的第一个版本于 3 月发布,MyFaces 于 5 月发布。我不知道有任何文章谈论这个,但您可以查看MyFaces Documentation Index 并单击 JSF 2.1 文档以查看新增内容。简而言之,该接口允许您使用类似地图的合约,因此您可以在组件内部调用 getTransientStateHelper().putTransient(...),避免使用瞬态变量并允许实际的每行组件状态。一个例子是 UIForm 提交的属性。对于您的问题,请不要将模型存储在 cc

以上是关于每次使用时在复合组件中获取相同的“componentType”实例的主要内容,如果未能解决你的问题,请参考以下文章

反应:呈现组件时在浏览器中获取运行时错误/警告

将自定义组件小部件动态添加到 Android 中的布局中

每次使用相同的给定道具 React Memo 渲染

正确使用 Facelet 模板和复合组件

当结果具有相同分数时在 Elasticsearch 中分页

当用户按下可编辑的 UI 组件时在 Linux 中自动启动键盘