tags:

views:

86

answers:

4

consider a simple composite component which takes an action parameter of some sort - a simple link 'prettifier' for example. i want to 'ajaxify' it.

   <composite:interface>
        <composite:attribute name="act" method-signature="java.lang.String action()"></composite:attribute>
        <composite:attribute name="text" required="true"></composite:attribute>
            <composite:clientBehavior  name="click" event="action"  targets="l"/>    </composite:interface>

   <composite:implementation>
        <h:commandLink id="l" act="#{cc.attrs.action}" immediate="true">            <b>#{cc.attrs.text}</b>         </h:commandLink>    </composite:implementation>

i expose an event through client behaviour. i use it like this:

<h:panelGroup layout="block" id="outside">

        #{mybean.otherdata} <br/>

                <mc:mylink text="Click click" action="#{mybean.click}" >
                    <f:ajax event="click" render="outside"/>"
                </mc:mylink><br/>

</h:panelGroup>

you can see what i want to do. i want to do an ajax render OUTSIDE the composite definition. just setting render to "outside" gives the dreaded " contains an unknown id" error.

yes i am aware of naming containers and i know we can prepend with a colon and specify an absolute path, but that's quite clunky. if i wrap it up in several more layers (which is the whole point) i'd have to chain these references together manually.

can i make some sort of relative reference like render="../outside" to skip the reference to the parent container of the component?

i did a jsf 1 app with a4j and this pattern was used all over the place.

A: 

If you are building your custom component using composite components, I'm pretty fure you'll have to use render="@all".

On the other hand, if you use the more traditional class-based custom components, you'll have freedom to walk the entire component tree, and update as you like.

Brian Leathem
nope. @all doesn't work - at least with mojarra. @form does.
james
A: 

after some tinkering heres one solution:

put a listener on the event.

<f:ajax event="click" listener="#{mycomp.listen}" render="#{mycomp.getParId('outside')}"/>"

implementation:

public void listen(AjaxBehaviorEvent event) {
    String clid=event.getComponent().getClientId();

    StringTokenizer st=new StringTokenizer(clid,":");

    StringBuilder sb=new StringBuilder(":");
    for (int x=1;x<st.countTokens();x++)
    {
        sb.append(st.nextToken()).append(":");
    }

    parId=sb.toString();
}

public String getParId(String suff) {
    //must precheck as id is prevalidated for existence. if not set yet set to form
    if (parId==null)
    {
        return "@form";
    }
    return parId+suff;
}

even so - why would you have to do this.

james
A: 

ok i've found an even better solution i'm happy with which makes use of the implicit 'component' mapping in the template. it allows basic relative mapping back from the composite component using '../' filesystem notation. it could be expanded to handle more but its all i need. works for execute parameter as well.

i'd like to thank me for figuring it out! again am i misunderstanding something? i shouldn't need to be figuring this out.

usage:

<mc:mylink text="Click me" action="#{blah.myaction}" render="../../pg" />

impl:

<composite:implementation>

    <h:commandLink id="l" action="#{cc.attrs.action}" immediate="true">
        <f:ajax render="#{pathProcessor.pathProcess(3, cc.attrs.render, component.clientId)}" />
                     #{simon.rand} . <b>#{cc.attrs.text}</b>
    </h:commandLink>

.....

pathprocessor method:

public static String pathProcess(int currentNesting, String path, String clid) {

    //System.out.println("clientid is "+clid);
    //System.out.println("path is "+path);
    //System.out.println("nesting is "+currentNesting);

    String backTok="../";
    String sepchar=":";

    StringBuilder src=new StringBuilder(path);
    int backs=0;
    int inc=backTok.length();

    while (src.indexOf(backTok,backs*inc)==backs*inc)
    {
        backs++;
    }

    //get rid of the source
    String suffix=src.substring(backs*inc);

    //add in internal nesting
    backs+=(currentNesting-4);

    StringTokenizer st=new StringTokenizer(clid,sepchar);

    StringBuilder sb=new StringBuilder(sepchar);
    for (int x=0;x<st.countTokens()-backs;x++)
    {
        sb.append(st.nextToken()).append(sepchar);
    }

    //backtracked path
    String p=sb.toString();

    //add suffix to the backtracked client path to get full absolute path
    String abs=p+suffix;

    return abs;
}

errrr - don't know why i used the stringbuilder when counting the backs but you get the idea. something like that

james
A: 

In JSF 2.0, you can use the implicit cc and component objects inside EL. In order to fetch the full client ID for any component do:

#{component.clientId}

To retrieve the client ID for a composite component do:

#{cc.clientId}

In the same way, you can also fetch parent with #{cc.parent}. In this case, this is probably what you want. For a bit longer answer see Acquire full prefix for a component clientId inside naming containers with JSF 2.0.

Tuukka Mustonen