Getting same instance of `componentType` in composite component on every use

这一生的挚爱 提交于 2019-12-07 18:35:17

问题


Hi Have this Wierd Issue in which I am using a Composite Component which I wrote and I get values from the previous use of the backing bean of the CC (the componentType bean)

I don't know how to describe this better than just show the code. I'll try to be brief about it and cut the redundant parts: This is the Composite Component definition:

<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>

The CC bean defintion:

@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");
    }

}

And the use code:

<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>

The text of id controlMsg displays the correct values in #{group.values} while the control output inside the component of id inner_control_component shows the values from the previous use.

The values are correct the first time...

I guess it's a fundemental error in use of a CC bean, otherwise it could be a bug with MyFaces 2.1 (Which I'm using)


回答1:


The explanation for this behaviour is simple: there's only one component definied in the view. So there's also only one backing component with one model. Since the model is lazily loaded on first get, the same model is been reused in every iteration of a parent iterating component.

The <ui:repeat> doesn't run during view build time (as JSTL does), but during view render time. So there are physically not as many components in the view as items which are iterated by <ui:repeat>. If you were using <c:forEach> (or any other iteration tag which runs during view build time), then the composite component would have behaved as you'd expect.

You would like to change the way how the datamodel is kept in the backing component. You would like to preserve a separate datamodel for each iteration of a parent iterating component. One of the ways is to replace the model property as follows:

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;
}

See also:

  • What's the view build time?



回答2:


The problem described here is an old known isse in JSF, hidden by the usage of composite components. It is so important and so difficult, that instead answer here I create a detailed answer in a blog entry for this one: JSF component state per row for datatables

To keep this answer short, I'll say to you it is not a bug in MyFaces 2.1. Please use 2.1.1, because that is a quick bug fix version of 2.1.0. In JSF 2.1 there is a new property for h:dataTable called rowStatePreserved, and this scenario is just one case where "this little baby" becomes useful. Just replace ui:repeat with h:dataTable and add rowStatePreserved="true". That will do the trick. If you need to manipulate the model (add or remove rows) you can use tomahawk t:dataTable and t:dataList, but you will have to take an snapshot version for now. Note this is new stuff not available in any different JSF framework an the moment (JUN 2011).

If you need more info, keep tuned with MyFaces Team on Twitter or ask to the experts on MyFaces Users and Dev Mailing Lists.



来源:https://stackoverflow.com/questions/6485062/getting-same-instance-of-componenttype-in-composite-component-on-every-use

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!