Sunday, January 22, 2012

JSF Continued

We have seen some very basic JSF components and the backing beans that will interact with them.  Now we need to implement the application.  And explore JSF more fully.

Here is the main page for the application.  It has many features that we will go through in the next few posts.  We will itereate over the file a few times as we discuss more and more complicated concepts.  I will skip parts to leave the discussion of them for later.

/JSFDemo.war/startPage.xhtml

<?xml version='1.0' encoding='UTF-8' ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:p="http://primefaces.prime.com.tr/ui"
      xmlns:f="http://java.sun.com/jsf/core"      
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      >
    <h:head>
        <script type="text/javascript" src="jsfunctions.js"></script>  
        <script>
            function showMessage(message) {
               alert(message);
               return true;
            }
        </script>
        <title>Tutorial Application - Main Page</title>
        <h:outputStylesheet library="css" name="basecss.css"  />
    </h:head>
    <h:body>
        <h:form id="menuForm">
            <p:toolbar>
                <p:toolbarGroup align="left">
                    <p:menuButton value="File">
                        <p:menuitem value="New Order" onclick="newOrderDlg.show();"/>  
                        <p:menuitem value="Open/Delete Order" onclick="orderSelectorDlg.show();"></p:menuitem>
                        <p:menuitem value="Save Order" actionListener="#{mainSampleViewBean.save}" update="msgPanel"></p:menuitem>
                        <p:menuitem value="Add Item" onclick="partSelectorDlg.show();"/>
                        <p:menuitem value="Delete Item" actionListener="#{mainSampleViewBean.orderPartsBean.removeOrderParts}" update="treePanel partSelectorForm"/>
                        <p:menuitem value="Checkout" action="#{mainSampleViewBean.checkout}" ajax="false"/>
                    </p:menuButton>
                </p:toolbarGroup>
                <p:toolbarGroup id="pagebar" align="left">
                    <p:commandButton type="button" value="Open" title="Open an order" onclick="orderSelectorDlg.show();" />
                    <p:commandButton type="push" image="ui-icon-disk" value="Save" title="Save The Order For Later" actionListener="#{mainSampleViewBean.save}" update="msgPanel"/>
                    <p:commandButton type="button" value="Add Item" onclick="partSelectorDlg.show();"/>
                    <p:commandButton image="ui-icon-trash" value="Delete Item" title="Delete Item" actionListener="#{mainSampleViewBean.orderPartsBean.removeOrderParts}" update="treePanel partSelectorForm"/>
                    <p:commandButton type="push" value="Check Out" title="Submit Order" action="#{mainSampleViewBean.checkout}" update="msg" ajax="false"/>
                    <p:commandButton type="push" value="Ajax/Validation" onclick="demoDlg.show();"/>
                    <p:commandButton type="button" value="REST" onclick="restDlg.show();"/>
                    <p:commandButton type="button" value="SOAP" onclick="soapDlg.show();"/>
                </p:toolbarGroup>
                <p:toolbarGroup align="right">         
                    <p:commandButton type="button" value="No bean redirect" onclick="window.location.href='index.xhtml'; return false;"/> 
                    <h:commandButton type="button" title="Validate User Name" value="Login" onclick="loginDlg.show();"/>
                    <h:commandButton type="push" title="End Session" value="Logout" action="#{mainSampleViewBean.logout}" /> 
                </p:toolbarGroup>
            </p:toolbar>
        </h:form>
        <h:form id="sampleForm" prependId="false">
            <h:panelGrid columns="1" cellpadding="0" columnClasses="alignTop, alignTop">
                <h:panelGroup>
                    <p:panel id="userPanel">
                        <h:outputText value="User Name: #{mainSampleViewBean.userLogin.userName}" />
                    </p:panel>
                    <p:panel id="msgPanel">
                        <h:outputText value="Purchase Order: #{mainSampleViewBean.orderEntity.id}, #{mainSampleViewBean.orderEntity.name} : #{mainSampleViewBean.orderEntity.counter}  - #{mainSampleViewBean.msg}"/>
                    </p:panel>
                    <p:panel>
                        <p:tree id="treePanel" value="#{mainSampleViewBean.treeBean.orderTree}" var="node" 
                                dynamic="true" selection="#{mainSampleViewBean.treeBean.selectedNode}" selectionMode="single"
                                update="partSelectorForm msgPanel"  
                                nodeSelectListener="#{mainSampleViewBean.treeBean.onNodeSelect}">                            
                            <p:treeNode>
                                <h:outputText value="#{node}"/>
                            </p:treeNode>
                        </p:tree>
                    </p:panel>
                </h:panelGroup>
            </h:panelGrid>
        </h:form>
        <ui:include src="demoDialog.xhtml" />
        <ui:include src="orderSelectorDialog.xhtml" />
        <ui:include src="webServicesDialogs.xhtml" />
        <p:dialog widgetVar="partSelectorDlg">
            <h:form id="partSelectorForm" prependId="false">
                <p:dataTable id="partSelectorTable" var="partName" value="#{mainSampleViewBean.orderPartsBean.availableParts}" 
                             paginator="false" rows="10"  
                             selection="#{mainSampleViewBean.orderPartsBean.selectedPart}" selectionMode="single"  
                             > 
                    <p:column>
                        <f:facet name="header">
                            <h:outputText value="Name" />
                        </f:facet>
                        <h:outputText value="#{partName}" />
                    </p:column>
                    <f:facet name="footer">  
                        <p:commandButton value="Add" image="ui-icon ui-icon-search" actionListener="#{mainSampleViewBean.orderPartsBean.addOrderParts}" 
                                         update="treePanel partSelectorForm" />  
                    </f:facet>
                    <f:facet name="footer">  
                        <p:commandButton value="Close" oncomplete="partSelectorDlg.hide();"/>  
                    </f:facet>
                </p:dataTable>
            </h:form>
        </p:dialog>
        <p:dialog widgetVar="newOrderDlg" width="400" hideEffect="explode">
            <h:form id="newOrderForm" prependId="false">
                <h:panelGrid id="newOrderPanelGrid" columns="3" cellpadding="5" >
                    <h:outputLabel for="newname" value="Name: " />
                    <h:inputText value="#{orderBean.newOrder.name}" id="newname" validator="#{orderBean.validate}"/>
                    <h:message id="nameValid" for="newname" />
                    <h:outputLabel for="newcounter" value="Initial Counter Value: " />
                    <h:inputText value="#{orderBean.newOrder.counter}" id="newcounter" required="true" validatorMessage="#{msgs.counterMessage}">
                        <f:validateLongRange minimum="1" maximum="1000"/>
                    </h:inputText>
                    <h:message id="counterValid" for="newcounter" />
                    <f:facet name="footer">
                        <p:commandButton actionListener="#{orderBean.createOrder}" update="newOrderForm partSelectorForm sampleForm orderSelectorTable" value="Create" 
                                         oncomplete="newOrderComplete(xhr, status, args)"/> 
                        <p:commandButton type="button" value="Cancel" onclick="newOrderDlg.hide();" />  
                    </f:facet>
                </h:panelGrid>
            </h:form>
        </p:dialog>
        <p:dialog id="loginUser" header="User Login" widgetVar="loginDlg">
            <h:form id="loginForm" prependId="false">
                <h:panelGrid id="loginPanelGrid" columns="2" cellpadding="5" >
                    <h:inputText label="User Name: " value="#{mainSampleViewBean.userLogin.name}" id="loginName" />
                    <h:inputSecret label="Password: " value="#{mainSampleViewBean.userLogin.password}" id="loginPassword" />
                    <f:facet name="footer">
                        <p:commandButton actionListener="#{mainSampleViewBean.userLogin.authorize}" oncomplete="loginDlg.hide()" value="Login" update="userPanel"/> 
                    </f:facet>
                </h:panelGrid>
            </h:form>
        </p:dialog>
    </h:body>
</html>

We start the file just as we did with the basic example with the declaration of the doc type and xml name spaces.  Then we give it a h:head.  Notice we included primefaces as a namespace.

Next I demonstrate the inclusion of javascript.  There are 3 ways to include Javascript.  <h:outputScript library="js" name="filename.js" /> which, by the way, is the preferred method.  Or, the two I demonstrated.  One is the <script> html tag, and the other the inclusion of a js script file with the <script> tag.  Now, the h:outputScript is going to behave similarly to the h:outputStyleSheet or the h:graphicImage tag in that it is going to resolve the path to the file as [root]/faces/javax.faces.resources/[library]/[name].  In our tutorial this is the equivalent of /JSFDemoApp/web/resources/css/basecss.css for the h:outputStyleSheet used a few lines later.

Now we declare the body, and the first form:
    <h:body>
        <h:form id="sampleForm" prependId="false">

Here we use the prependId="false".  If we did not do that, the id's for the components would have the form id prepended to it, such that a component named foo would be referenced by sampleForm:foo.  Now, you don't always need or want prependId="false", but this technique can simplify templates, and make finding components a bit easier.

Skipping down to the h:commandButton we have:
<h:commandButton type="button" title="Validate User Name" value="Login" onclick="loginDlg.show();"/>
 <h:commandButton type="push" title="End Session" value="Logout" action="#{mainSampleViewBean.logout}" /> 

Everything we have on our jsf page will ultimately render to html.  So here we have our basic jsf buttons which will render to <input type="button"> and <input type="submit"> respectfully.  The first button has a bubble title of "Validate User Name" that will display as you roll over it.  The label of the button is "Login" and if the button is pressed, the component "loginDlg" will be displayed, since it is hidden by default.  The second button's label it "Logout".  When the second button is pressed, it will be redirected to whatever page is returned by the mainSampleViewBean.logout method.  Remember the signature of the logout method is:  public String logout();  It also has a bubble title of "End Session".  When we look at the mainSampleViewBean is SessionScoped.  It will be created the first time we view the page, but its state will be kept for the duration of the http session.  This second button will actually end the http session and cause a new session to be instantiated.  But, all that is done in the bean method "logout".

Continuing on down, we have our basic output text field in:
<h:outputText value="User Name: #{mainSampleViewBean.userLogin.userName}" />
<h:outputText value="Purchase Order: #{mainSampleViewBean.orderEntity.id}, #{mainSampleViewBean.orderEntity.name} : #{mainSampleViewBean.orderEntity.counter}  - #{mainSampleViewBean.msg}"/>


These components will render the bean field values by calling their getters.  Notice, we can use multiple fields and / or methods.  We could style these with the style attribute or styleClass.

As we move down, we come to the end of the form.  But, this form is far from the end of the file.  There are several forms on this one page.  When we create a page that is complicated enough, or renders enough information that a specific buffer is overrun, we will encounter a Mojarra bug 2215 which is why we have the context-param com.sun.faces.writeStateAtFormEnd value false in our web.xml.

Traveling down a little further, we encounter a few <ui:include>.  These allow us to insert external xhtml files into our file, thereby giving us the same capability we have everywhere else when writting code.  We can compartmentalize our code in individual files and consolidate them as needed.  There isn't any inheritence or such, so these aren't class files, but it still allows us to organize and possibly reuse our code.  There is an even greater ability to reuse code with the templates, but in this tutorial, we have no example of using them.  I leave that to you to review.

So, we have seen the JSF file, and discussed the basic components.  But what is actually going on when we visit a JSF page?  For that, we must discuss the JSF lifecycle.

There are 6 phases of the JSF lifecycle:  Restore view -> Apply request values -> Process validations -> Update model values-> Invoke application -> Render response

But, we have events that can be handled during the lifecycle, such that an expanded view of the lifecycle is:

Restore view -> Apply request values, process events -> Process validations, process events -> Update model values, process events-> Invoke application, process events -> Render response

But, we can skip some phases when necessary.  And, we can add ajax so that we go through the phases, but not on the entire form / page.  There are plenty of really good descriptions of the lifecycle, that I'm not going to repeat it here, but the next post will look at the practical application of the lifecycle and validation.

No comments:

Post a Comment