Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find component by type in JSF

My question is related to this one Get all hidden input fields in JSF dynamically but it's not the same as I want to work with JSF and not plain HTML, and assuming that I have the following in my .xhtml file:

<h:inputHidden id="name1" value="SomeValue1"/>
<h:inputHidden id="name2" value="SomeValue2"/>

I developed a small code where I tried to get all the h:inputHidden tags dynamically and print their values to the console, but the problem is that I can't figure out a way how to make everythning dynamic. In my code I should know the form id if I want to Iterate over the uicomponents, how can I iterate over all the UIComponent in the component tree ? (I tried UIViewRoot#getChildren() but i get only the first childrens).

Here is the code snippet:

// formId is the id of my form
List<UIComponent> components = FacesContext.getCurrentInstance().getViewRoot().findComponent("formId").getChildren();
// A List of UIComponent where I am adding my Hidden Inputs
List<UIComponent> hiddenComponents = new ArrayList<UIComponent>();

for (UIComponent component : components) {

    // using the hidden inputs type in JSF: HtmlInputHidden
    if (component instanceof HtmlInputHidden) {
        hiddenComponents.add(component);
    }

}

for (UIComponent component : hiddenComponents) {

    // Printing the hidden inputs values for demonstration purposes
    System.out.println(((HtmlInputHidden)component).getValue());

}
like image 511
Tarik Avatar asked Feb 17 '15 17:02

Tarik


1 Answers

You also need to iterate over children of children, and their children, etcetera. You see, it's a component tree.

Here's a kickoff snippet of an utility method which does exactly that using tail recursion:

public static <C extends UIComponent> void findChildrenByType(UIComponent parent, List<C> found, Class<C> type) {
    for (UIComponent child : parent.getChildren()) {
        if (type.isAssignableFrom(child.getClass())) {
            found.add(type.cast(child));
        }

        findChildrenByType(child, found, type);
    }
}

Here's how you could use it:

UIForm form = (UIForm) FacesContext.getCurrentInstance().getViewRoot().findComponent("formId");
List<HtmlInputHidden> hiddenComponents = new ArrayList<>();
findChildrenByType(form, hiddenComponents, HtmlInputHidden.class);

for (HtmlInputHidden hidden : hiddenComponents) {
    System.out.println(hidden.getValue());
}

Or, better, use UIComponent#visitTree() which uses the visitor pattern. The major difference is that it also iterates over iterating components such as <ui:repeat> and <h:dataTable> and restores the child state for every iteration. Otherwise you would end up getting no value when you have an <h:inputHidden> enclosed in such component.

FacesContext context = FacesContext.getCurrentInstance();
List<Object> hiddenComponentValues = new ArrayList<>();
context.getViewRoot().findComponent("formId").visitTree(VisitContext.createVisitContext(context), new VisitCallback() {
    @Override
    public VisitResult visit(VisitContext visitContext, UIComponent component) {
        if (component instanceof HtmlInputHidden) {
            hiddenComponentValues.add(((HtmlInputHidden) component).getValue());
            return VisitResult.COMPLETE;
        } else {
            return VisitResult.ACCEPT;
        }
    }
});

for (Object hiddenComponentValue : hiddenComponentValues) {
    System.out.println(hiddenComponentValue);
}

See also:

  • How to collect values of an UIInput component inside an UIData component in bean's action method?
  • How do I attach a FacesMessage from the backing bean to a specific field in a ui:repeat?
  • Accessing dynamic UIComponents in JSF Managed Bean
  • JSF 2 -- Save All Valid Component Values

After all, it's probably easiest to just bind them to a bean property the usual way, if necessary inside an <ui:repeat>:

<h:inputHidden id="name1" value="#{bean.name1}"/>
<h:inputHidden id="name2" value="#{bean.name2}"/>
like image 148
BalusC Avatar answered Nov 14 '22 23:11

BalusC