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}">
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