1

I want to have a composite that composes

<h:form id="f_imgA" >
        <h:graphicImage id="imgA"
            onclick="document.getElementById('#{k_imgA.clientId}').value=mods(event)"
            value="images/img?r=#{Math.random()}">
            <f:ajax event="click" listener="#{mBean.handleEvent}"
                execute="@this k_imgA" render="@this"></f:ajax>
        </h:graphicImage>
        <h:inputHidden id="k_imgA" binding="#{k_imgA}" value="#{mBean.keyX}" />
</h:form>

when I write

<comps:cimg imgId="imgA" />

The original purpose of this code is to send the modifier-states (Ctrl, Shift, Alt) to the server.

I have

<composite:interface>
    <composite:attribute name="imgId" />
</composite:interface>

<composite:implementation>
    <h:form id="f_#{cc.attrs.imgId}">
        <h:graphicImage id="#{cc.attrs.imgId}"
            onclick="document.getElementById('#{k_${cc.attrs.imgId}.clientId}').value=mods(event)"
            value="images/img?r=#{Math.random()}">
            <f:ajax event="click" execute="@this k_#{cc.attrs.imgId}"
                listener="#{mBean.handleEvent}" render="@this">
            </f:ajax>
        </h:graphicImage>
        <h:inputHidden id="k_#{cc.attrs.imgId}"
            binding="k_#{cc.attrs.imgId}" value="#{mBean.keyX}" />
    </h:form>
</composite:implementation>

which, quite expected, does not work. The offending expression is

#{k_${cc.attrs.imgId}.clientId}

which is intended to return the clientId of the hiddenInput with id k_imgA . As far as I know, EL cannot handle nested expressions like the one above, but it was worth a try. So is there a simple, straightforward way to get the clientId of k_imgA? I don't want to use more javascript, if this can be avoided.

Edit: don't get confused about #{Math.random()}, it works just because I have a bean called "Math".

The javascript function mods is given by

<h:outputScript target="body">function mods(event) {return  ((event.ctrlKey)?1:0)+((event.shiftKey)?2:0)+((event.altKey)?4:0)} </h:outputScript>
Gyro Gearloose
  • 1,056
  • 1
  • 9
  • 26

1 Answers1

1

You don't need to fiddle with all those _#{cc.attrs.imgId}. Just give the composite a fixed id. They are already naming containers themselves. JSF will then worry about the rest.

<composite:implementation>
    <h:form id="f">
        <h:graphicImage id="i" ... />
        <h:inputHidden id="k" ... />
    </h:form>
</composite:implementation>

Usage:

<comps:cimg id="imgA" />

Generated HTML:

<form id="imgA:f" ...>
    <img id="imgA:f:i" ... />
    <input id="imgA:f:k" ... />
</form>

As to the JS attempt, you'd best use element's own ID as base:

<h:graphicImage onclick="document.getElementById(id.substring(0,id.lastIndexOf(':')+1)+'k').value='...'" />

Or, easier, if the hidden element is guaranteed to be the direct sibling of the image element in the generated HTML DOM tree, then just grab it by Node#nextSibling:

<h:graphicImage onclick="nextSibling.value='...'" />
<h:inputHidden ... />

The binding will never work as in your attempted construct, and even then you'd best do it via a Map in request scope or a so-called backing component.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I have tried, (via copy&paste), but the result was that only the last copy worked. But maybe I'm missing some points in general understanding (oh, I'm quite sure that I miss a lot of points in general understanding, but there are so many of these that I can't figure out which one applies here). "nextSibling" looks like the most promising approach, also it isn't a general approach. Will have to do some experimentation based on your advice. This will take some time. – Gyro Gearloose Dec 23 '15 at 22:18
  • Not sure what you mean with "last copy". You only need to make sure you remove `binding` altogether, else they are overriding each other until the last one, and that kind of matches the described symptom. – BalusC Dec 23 '15 at 22:38
  • Yes, it's a binding problem. I want to use the composite more than once (why else should I bother making one?). The idea is from here http://stackoverflow.com/a/8656921/4142984, and it works fine as long as the binding keys are all different. Your suggested solution with javascript and substring will only work as long as the composites are not inside a NamingContainer. – Gyro Gearloose Dec 24 '15 at 10:35
  • 1
    Composites are naming containers themselves. That's exactly why it works and why you can reuse them without facing duplicate component ID errors over all place. I'm not seeing how my JavaScript example is naming container parent dependent, too. – BalusC Dec 24 '15 at 10:37
  • As far as I understand, this is only half of the story. If the composite is inside some other container, wouldn't the clientIds be outer:imgA:f:k or do I have a misconception there? – Gyro Gearloose Dec 24 '15 at 10:50
  • I don't see how given JS code would fail on that. Or haven't you actually tested it? – BalusC Dec 24 '15 at 10:58
  • OK, my fault, you check the name starting back to front, so other prepended stuff does not matter. – Gyro Gearloose Dec 24 '15 at 11:00
  • Using nextSibling is simpler, and it is working now, as *onclick="this.nextSibling.value=mods(event)"*, thank's a lot. Spinoff-question: is there really no way to use binding within composite components (except for hard-coded names)? – Gyro Gearloose Dec 24 '15 at 11:15
  • The "See also" list covers all spinoff questions. – BalusC Dec 24 '15 at 12:02