Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF Composite component <f:ajax> contains an unknown id - cannot locate it in the context of the component

I'm trying to update a parent component from a composite component event using f:ajax.

The composite component is here:

<cc:interface>
    <cc:attribute name="update" />
    <cc:attribute name="customid" required="true"/>
    <cc:attribute name="val" required="true"/>
    <cc:attribute name="selectedvalue" required="true"/>
</cc:interface>
<cc:implementation>
    <h:panelGrid columns="2" style="font-size: 10px" >
        <p:selectOneMenu id="#{cc.attrs.customid} value="#{cc.attrs.selectedvalue}">
            <f:selectItems value="#{cc.attrs.val}"/>
            <f:ajax event="change" render="#{cc.attrs.update" />
        </p:selectOneMenu>
        <p:commandButton type="button" icon="ui-icon-plus" onclick="dlg.show();" />
    </h:panelGrid>
</cc:implementation>

Now when using this component as follows:

<h:form>
    <ez:combo customid="make" val="#{vehicleBean.makes}" selectedvalue="#vehicleBean.vehicle.make}" update="model"  />
    <p:selectOneMenu id="model" value="#{vehicleBean.vehicle.model}">
        <f:selectItems value="#{vehicleBean.models}" />
    </p:selectOneMenu>
</h:form>

I get the following error:

contains an unknown id 'model' - cannot locate it in the context of the component make

like image 402
Anurag Singh Avatar asked Mar 25 '12 08:03

Anurag Singh


2 Answers

Since the component to update is outside the cc you have to address it in a different way. First give your form an id:

<h:form id="myform">

Then address the target component from your cc like this:

render=":myform:model"

Notice the trailing colon which lets JSF search the attribute from the document root.

like image 167
Matt Handy Avatar answered Nov 10 '22 10:11

Matt Handy


I had the same problem a while ago, just for information, I checked the jsf mojara implementation's sources; here is how it seems to work: The class ajaxBehaviorRendered, when encounters a f:ajax element to render, analyses the content of the render attribute through its method getResolvedId:


private static String getResolvedId(UIComponent component, String id) {

        UIComponent resolvedComponent = component.findComponent(id);
        if (resolvedComponent == null) {
...

The point is the method findComponent: this one needs of base component base as a strat point for searching in the component tree. If the identifier begins with the char ":", the component base is the viewRoot.



    UIComponent base = this;
     if (expr.charAt(0) == sepChar) {
        // Absolute searches start at the root of the tree
        while (base.getParent() != null) {
            base = base.getParent();
         }
        expr = expr.substring(1);
    }

Else the base component is the current component's closest parent of type NamingContainer (that is, your composite component where you are defining your ajax component).



    //Treat remainder of the expression as relative
    else if (!(base instanceof NamingContainer)) {
        // Relative expressions start at the closest NamingContainer or root
        while (base.getParent() != null) {
            if (base instanceof NamingContainer) {
                break;
            }
            base = base.getParent();
        }
    }

Then, in both cases, it starts to search the component with the given identifier from this start.

This behaviour is the one specified from jsf.

From my point of you, if you need to reference a component outside the composite, you have to define the complete name, usind the prefix ':' followed by the 'cc.clientId' attribute.

like image 4
Francois Gergaud Avatar answered Nov 10 '22 09:11

Francois Gergaud