X

Technical Articles relating to Oracle Development Tools and Frameworks

Locating JSF components by ID

Duncan Mills
Architect

An occasional problem for developers using JSF is how to locate a component given just it's ID. Now if you have the complete ID, which includes the computed location, for example:
Button B1 inside Region R1 inside page with template PT1 ==> PT1:R1:B1, you can hardcode that using the basic findComponent() API, or even better use the invokeOnComponent() API provided by the UIComponentBase (see the JavaDoc for more information).  However, what it you have no idea, at the time of writing the code, what the context is that the component that you want will be used in?  In that case, the only information that you might have is the basic ID of the component and you know nothing of the naming containers that contribute to the full unique ID.

 In this scenario I've seen a lot of examples where developers fall back to recursion. i.e. They grab the viewRoot from the FacesContext, get it's children, check to see if any of those children has the correct ID, if not they then get the children of each of those and check those and so on.
There is, however, a much neater, and more efficient way of doing this using the JSF 2.0 visitTree API. With the visitTree API, in a similar way to the invokeOnComponent() API, you provide a callback inner class which implements a visit() method. This method is processed for each component in the component tree. You then have the ability to control how the tree is traversed based on the return value that you provide from that visit method.
The valid return values are:

  • VisitResult.COMPLETE - stop the traversal all together
  • VisitResult.ACCEPT - continue with the traversal
  • VisitResult.REJECT - stop traversal for the current subtree, but continue with the rest

So here's a simple example of a helper method to invoke a ActionEvent on a commandItem such as <af:button> given just the basic id of that button. This might be the kind of thing you would want to do in a serverListener callback being invoked from a client side customEvent.

private static boolean queueActionOnCommandComponmentById(String commandId) {
if (commandId != null && commandId.length() > 0) {
  final String candidateId = commandId;
    FacesContext ctx = FacesContext.getCurrentInstance();
    VisitContext visitContext = VisitContext.createVisitContext(ctx);
    return UIXComponent.visitTree(visitContext, ctx.getViewRoot(), new VisitCallback(){
    public VisitResult visit(VisitContext context, UIComponent component) {
      try {
        if (component instanceof UIXCommand && component.getId().equals(candidateId)) {
          ActionEvent cmdEvent = new ActionEvent(component);
            cmdEvent.setPhaseId(PhaseId.INVOKE_APPLICATION);
            cmdEvent.queue();
            return VisitResult.COMPLETE;
          }
          return VisitResult.ACCEPT;
        } catch (Exception e) {
        e.printStackTrace();
          return VisitResult.COMPLETE;
        }
      }
    });
  }
        _logger.warning("Empty or null command component ID provided, no event queued");
        return false;

Of course this is a trivial example, and suffers like any of these techniques, from the risk that you might conceivably have more than one component with the same basic ID, so in this case I'd be sure that the ID assigned to the command button as already pretty unique. However, more complex uses of this traversal approach may be touching multiple components and that's where it really comes into it's own.

Anyway, I hope you get the message, never use the getChildren() recursion technique, use visitTree() instead. 

Join the discussion

Comments ( 1 )
  • guest Saturday, November 29, 2014

    Thanks for this nice tutorial. I have used getChidren() recursive technique in past, but no more of that, will use visitTree() now onwards.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.