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

Petition by Java EE Guardians

Twitter

joi, 5 februarie 2015

Use Lambda Expressions (EL 3.0) with JSF 2.2 Artifacts

EL 3.0 (JSR 341, part of Java EE 7) represents a major boost of EL 2.2. The main features of EL 3.0 are as follows:

• New operators +, =, and ;
• Lambda expressions
• Collection objects support
• An API for standalone environments

In order to get familiar with these new features you can check the examples from "JSF 2.2 and Expression Language 3.0 (EL 3.0)- over 50 examples". In this post we will take a look at writing lambda expressions to solve server-side tasks. Per example, let's check the below code:

...
<h:outputLabel for="name" value="First Name" />
<h:inputText id="name" label="First Name" required="true" value="#{dataBean.name}" />
<h:message for="name"/>
...

We know that the presence of the label attribute makes the difference when the validation fails and instead of seeing a message of type:

j_idt5:name: Validation Error: Value is required.

we will see a message of type:

First Name: Validation Error: Value is required.

When we have multiple inputs, and the value of input label is always equal to the value of output label, the redundancy of using label attribute is obvious. But, if you are an OmniFaces fan, you already know that this problem is solved via <o:outputLabel> tag. Check out the OmniFaces ShowCase.

Now, let's suppose that we want to use lambda expressions to generate the label attribute for each case as below:

...
<h:outputLabel for="name" value="First Name" />
<h:inputText id="name" required="true" value="#{dataBean.name}" />
<h:message for="name"/>
...

This can be achieved by writing a lambda expression directly in page. Per example, we can try to loop the component tree, and find all HtmlOutputLabel components. For each such component, extract the value of the for attribute and use it to find the linked input. Finally, add the label attribute to the linked input and set its value as the value of the HtmlOutputLabel.

·         transform the component tree in a stream and use this stream as the parameter for the lambda function, named findAllOutputLabel():

#{findAllOutputLabel(facesContext.viewRoot.children.stream());''}

·          loop this stream inside the findAllOutputLabel() function, and  for each item, call another lambda function, named checkOutputLabel():

#{findAllOutputLabel = (treeStream) -> treeStream.forEach((t) -> checkOutputLabel(t));''}

·         now, in checkOutputLabel() function, each time we find a component of type HtmlOutputLabel, we call another lambda function, named findInputOfLabel(). We pass to this function, the values of for and value attributes, and the parent of this component. When the checked component is not of type HtmlOutputLabel, we call the findAllOutputLabel() function, and pass to it the stream obtained from this component children. This create a recursive mechanism.

#{checkOutputLabel = (treeItem) -> treeItem['class'].simpleName eq 'HtmlOutputLabel' ? findInputOfLabel(treeItem.attributes.get('for'), treeItem.attributes.get('value'), treeItem.getParent()):findAllOutputLabel(treeItem.children.stream());''}

·         finally, in the findInputOfLabel() we find the linked input component and add the label attribute with the specific value

#{findInputOfLabel = (forAttrV, valueAttrV, parent) -> forAttrV ne null and valueAttrV ne null ? (c = parent.findComponent(forAttrV); c ne null ? (c.attributes.get('label') eq null ? c.attributes.put('label', valueAttrV):''):''): '';''}

So, the final code will look like this:

#{findInputOfLabel = (forAttrV, valueAttrV, parent) -> forAttrV ne null and valueAttrV ne null ? (c = parent.findComponent(forAttrV); c ne null ? (c.attributes.get('label') eq null ? c.attributes.put('label', valueAttrV):''):''): '';''} #{checkOutputLabel = (treeItem) -> treeItem['class'].simpleName eq 'HtmlOutputLabel' ? findInputOfLabel(treeItem.attributes.get('for'), treeItem.attributes.get('value'), treeItem.getParent()):findAllOutputLabel(treeItem.children.stream());''} #{findAllOutputLabel = (treeStream) -> treeStream.forEach((t) -> checkOutputLabel(t));''} #{findAllOutputLabel(facesContext.viewRoot.children.stream());''}

Let's see some other lambdas:

·         get in a JavaScript object the list of clientIds that belongs to invalid components of a form:

...
<h:panelGroup id="validationId" layout="block">           
 <h:panelGroup layout="block" rendered="#{facesContext.postback}">                             
  #{componentIdsJSON = '\'[';''}
  #{currentRequestParameters = facesContext.externalContext.requestParameterMap;''}
               
  <!-- Each invalid input is added in the JSON object -->
  #{checkInputValidityFunction = (treeItem, submittedFormId) -> treeItem['class'].simpleName eq 'HtmlInputText' or treeItem['class'].simpleName eq 'HtmlSelectOneListbox' ? //or ... add here a check for each component type in form (!treeItem.valid ? (treeItem.clientId.startsWith(submittedFormId) ? (componentIdsJSON = componentIdsJSON += "{\"cid\":" += "\"" += treeItem.clientId += "\"}" += ','):''):''):detectTreeFunction(treeItem.children.stream(), submittedFormId);''}                              
#{detectTreeFunction = (treeStream, submittedFormId) -> treeStream.forEach((t)->checkInputValidityFunction(t, submittedFormId)); ''}               
                
  <!-- Find the submitted form by inspecting the request map parameter -->
  #{checkParameterTypeFunction = (parameterAsComponent) -> (parameterAsComponent['class'].simpleName eq 'HtmlForm' ? detectTreeFunction(parameterAsComponent.children.stream(), parameterAsComponent.clientId):'');''}
  #{detectSubmittedFormFunction = (requestMapStream) -> requestMapStream.forEach((t) -> t.trim().length() gt 0 ? checkParameterTypeFunction(facesContext.viewRoot.findComponent(t)):'');''}
               
  <!-- Starting point -->
  #{detectSubmittedFormFunction(currentRequestParameters.values().stream());''}               
               
  #{componentIdsJSON.endsWith(',') ? (componentIdsJSON = componentIdsJSON.substring(0, componentIdsJSON.length() - 1) += ']\'') : (componentIdsJSON = componentIdsJSON += ']\'');''}

  <h:outputScript target="#{facesContext.partialViewContext.ajaxRequest ? 'form' : 'body'}" 
                  rendered="#{facesContext.postback}">                   
    // do something with invalid IDs
                   
  </h:outputScript> 
 </h:panelGroup>
</h:panelGroup>
...

In AJAX based forms, you need to add the validationId on render:

<h:form>
 ...
 <h:commandButton value="Register">                  
  <f:ajax execute="@form" render=":validationId @form"/>
 </h:commandButton>
</h:form>

·         extract all view parameters

#{vdl = facesContext.application.viewHandler.getViewDeclarationLanguage(facesContext, view.viewId);''} #{viewMetadata = vdl.getViewMetadata(facesContext, view.viewId);''} #{allViewParams = viewMetadata.getViewParameters(view);''}

·         filter a list (display only items that respect some conditions). Per example, add in PrimeFaces Galleria, only a subset of photos:

//in bean, MyGalleryBean - all photos
private List<MyPhoto> photos = new ArrayList<>();

// getters and setters for photos


// in JSF page
<p:galleria id="galleriaId"
            value="#{myGalleryBean.photos.stream().filter((i)->i.selected eq true).toList()}"
            var="t"
            ...>
 <p:graphicImage name="photos/#{t.name}" title="#{t.name}"/>
</p:galleria>

Note Using lambda expressions are not easy to write and debug, are not elegant and doesn't fit any situation, but, sometimes, you can solve tasks that usually are written in a Java class. 

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