-
1. Re: how to conditionally render a row of dataTable
matt.drees Aug 19, 2007 2:13 PM (in response to ellenzhao)I imagine you could create an el function to do your filtering for you. Something like
<h:dataTable value="#{my:filter(fooManager.fooList)}" var="foo" > </h:dataTable>
-
2. Re: how to conditionally render a row of dataTable
ellenzhao Aug 19, 2007 2:42 PM (in response to ellenzhao)Yes that's a good idea! Could you please point me to documentation on how to create an EL function? In my view code (.xhtml files), there is heck lot of rendering rules (occurence of the word "rendered = " > 100), some are repeated again and again and again for different fields/buttons.
It would be really nice if I could say something like:rendering rules: for view-id = "foo.xhtml" rule: "rendering rule for the use case "create a foo" when fooManager.useCases.peek() == "createAFoo", fooManager.foo.crudStatus.toString == "new", // "new" means the id has not been fetched from the DB yet. identity.hasRole('admin'), ..... then render fields: setter for (fooManager.foo.name, fooManager.foo.description, fooManager.foo......) render action button: button1, button2.... not render: // some fields and/or buttons you do not want to render end rule: "rendering rule for the use case "update a foo" when .... then ... end
This way composition will be really easy....Even better, the view can be mapped/annotated in the entity class to reduce the view code, for example:@Column(name = "name"....) @View(outputText) public String getName(){...} @View(inputText) public void setName(String name){...} .....
And in the fooManager class:... @View(actionButton) public void tryUpdateAFoo(){ } ...
Regards,
Ellen -
3. Re: how to conditionally render a row of dataTable
matt.drees Aug 19, 2007 3:06 PM (in response to ellenzhao)Rick Hightower wrote a couple good articles on Facelets, and the second gives a good example on using functions. I found it helpful.
He goes for the programmatic approach of defining a facelets tag library; some people prefer defining their library in xml. I like the programmatic approach better; it seems create less duplication.
http://www.ibm.com/developerworks/java/library/j-facelets2.html
As far as creating some kind of annotation-driven framework for conditional rendering, I'm sure you could do it. Instead of using el functions, I'd guess you would be better off creating a component that marks its children rendered or not rendered, depending on their value-bindings. You could get some direction on how to do this by looking at Seam's components for model validation (ModelValidator and UIValidateAll, as well as Hibernate Validator code). But who knows; maybe that'd be harder to do. -
4. Re: how to conditionally render a row of dataTable
ellenzhao Aug 19, 2007 4:36 PM (in response to ellenzhao)I read that article. It seems there's a lot work to do in the infrastructure for adding a simple filtering feature....
Here are small view files in my own project demonstrating how I compose views. Hopefully they are readable enough....the foodCont is the name of a manager backing bean. This file is never directly accessed, but always included in other view files. All my directly-accessed views are started with a verb, shortly describing a use case.
Here the view-a-food.xhtml:<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- version: $Id: view-a-food.xhtml 211 2007-08-16 16:42:02Z ningning $ --> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.ajax4jsf.org/rich" xmlns:a="https://ajax4jsf.dev.java.net/ajax" template="layout/template.xhtml"> <ui:define name="body"> <h3>Food Item Detail</h3> <h:messages globalOnly="true" styleClass="message" /> <ui:include src="/includes/food.xhtml" /> <rich:spacer width="100%" height="7" /> <div class="section"><s:button value="Ok" action="#{foodCont.quitAFoodView()}" /></div> </ui:define> </ui:composition>
Here the ultimately reusable food.xhtml:<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- version: $Id: food.xhtml 210 2007-08-16 06:34:57Z ningning $ --> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.ajax4jsf.org/rich" xmlns:a="https://ajax4jsf.dev.java.net/ajax"> <div class="section"> <!-- #################### begin error messages ##################### --> <s:fragment rendered="#{not empty foodCont.getErrors()}"> <rich:dataList value="#{foodCont.getErrors()}" var="error"> <h:outputText value="#{error.getMessage()}" class="error" /> </rich:dataList> <rich:spacer width="100%" height="7" /> </s:fragment> <!-- ####################### end error messages ############## --> <!-- ################# permission guard ######################### --> <s:fragment rendered="#{foodCont.getNeedsLogin() and not identity.loggedIn}"> <div class="section"><h:form> <rich:panel> <f:facet name="header">Login</f:facet> <p>You need to login to perform this action</p> <div class="dialog"><h:panelGrid columns="2" rowClasses="prop" columnClasses="name,value"> <h:outputLabel for="username">Username</h:outputLabel> <h:inputText id="username" value="#{foodCont.actor.name}" /> <h:outputLabel for="password">Password</h:outputLabel> <h:inputSecret id="password" value="#{foodCont.actor.password}" /> <h:outputLabel for="rememberMe">Remember me</h:outputLabel> <h:selectBooleanCheckbox id="rememberMe" value="#{identity.rememberMe}" /> </h:panelGrid></div> </rich:panel> <div class="actionButtons"><h:commandButton value="Login" action="#{foodCont.login}" /></div> </h:form></div> <rich:spacer width="100%" height="7" /> </s:fragment> <!-- ################## end of permission guard ############### --> <!-- ############### begin atomic fields of a food ################# --> <s:fragment> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Food Description</ui:define> <h:outputText value="#{foodCont.food.longDesc}" /> </s:decorate> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Food Group: </ui:define> <h:outputText value="#{foodCont.food.fdGrp.desc_en}" /> </s:decorate> <s:fragment rendered="#{foodCont.food.comName != null}"> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Common Name:</ui:define> <h:outputText value="#{foodCont.food.comName}" /> </s:decorate> </s:fragment> <s:fragment rendered="#{foodCont.food.facName != null}"> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Manufacturer Name:</ui:define> <h:outputText value="#{foodCont.food.facName}" /> </s:decorate> </s:fragment> <s:fragment rendered="#{foodCont.food.sciName != null}"> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Scientific Name:</ui:define> <h:outputText value="#{foodCont.food.sciName}" /> </s:decorate> </s:fragment> <s:fragment rendered="#{foodCont.food.refuDesc != null}"> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Refuse Description:</ui:define> <h:outputText value="#{foodCont.food.refuDesc}" /> </s:decorate> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Refuse Percentage:</ui:define> <h:outputText value="#{foodCont.food.refuPercentage}%" /> </s:decorate> </s:fragment> <s:fragment rendered="#{not empty foodCont.food.userOpinions}"> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Average User Rate:</ui:define> <h:outputText value="#{foodCont.food.getRateMean()}"> <f:convertNumber type="number" maxFractionDigits="3" /> </h:outputText> <h:outputText value=" from #{foodCont.food.getTotalRates()} rates" /> </s:decorate> </s:fragment> <s:decorate template="/layout/display.xhtml"> <ui:define name="label">Current Price per 100g:</ui:define> <h:outputText value="#{foodCont.food.pricePer100g}"> <f:convertNumber pattern="? ###0.00" /> </h:outputText> </s:decorate> <rich:spacer width="100%" height="7" /> </s:fragment> <!-- ############### end of atomic fields of a food ############### --> <!-- ################# begin weight convertor #################### --> <s:fragment rendered="#{not empty foodCont.food.getWeights()}"> <div class="section"><h:form> <s:decorate template="/layout/edit.xhtml"> <ui:define name="label">Amount: </ui:define> <h:inputText value="#{foodCont.food.givenAmount}" required="true" /> </s:decorate> <h:selectOneMenu id="selectUnit" class="value" value="#{foodCont.food.givenWeight}"> <s:selectItems value="#{foodCont.food.weights}" var="unit" label="#{unit.msreDesc}" noSelectionLabel="Select a weight unit..." /> <s:convertEntity /> </h:selectOneMenu>   <h:commandButton type="submit" value="Convert to gram" action="#{foodCont.food.convertWeight()}" /> </h:form>    <h:outputText value="#{foodCont.food.getWeightInGram()}"> <f:convertNumber type="number" maxFractionDigits="3" /> </h:outputText> gram</div> <rich:spacer width="100%" height="7" /> </s:fragment> <!-- ################# end weight convertor #################### --> <!-- ############### begin control options ################# --> <div class="section"><rich:toolBar height="26" itemSeparator="disc"> <rich:toolBarGroup rendered="#{(identity.hasRole('admin') and (foodCont.getUseCases().peek() ne 'updateAFood')) or (not identity.loggedIn)}"> <s:link action="#{foodCont.tryUpdateAFood()}" value="Update Price" /> </rich:toolBarGroup> <rich:toolBarGroup> <s:link action="#{userOpinionCont.startAddOpinion(foodCont.food)}" value="Rate and/or Comment it" rendered="#{userOpinionCont.getUseCases().peek() ne 'addOrUpdateUserOpinion'}" /> </rich:toolBarGroup> <rich:toolBarGroup> <s:link action="#{foodCont.food.setRenderNutrients(true)}" value="Show Nutrient Facts" rendered="#{not foodCont.food.getRenderNutrients()}" /> <s:link action="#{foodCont.food.setRenderNutrients(false)}" value="Fold Nutrient Facts" rendered="#{foodCont.food.getRenderNutrients()}" /> <s:link action="#{foodCont.food.setRenderComments(true)}" value="Show User Comments" rendered="#{foodCont.food.getComments().size > 0 and not foodCont.food.getRenderComments()}" /> <s:link action="#{foodCont.food.setRenderComments(false)}" value="Fold User Comments" rendered="#{foodCont.food.getComments().size > 0 and foodCont.food.getRenderComments()}" /> <s:link action="#{foodCont.food.setRenderRecipes(true)}" value="Show Containing Recipes" rendered="#{foodCont.food.getIngredients().size > 0 and not foodCont.food.getRenderRecipes()}" /> <s:link action="#{foodCont.food.setRenderRecipes(false)}" value="Fold Containing Recipes" rendered="#{foodCont.food.getIngredients().size > 0 and foodCont.food.getRenderRecipes()}" /> </rich:toolBarGroup> </rich:toolBar> <rich:spacer width="100%" height="7" /> </div> <!-- ################ end of control options ################## --> <!-- ############### begin of price update form ################## --> <s:fragment rendered="#{foodCont.getUseCases().peek() eq 'updateAFood'}"> <div class="section"><h:form> <s:decorate template="/layout/edit.xhtml"> <ui:define name="label">Current Price per 100g:</ui:define> <h:inputText value="#{foodCont.food.pricePer100g}" required="true" /> <h:outputText value=" euro" /> </s:decorate> <div class="section"><h:commandButton value="update" action="#{foodCont.doneUpdateAFood()}" /><s:button value="Cancel" action="#{foodCont.cancelUpdatePrice()}" /></div> </h:form></div> <div class="section"><rich:spacer width="100%" height="7" /></div> </s:fragment> <!-- ############### end of price update form ################## --> <!-- ############### begin of user opinion form ############### --> <s:fragment rendered="#{userOpinionCont.getUseCases().peek() eq 'addOrUpdateUserOpinion'}"> <div class="section"><ui:include src="user-opinion.xhtml" /> <rich:spacer width="100%" height="7" /></div> </s:fragment> <!-- ################ end of user opinion form ################## --> <!-- ############## begin of nutrient facts ################### --> <s:fragment id="nutrients" rendered="#{foodCont.food.getRenderNutrients()}"> <div class="section"> <h4>Nutrient Facts</h4> <h:form> <rich:dataTable id="nutrientFacts" rows="25" value="#{foodCont.food.getNutrients()}" var="n"> <rich:column> <f:facet name="header"> <h:outputText value="Nutrient Desc" /> </f:facet> <h:outputText value="#{n.nutr.nutrDesc}" /> </rich:column> <rich:column> <f:facet name="header"> <h:outputText value="Amount per 100g" /> </f:facet> <h:outputText value="#{n.nutrVal} #{n.nutr.units}"> <f:convertNumber type="number" maxFractionDigits="3" /> </h:outputText> </rich:column> <rich:column rendered="#{foodCont.food.getWeightInGram()>0}"> <f:facet name="header"> <h:outputText value="Amount in #{foodCont.food.getGivenAmount()} #{foodCont.food.getGivenWeight().msreDesc}" /> </f:facet> <h:outputText value="#{foodCont.food.getWeightInGram() / 100 * n.nutrVal}"> <f:convertNumber type="number" maxFractionDigits="3" /> </h:outputText> #{n.nutr.units} </rich:column> </rich:dataTable> <rich:datascroller for="nutrientFacts" /> </h:form></div> <rich:spacer width="100%" height="7" /> </s:fragment> <!-- ################# end of nutrient facts #################### --> <!-- ################# begin of user comments table ############# --> <s:fragment id="userCommments" rendered="#{foodCont.food.getComments().size > 0 and foodCont.food.getRenderComments()}"> <div class="section"><h:form> <rich:dataTable id="commentsTable" rows="25" value="#{foodCont.food.getComments()}" var="o"> <rich:column> <f:facet name="header"> <h:outputText value="User" /> </f:facet> <h:outputText value="#{o.user.name}" /> </rich:column> <rich:column> <f:facet name="header"> <h:outputText value="Rate" /> </f:facet> <h:outputText value="#{o.rate}" /> </rich:column> <rich:column> <f:facet name="header"> <h:outputText value="Comment" /> </f:facet> <h:outputText value="#{o.comment}" /> </rich:column> <rich:column> <f:facet name="header"> <h:outputText value="Date" /> </f:facet> <h:outputText value="#{o.version}" /> </rich:column> </rich:dataTable> <rich:datascroller for="commentsTable" /> </h:form></div> <rich:spacer width="100%" height="7" /> </s:fragment> <!-- ################# end of user comments table ############# --> <!-- ################# begin of containing recipes table ########### --> <s:fragment rendered="#{foodCont.food.getIngredients().size > 0 and foodCont.food.getRenderRecipes()}"> <div class="section"><h:form> <rich:dataTable id="recipesTable" rows="25" value="#{foodCont.food.getIngredients()}" var="i"> <rich:column> <f:facet name="header"> <h:outputText value="Recipe Id" /> </f:facet> <h:outputText value="#{i.recipe.id}" /> </rich:column> <rich:column> <f:facet name="header"> <h:outputText value="Recipe Name" /> </f:facet> <h:outputText value="#{i.recipe.name}" /> </rich:column> <rich:column> <f:facet name="header"> <h:outputText value="Weight Percentage" /> </f:facet> <h:outputText value="#{i.getWeightPercentage()}"> <f:convertNumber pattern="###0.00%" /> </h:outputText> </rich:column> <rich:column> <f:facet name="header"> <h:outputText value="Option" /> </f:facet> <h:commandButton action="#{foodCont.selectContainingRecipe(i)}" value="view the recipe" /> </rich:column> </rich:dataTable> <rich:datascroller for="recipesTable" /> </h:form></div> <rich:spacer width="100%" height="7" /> </s:fragment> <!-- ################# end of containing recipes table ########### --></div> </ui:composition>
yes my entity object is very rich of behaviours and transient member fields...
Here a bit navigation rules....<page view-id="/view-a-food.xhtml"> <navigation from-action="#{foodCont.quitAFoodView()}"> <rule if="#{foodCont.getUseCases().peek() eq 'readFoods'}"> <redirect view-id="/view-foods.xhtml" /> </rule> </navigation> <navigation from-action="#{recipeManager.setRecipe(r)}"> <redirect view-id="/view-a-recipe.xhtml" /> </navigation> <navigation>....</navigation> </page>
the code in the conversation beans is very lean, just does stuffs like begin/end conversations, push/pop/remove use cases, etc. -
5. Re: how to conditionally render a row of dataTable
matt.drees Aug 20, 2007 11:47 AM (in response to ellenzhao)Yeah, there is some infrastructure for setting up a tag library. But I think it's totally worth it.