My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Se încarcă...

Petition by Java EE Guardians

Twitter

vineri, 13 februarie 2015

Invoke an operation for each component in the component tree via OmniFaces Components.ForEach


[OmniFaces utilities] The forEachComponent() methods allows us to invokes an operation on every component in the component tree. This is a simplified version of regular component visiting that uses the builder pattern to provide the various optional parameters. Includes supports for only visiting components of a certain class type and two simplified functional interfaces / lambdas.

Do you need to accomplish a task as the ones listed below (or related to these) ?

·         Invoke an operation on every component in the component tree
·         Invoke an operation on every component in  a sub-tree of the component tree
·         Invoke an operation only on components with specific IDs
·         Invoke an operation only on components with specific type (e.g. UIInput, UIForm, etc)
·         Invoke an operation only on components with specific VisitHint
·         Invoke an operation on components that meet a combination of above restrictions

Well, if your answer is yes, that this post can be very useful for you. Most probably, until now, you solved such tasks directly via JSF VisitTree API (you can see an example of using this API in post "Use JSF VisitTree API to check a componentin the component tree"). OmniFaces combines this API with its collection of callback interfaces (Callback) to obtain simplified version of regular component visiting.
If you are not familiar with OmniFaces Callback, then you can find more details and an example in post "Subscribe With OmniFaces 2.1 (serializable) /2.0 (un-serializable) Callback-listeners to JSF System Events". For this post, you have to be familiar with these two callbacks:

·          callback which takes an argument
·         callback which takes one argument and returns one value
The OmniFaces approach for solving tasks as listed above is implemented as an OmniFaces utilities in the Components class. Here we have the following two methods available starting with OmniFaces 2.0:
Obviously, the "goodies" relies in ForEach class, which is a static class that encapsulates a simplified version of regular component visiting - please read the additional comments:

public static class ForEach {

 private FacesContext facesContext;      // the FacesContext instance
 private UIComponent root;        // the component where tree visiting starts
 private Collection<String> ids;         // IDs of the components that are visited
 private Set<VisitHint> hints;    // VisitHints IDs of the components that are visited
 private Class<?>[] types;        // types of the components that are visited

 public ForEach() {
  facesContext = Faces.getContext();
 }

 public ForEach(FacesContext facesContext) {
  this.facesContext = facesContext;
 }
 ...
}

Now, behind each of these private fields, we have a at least a public method for initializing them, as follows:
·         optional, we indicate the root component where tree visiting starts (when it is omitted OmniFaces uses the default value, UIViewRoot) via fromRoot():

public ForEach fromRoot(UIComponent root) {
 this.root = root;
 return this;
}

·         optional, we indicate the IDs of the components that are visited via havingIds() methods:

public ForEach havingIds(Collection<String> ids) {
 this.ids = ids;
 return this;
}

public ForEach havingIds(String... ids) {
 this.ids = asList(ids);
 return this;
}

·          optional, we indicate the VisitHints that are used for the visit via withHints() methods:

public ForEach withHints(Set<VisitHint> hints) {
 this.hints = hints;
 return this;
}

public ForEach withHints(VisitHint... hints) {

 if (hints.length > 0) {
     EnumSet<VisitHint> hintsSet = EnumSet.noneOf(hints[0].getDeclaringClass());
     for (VisitHint hint : hints) {
         hintsSet.add(hint);
     }

     this.hints = hintsSet;

 }
 return this;
}

·         optional, we indicate the types of the components that are to be visited via ofTypes() method:

@SafeVarargs
public final ForEach ofTypes(Class<?>... types) {
 this.types = types;
 return this;
}

Practically, we can call one of these methods or a logic combination of them to obtain the desired visiting model. Finally, we have three approaches to trigger the visit process (three invoke() methods implementations). So, we can pass the operation to be invoked for each visited component as:

·         a Callback.WithArgument<UIComponent> argument

public void invoke(final Callback.WithArgument<UIComponent> operation) {
 invoke(new VisitCallback() {
  @Override
  public VisitResult visit(VisitContext context, UIComponent target) {
   operation.invoke(target);
   return ACCEPT;
  }
 });
}

·         a Callback.ReturningWithArgument<VisitResult, UIComponent> argument

public void invoke(final Callback.ReturningWithArgument<VisitResult, UIComponent> operation) {
 invoke(new VisitCallback() {
  @Override
  public VisitResult visit(VisitContext context, UIComponent target) {
   return operation.invoke(target);
  }
 });
}

·         a VisitCallback argument

public void invoke(final VisitCallback operation) {
 VisitCallback callback = operation;
 if (types != null) {
     callback = new TypesVisitCallback(types, callback);
 }

 getRoot().visitTree(createVisitContext(getContext(), getIds(), getHints()), callback);
}

The TypesVisitCallback is an OmniFaces implementation of VisitCallback which takes into account the types of the components that are to be visited:

private static class TypesVisitCallback implements VisitCallback {

 private Class<?>[] types;
 private VisitCallback next;

 public TypesVisitCallback(Class<?>[] types, VisitCallback next) {
  this.types = types;
  this.next = next;
 }

 @Override
 public VisitResult visit(VisitContext context, UIComponent target) {
  if (isOneInstanceOf(target.getClass(), types)) {
      return next.visit(context, target);
  }
  return ACCEPT;
 }
}

Now, that we have the "rough" picture of what is happening, let's have several examples. For start we need the below JSF page snippet:
...
<h:body>
 <h:panelGroup id="myDivId" layout="block">
  <h:outputText value="New ATP Player"/>
 </h:panelGroup>

<h:form id="myFormId" binding="#{playerBean.uiForm}">
  <h:panelGrid id="panelId" columns="2">
   <o:outputLabel id="nameLabelId" for="nameId" value="Name"/>
   <h:inputText id="nameId" value="#{playerBean.name}"/>         
   <o:outputLabel id="surnameLabelId" for="surnameId" value="Surname"/>
   <h:inputText id="surnameId" value="#{playerBean.surname}"/>
   <o:outputLabel id="ageLabelId" for="surnameId" value="Age"/>
   <h:inputText id="ageId" value="#{playerBean.age}"/>              
   <o:outputLabel id="titleLabelId" for="titleId" value="Title" rendered="false"/>
   <h:inputText id="titleId" value="#{playerBean.title}" rendered="false"/>       
               
   <h:commandButton id="btnId" value="Save" action="#{playerBean.someAction(1,2,3 or 4)}"/> 
  </h:panelGrid>
 </h:form>               
</h:body>
...

So, we have a simple snippet of a JSF page, and we want to exploit the OmniFaces implementation for executing an operation for each component in the component tree. Pass the integer 1,2,3 or 4 to the someAction() method to switch between the below examples:

·         1 - suppress page rendering (this is the operation).  Visit from root UIViewRoot (default)

// 1 - suppress page rendering; visit from root
// use Callback.WithArgument<UIComponent>()
public void useCase_1(FacesContext fc) {
 // is like using fromRoot(fc.getViewRoot())
 Components.forEachComponent(fc).invoke(new Callback.WithArgument<UIComponent>() {
  @Override
  public void invoke(UIComponent component) {
   component.setRendered(false); // invoke any other operation
  }
 });
}

·         2 - suppress rendering for the components with IDs: surnameLabelId, surnameId, ageLabelId, and ageId. Visit from root with ID, myFormId (the above uiForm)

// 2 - suppress surnameLabelId, surnameId ,ageLabelId, ageId rendering; visit from form, myFormId
// use Callback.WithArgument<UIComponent>()
public void useCase_2(FacesContext fc) {
 // use havingIds(String... ids) if you don't like to use a collection
 Collection<String> ids = Arrays.asList("myFormId:surnameId", "myFormId:ageId", "myFormId:surnameLabelId", "myFormId:ageLabelId");
 Components.forEachComponent(fc).fromRoot(uiForm).havingIds(ids).invoke(new Callback.WithArgument<UIComponent>() {
  @Override
  public void invoke(UIComponent component) {
   component.setRendered(false); // invoke any other operation
  }
 });
}

·         display IDs only of rendered components (will skip components with IDs: titleLabelId and titleId.  Visit from root with ID, myFormId (the above uiForm), with VisitHint.SKIP_UNRENDERED

// 3 - display IDs only of rendered components (will skip titleLabelId and titleId); visit from form, myFormId, with VisitHint.SKIP_UNRENDERED
// use Callback.WithArgument<UIComponent>()
public void useCase_3(FacesContext fc) {
 // use withHints(VisitHint... hints) if you don't like to use a set
 Set<VisitHint> vhs = new HashSet<>(Arrays.asList(VisitHint.SKIP_UNRENDERED));
 Components.forEachComponent(fc).fromRoot(uiForm).withHints(vhs).invoke(new Callback.WithArgument<UIComponent>() {
  @Override
  public void invoke(UIComponent component) {
    // you should not see listed: titleLabelId and titleId               
    System.out.println(component.getClientId()); // invoke any other operation
  }
 });
}

·         suppress the rendering of all components of type UIInput and HtmlOutputLabel. Visit from root with ID, myFormId (the above uiForm), with VisitHint.SKIP_UNRENDERED

// 4 - suppress the rendering of all components of type UIInput and HtmlOutputLabel; visit from form, myFormId, with VisitHint.SKIP_UNRENDERED
// use Callback.WithArgument<UIComponent>()
public void useCase_4(FacesContext fc) {
 // use withHints(VisitHint... hints) if you don't like to use a set
 Set<VisitHint> vhs = new HashSet<>(Arrays.asList(VisitHint.SKIP_UNRENDERED));
 Components.forEachComponent(fc).fromRoot(uiForm).withHints(vhs).ofTypes(UIInput.class, HtmlOutputLabel.class).invoke(new Callback.WithArgument<UIComponent>() {
  @Override
  public void invoke(UIComponent component) {
   component.setRendered(false); // invoke any other operation
  }
 });
}

You can find the complete source on GitHub [ComponentsForEach]

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors