We use facelets to create some custom ajaxy components. One of the behaviours we would like to mimic from the default components is that an id is optional, and an id is generated if it is not passed. I can already do it like this:
<ui:composition ...>
<div class="myComponent" id="#{jsfSupport.generateId(id)}">
...
</div>
</ui:composition>
I use JBoss el to call a support method (could use el functions as well):
public class JsfSupport {
public String generateId(String id) {
if (id==null || "".equals(id){
return FacesContext.getCurrentInstance().getViewRoot().createUniqueId();
}
return id;
}
}
The problem is that if I need that id somewhere in my javascript code in the component, I need to retrieve it again. So I thought I could do the following:
<ui:composition ...>
<c:set var="id" value="#{jsfSupport.generateId(id)}" />
<div class="myComponent" id="#{id}">
...
</div>
<script type="text/javascript">
document.getElementById('#{id}');
</script>
</ui:composition>
But that doesn't work. The id gets regenerated anyway and I get two different ones. Any ideas on what would be the ideal way to do this?
<c:set>
in Facelets means aliasing, not an assignment like in JSP. So every use of #{id}
is translated into a separate call to #{jsfSupport.generateId(id)}
, which results in problems you describe.
You could write your own version of <c:set>
tag which would evaluate the passed expression only once, and save the returned value:
public class SetOnceHandler extends TagHandler
{
private TagAttribute var;
private TagAttribute value;
public SetOnceHandler(TagConfig cfg)
{
super(cfg);
value = getRequiredAttribute("value");
var = getRequiredAttribute("var");
}
public void apply(FaceletContext ctx, UIComponent parent)
{
ctx.setAttribute(var.getValue(ctx), value.getObject(ctx));
}
}