Lars Röwekamp | open knowledge GmbH | @mobileLarson Hidden Features JSF 2 Best Practices
Jun 24, 2015
Lars Röwekamp | open knowledge GmbH | @mobileLarson
Hidden Features
JSF 2
Best
Pra
ctic
es
Don‘t ask me, RTFS!
„Come on, that‘s way to much.“*
*JSF 2 Spec: > 500P
Bookmarking
Validation
Ajax
ComponentsBehavior
Stuff
Ajax
It‘s sooo easy!
Ajax
2.) „Was soll gerendert werden?“
1.) „Was soll ausgeführt werden?“
Ajax> Asynchrones Request/Response Handling> Partitial Execute/Rendering via Lifecycle> JSF Component Tree > Ajax Request Status Handling> Ajax Request Error Handling
> Easy to use: f:ajax-Tag > Total control: jsf.ajax.request( )
Ajax<!-- Das AJAX TAG --><h:form ...> <h:commandButton value=“Update“> <f:ajax execute=“@form“ render=“updateMe“ /> </h:commandButton>
<h:outputTextField value=“#{aBeansValue}“ id=“updateMe“ /></h:form>
Ajax<f:ajax execute = „wen ausführen?“ render = „wen updaten?“ event = „auf was reagieren?“ listener = „wen interessiert‘s noch?“ onevent = „zusätzliche JavaScript Callback-Funktion“ onerror = „zusätzliche JavaScript Callback-Fehler-Funktion“ />
Ajax<!-- Das AJAX TAG (Master / Detail) --><h:selectOneMenu id="master" value="#{demo.master}"> <f:selectItems value="#{demo.masterItems}"/> <f:ajax render="detail" listener="#{demo.masterChanged}"/></h:selectOneMenu>
<h:selectOneMenu id="detail" value="#{demo.detail}"> <f:selectItems value="#{demo.detailItems}"/></h:selectOneMenu>
Ajax<f:ajax execute = „wen ausführen?“ render = „wen updaten?“ event = „auf was reagieren?“ listener = „wen interessiert‘s?“ onevent = „zusätzliche JavaScript Callback-Funktion“ onerror = „zusätzliche JavaScript Callback-Fehler-Funktion“ />
Ajax<f:ajax execute = @this render = @none event = action (Button/Link) valueChange (sonst) listener = @none onevent = status: begin, complete, success source: triggering DOM event responseCode, responseText/XML/>
Ajax<!-- Die JavaScript API (Master/Detail) --><h:selectOneMenu id="master" value="#{demo.master}" valueChangeListener="#{demo.masterChanged}" onChange="jsf.ajax.request( this, event, {render: detail});"/>
<h:selectOneMenu id="detail" value="#{demo.detail}"> <f:selectItems value="#{demo.detailItems}"/></h:selectOneMenu>
Ajax<!-- Die JavaScript API (Master/Detail) --><h:selectOneMenu id="master" value="#{demo.master}" valueChangeListener="#{demo.masterChanged}" onChange="jsf.ajax.request( this, event, {render: detail});"/>
<h:selectOneMenu id="detail" value="#{demo.detail}"> <f:selectItems value="#{demo.detailItems}"/></h:selectOneMenu>
Source Option(s)Event
AjaxPitfalls> inner/outer Component IDs> Ajax in Custom Components> Ajax Status Feedback
„Want some cool stuff?“
Ajax<!-- axaj response manipulation via PartitialResponseWriter (PrimeFaces style)--><partial-response> <changes> <update id="abc">...</update> <update id="xyz“>...</update> <extension ln="primefaces" type="args"> {"loggedIn":false} </extension> </changes></partial-response>
http://www.primefaces.org/showcase/ui/dialogLogin.jsf
Ajax<!-- axaj response handling inside JSF view (PrimeFaces style)--><script type="text/javascript"> function handleLoginRequest(xhr, status, args) { if(args.validationFailed || !args.loggedIn) { jQuery('#dialog') .effect("shake", { times:3 }, 100); } else { dlg.hide(); jQuery('#loginLink').fadeOut(); } } </script>
Ajax// PartitialResponseWriter (PrimeFaces style)
@Overridepublic void endDocument() throws IOException { Map<String, String> attributes = new HashMap<String, String>(); attributes.put("ln", "primefaces"); attributes.put("type", "args"); startExtension(attributes); write("{"loggedIn":false}"); endExtension(); super.endDocument();}
Behavior
Behavior> Idee: Komponente um clientseitige Funktionalität erweitern, die vom Autor ursprünglich nicht vorgesehen war.
> Mittel: JSF Behavior API zur Erweiterung beliebiger Komponenten um Client-side Scripting
Behavior<!-- Behavior in Action --><h:form ...> <h:commandButton value=“Update“> <f:ajax execute=“@form“ render=“updateMe“ /> </h:commandButton>
<h:outputTextField value=“#{aValue}“ id=“updateMe“ /></h:form>
Standard Behavior
Behavior> Client-side Validation> Client-side Logging> DOM & Style Manipulation> Animationen & visuelle Effekte> Alerts & Confirmation Dialoge> Lazy Data Fetching> Integration mit 3rd Party Libraries> ...
BehaviorMain Player
> ClientBehavior a.k.a. Script Generator: zuständig für Generierung von passendem Skript
> ClientBehaviorHolder a.k.a. Vermittler: zuständig für das Wiring zwischen Komponente, Event und ClientBehavior
Behavior<!-- BEHAVIOR in action -->
// chain of behaviors <h:commandButton value=“Update“>
// 1. ask user for permission <mystuff:confirm event=“click“ />
// 2. if YES send AJAX call <f:ajax event=“click“ render=“updateMe“ />
</h:commandButton>
„My“ Behavior
Standard Behavior
Behaviorpackage de.openknowledge.example.behavior; @FacesBehavior(xyz.behavior.Confirm)public class ConfirmBehavior
extends ClientBehaviorBase {
@Override public String getScript(
ClientBehaviorContext behaviorContext) { return “return confirm(‘Are your sure?‘)“;
}}
„My“ Behavior
Behavior<?xml version='1.0' encoding='UTF-8'?><facelet-taglib xmlns="..." version="2.0"> <namespace>http://xyz.de/mystuff</namespace> <tag> <tag-name>confirm</tag-name> <behavior> <behavior-id> xyz.behavior.Confirm </behavior-id> </behavior> </tag></facelet-taglib>
Facelets TagLib
in WEB-INF o. META-INF
Server-side Action?
Behavior<!-- BEHAVIOR with server-side action -->
// input field with behavior <h:inputText value=“#{someValue}“>
// inject jsf.ajax.request/show suggestions <foo:suggest suggestions=“#{serverSuggestions}“ />
</h:inputText>Server-side Action
Behavior// BEHAVIOR with server-side action
// participate in request decodingpublic void decode(FacesContext context, UIComponent uiComponent) {
// create suggestion list via // behavior directly or via service or ... ...}
Server-side Action
Component
It‘s so easy, again!
Components<html xmlns=“http://www.w3.org/1999/xhtml“ xmlns:composite=“.../jsf/composite“ > <!--INTERFACE --> <composite:interface> ... </composite:interface>
<!--IMPLEMENTATION --> <composite:implementation> ... </composite:implementation></html>
Komponente liegt unter: ./resources/comp/util/myComp.xhtml
Comp Interface
Comp Implementation
Components<html xmlns=“http://www.w3.org/1999/xhtml“ ... xmnls:util= “http://java.sun.com/jsf/composite/ comp/util“ > ... <util:myComp ... /> ...
</html>
Komponente liegt unter: ./resources/comp/util/myComp.xhtml
Comp in Action
Components> XHTML plus> Interface & Implementation> Convention over Configuration> Convention over Code
> Pitfall „couldn‘t find ID“> Pitfall „couldn‘t find ACTION“> Pitfall „couldn‘t add VALIDATOR “> Pitfall „couldn‘t add CHILDS“> Pitfall „couldn‘t add I18N“
Components> XHTML plus> Interface & Implementation> Convention over Configuration> Convention over Code
> Pitfall „couldn‘t find ID“> Pitfall „couldn‘t find ACTION“> Pitfall „couldn‘t add VALIDATOR “> Pitfall „couldn‘t add CHILDS“> Pitfall „couldn‘t add I18N“
Components
Components
Components<html xmlns= ... > <!--INTERFACE --> <composite:interface> <composite:attribute name=“user“ /> <composite:attribute name=“userLabel“ /> <composite:attribute name=“pwdLabel“ /> <composite:attribute name=“loginBtnLabel“/> <composite:attribute name=“action“ method-signature=“java.lang.String action()“/> </composite:interface>
...
</html>
Components<html xmlns= ... > <!--INTERFACE --> <composite:interface> <composite:attribute name=“user“ /> <composite:attribute name=“userLabel“ /> <composite:attribute name=“pwdLabel“ /> <composite:attribute name=“loginBtnLabel“/> <composite:attribute name=“action“ method-signature=“java.lang.String action()“/> </composite:interface>
...
</html>Reserved Qualifier!
Components<html xmlns= ... > <!--INTERFACE --> <composite:interface> <composite:attribute name=“user“ /> <composite:attribute name=“userLabel“ /> <composite:attribute name=“pwdLabel“ /> <composite:attribute name=“loginBtnLabel“/> <composite:attribute name=“loginAction“ method-signature=“java.lang.String action()“/> </composite:interface>
...
</html>
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <h:form id=“form“> <h:panelGrid columns=“2“> #{cc.attrs.namePrompt} <h:inputText id=“name“ value=“#{cc.attrs.user.name}“/> ... </h:panelGrid> <h:commandButton id=“loginBtn“ value=“#{cc.attrs.loginBtnLabel}“ action=“#{cc.attrs.loginAction}“ /> </h:form> <p>Die Super-Login-Komponente von open knowledge</p> </composite:implementation/></html>
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <h:form id=“form“> <h:panelGrid columns=“2“> #{cc.attrs.namePrompt} <h:inputText id=“name“ value=“#{cc.attrs.user.name}“/> ... </h:panelGrid> <h:commandButton id=“loginBtn“ value=“#{cc.attrs.loginBtnLabel}“ action=“#{cc.attrs.loginAction}“ /> </h:form> <p>Die Super-Login-Komponente von open knowledge</p> </composite:implementation/></html>
Dependency to User
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <h:form id=“form“> <h:panelGrid columns=“2“> #{cc.attrs.namePrompt} <h:inputText id=“name“ value=“#{cc.attrs.user.name}“/> ... </h:panelGrid> <h:commandButton id=“loginBtn“ value=“#{cc.attrs.loginBtnLabel}“ action=“#{cc.attrs.loginAction}“ /> </h:form> <p>Die Super-Login-Komponente von open knowledge</p> </composite:implementation/></html>
Dependency to User
I18N
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <h:form id=“form“> <h:panelGrid columns=“2“> #{cc.attrs.namePrompt} <h:inputText id=“name“ value=“#{cc.attrs.user.name}“/> ... </h:panelGrid> <h:commandButton id=“loginBtn“ value=“#{cc.attrs.loginBtnLabel}“ action=“#{cc.attrs.loginAction}“ /> </h:form> <p>Die Super-Login-Komponente von open knowledge</p> </composite:implementation/></html>
Dependency to User
I18N
id=“form:name“
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <h:form id=“form“> <h:panelGrid columns=“2“> #{cc.attrs.namePrompt} <h:inputText id=“name“ value=“#{cc.attrs.user.name}“/> ... </h:panelGrid> <h:commandButton id=“loginBtn“ value=“#{cc.attrs.loginBtnLabel}“ action=“#{cc.attrs.loginAction}“ /> </h:form> <p>Die Super-Login-Komponente von open knowledge</p> </composite:implementation/></html>
Dependency to User
I18N
id=“form:name“
Child Tags, Facets,...?
Components<html xmlns= ... > <!--INTERFACE --> <composite:interface> <composite:attribute name=“user“ /> <composite:attribute name=“userName“ /> <composite:attribute name=“userPwd“ /> ...
</composite:interface>
...
</html>
Dependency to User
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <h:form id=“form“> ... </h:form>
<p>Die Super-Login-Komponente von open knowledge</p> <p>#{cc.resourceBundleMap.myFooterText}</p> </composite:implementation/></html>
I18N
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <h:form id=“form“> ... </h:form>
<p>Die Super-Login-Komponente von open knowledge</p> <p>#{cc.resourceBundleMap.myFooterText}</p> </composite:implementation/></html>
I18N
Components<html xmlns= ... > <!--INTERFACE --> <composite:interface>
<composite:attribute name=“userName“ /><composite:attribute name=“userPwd“ />
<composite:editableValueHolder name=“userName“ targets=“form:name“/> <composite:editableValueHolder name=“userPwd“ targets=“form:pwd“/> <composite:editableValueHolder name=“allFields“ targets=“form:name form:pwd“/> <composite:actionSource name=“loginButton“ targets=“form:loginBtn“/> ... </composite:interface> ...</html>
Expose Components
Components<html xmlns= ... > ... <!--IMPLEMENTATION --> <composite:implementation> <composite:renderFacet name=“header“ /> <h:form id=“form“> ... </h:form> <composite:insertChildren/> </composite:implementation/></html>
Child Tags
Facets
Bookmark
BookmarksJSF erlaubt „Bookmarking“, aber ...
> URLs scheinen hinterher zu hängen> URLs sind unschön> URLs implizieren i.d.R. einen State
Bookmarks
Bookmarks
Bookmarks
BookmarksJSF 1.x „Bookmarking“
> <h:outputLink> für GET> PhaseListener zur Manipulation> redirect um aus Post ein Get zu machen
BookmarksJSF 1.x „Bookmarking“
> <h:outputLink> für GET> PhaseListener zur Manipulation> redirect um aus Post ein Get zu machen
JSF 2.x „Bookmarking“
> <h:link> oder <h:button> für GET> <f:viewParam> zum Setzen von Params> <f:event type=“preRenderView“ zum Laden von benötigten Daten
Bookmarks
Bookmarks
<!-- ! Bookmarkable link for user, e.g.: ! <a href=“/context/user.xhtml?id=1234“>..</a>!-->!<h:link value=“Details von #{user.name}“! outcome="user">! <f:param name="id" value="#{user.id}" />!</h:link>!
Step 1: Create URL
userList.xhtml
Bookmarks<!-- ! Use bookmarkable link for user, e.g.: ! <a href=“/context/user.xhtml?id=1234“>..</a>!-->!<f:metadata>! <f:viewParam name="id" ! value="#{userManager.userId}" />! <f:event type="preRenderView" ! listener="#{userManager.loadUser}" />!</f:metadata>!<h:head>...</h:head>!<h:body>! ... <!-- display user details -->!</h:body>!
Step 2: Use URL
user.xhtml
Bookmarks<!-- ! Use bookmarkable link for user, e.g.: ! <a href=“/context/user.xhtml?id=1234“>..</a>!-->!<f:metadata>! <f:viewParam name="id" ! value="#{userManager.userId}" />! <f:viewAction action="#{userManager.loadUser}" />!!</f:metadata>!<h:head>...</h:head>!<h:body>! ... <!-- display user details -->!</h:body>!
Step 2: Use URL
ab JSF 2.2!
user.xhtml
Geht da noch mehr?
JSF 2.x URIs
> Ugly www.demo.de/faces/showCust?id=54
> Nice www.demo.de/customer/meier/hans
Bookmarks
JSF 2.x URIs
> WTH? www.demo.de/list?type=cust&f=a&t=d
> Ahh, alles klarwww.demo.de/customerlist/from/a/to/d
Bookmarks
JSF meets
PrettyFaces
Pretty Faces - URL Rewriting
> localhost:8080/faces/start.xhtml> localhost:8080/start
<url-mapping id="start“> <pattern value="/start" /> <view-id>/faces/start.xhtml</view-id></url-mapping>
Bookmarks
Pretty Faces - URL Rewriting
> .../faces/cust/d.xhtml?c=mobileLarson > .../customer/mobileLarson
<url-mapping id=“customerDetails“> <pattern value=“/customer/ #{name : custBean.username}" /> <view-id>/faces/cust/d.xhtml</view-id></url-mapping>
Bookmarks
Pretty Faces - URL Rewriting
> .../customer/mobileLarson
<url-mapping id=“customerDetails“> <pattern> ... </pattern> <view-id> ... </view-id> <action>#{custBean.loadCust}</action></url-mapping>
Bookmarks
Validation
Ready to use!
Validation
> Component Validation - check> Cross-Layer Validierung - kind of check> Cross-Component Validation - what?
Validation
> Component Validation - check> Cross-Layer Validierung - kind of check> Cross-Component Validation - what?
ValidationCross-Component Validation
> Validierung über mehrere Komponenten
> username != password> password == password repeat> password Validation wie in Klasse XYZ
ValidationCross-Component Validation
> Alternative A: externe Validation Lib, z.B. Apache MyFaces ExtVal
> Alternative B: Self-Made Validator inkl. Zugriff auf Komponente(n)
> Alternative C: JSF System Events, d.h. pre/postValidate Callbacks
Stuff
Stuff<!-- classic “debugging“ via ui:remove --><html><h:head /><h:body><h:form> Text to display. <ui:remove>Text to remove</ui:remove> Text to display.</h:form></body></html>
Stuff<!-- classic “debugging“ via ui:debug --><html ... ><h:head> ... </h:head><body> ... <ui:debug hotkey="0" rendered= "#{initParam['javax.faces.PROJECT_STAGE'] eq 'Development'}" />
</body></html>
Stuff<!-- classic “debugging“ via ui:debug --><html ... ><h:head> ... </h:head><body> ... <ui:debug hotkey="0" rendered= "#{initParam['javax.faces.PROJECT_STAGE'] eq 'Development'}" />
</body></html>
Lars Röwekamp | CIO New Technologies | @mobileLarson
Hidden Features
Thankyou
Best
Pra
ctic
es