I have some objects in a list witch are components and I implemented this to show them :
first.xhtml:
<h:form id="sceneForm">
<c:forEach items="#{templateController.model.selectedTemplate.templatesSet.toArray()}" var="templateControls" varStatus="ind">
<ui:include src="second.xhtml"/>
</c:forEach>
</h:form>
second.xhtml:
<c:forEach items="#{templateControls.controlAttributes.toArray()}" var="controlAttributes" >
<c:choose>
<c:when test="#{controlAttributes.controlAttributeType.name == 'top'}">
<ui:param name="top" value="#{controlAttributes.value}px" />
</c:when>
<c:when test="#{controlAttributes.controlAttributeType.name == 'left'}">
<ui:param name="left" value="#{controlAttributes.value}px" />
</c:when>
<c:when test="#{controlAttributes.controlAttributeType.name == 'width'}">
<ui:param name="width" value="#{controlAttributes.value}px" />
</c:when>
<c:when test="#{controlAttributes.controlAttributeType.name == 'height'}">
<ui:param name="height" value="#{controlAttributes.value}" />
</c:when>
</c:choose>
</c:forEach>
<c:if test="#{templateControls.controlType.type == 'Button'}">
<div style="top:#{top}; left:#{left}; width:#{width}; height:#{height}px;>
<div class="gui-component-inner">
<span>#{value}</span>
<em class="helper"></em>
</div>
</div>
</c:if>
I iterate throught my attributes list and I set some variables and after it I set my component with variables. This works fine but it is not optim. I make a button:
<p:commandButton value="Test" actionListener="#{templateController.testAction}" onstart="Utils.PrintTime()" oncomplete="Utils.PrintTime()"/>
Witch prints the time on request start and on request end, and it takes 2 second if, if I delete the whole <c:forEach> then it takes 0,05 seconds. How could I make a better request time with iteration? Any idea?
Why I get that minus I don`t know...
Also here is my solution:
I make an object with top,left,width,height and declared it in the TemplateControl object. On page load I iterate the templateControls.controlAttributes and I set top,left,width,height for each component. And in xhtml I simply write:
<c:if test="#{templateControls.controlType.type == 'Button'}">
<div style="top:#{templateControls.atr.top}; left:#{templateControls.atr.left}; width:#{templateControls.atr.width}; height:#{templateControls.atr.height}px;>
<div class="gui-component-inner">
<span>#{value}</span>
<em class="helper"></em>
</div>
</div>
</c:if>
Like this I make my iteration in java at the beginning and not in xhtml and I freed from the <c:forEach> and <c:if> tags and now my requests are very fast.
Related
This is the main view which contains the draggable items. In this view I have a ui:include for including a UI fragment that also has the drop area.
mainView.xhtml
<p:dataList id="outerList" var="varOuter" value="#{myVO.getOuterList()}">
<div>
<span>#{varOuter.someVal}</span>
</div>
<p:dataList id="innerList" var="innerVar" value="#{myVO.getInnerList()}">
<div class="crew-list">
<h:form>
<h:panelGroup id="draggableItem" layout="block">
<span>#{innerVar.someVal}</span>
</h:panelGroup>
<p:draggable for="draggableItem" opacity="0.5" scope="ab"
revert="true" helper="clone" appendTo="#(body)" zindex="4" >
</p:draggable>
</h:form>
</div>
</p:dataList>
</p:dataList>
...
<ui:include src="fragment.xhtml">
<ui:param name="param" value="#{paramVal}" />
</ui:include>
fragment.xhtml
<p:dataList id="list" var="pos" value="#{myVO.getPositions()}" rowIndexVar="rowIndex">
<ui:fragment rendered="#{!pos.isFilled()}">
<h:panelGroup layout="block" id="dropArea">
<span>Drop Here</span>
</h:panelGroup>
<p:droppable for="dropArea" hoverStyleClass="active"
scope="ab" datasource=":outerList:innerList">
<p:ajax listener="#{crewChangeVO.onDrop}" update="list" />
<f:attribute name="index" value="#{rowIndex}" />
</p:droppable>
</ui:fragment>
</p:dataList>
Now the problem that I run into is that, I am unable to map the datasource on the droparea correctly. Without datasource the drag-drop works but I don't get the data (dragged object) in my bean method and I need a way to know which object was dragged.
Would appriciate any input on how to achive this functionality.
Update
Using a single(non-nested) dataList as the source with a form outside it works. But the problem is that it doesn't work for a nested list. So, for now I am proceeding with design changes and removing the nested list.
I'm rendering a tree of nodes recursively using the following two JSF composite components I created:
tree.xhtml:
...
<cc:implementation>
<h:panelGroup id="content" layout="block">
<ul>
<c:forEach items="#{cc.attrs.rootNode.children}" var="node">
<ui:include src="treeNode.xhtml">
<ui:param name="level" value="1" />
</ui:include>
</c:forEach>
</ul>
</h:panelGroup>
</cc:implementation>
treeNode.xhtml:
...
<cc:implementation>
<li>
#{node.label}
<h:selectBooleanCheckbox value="#{node.checked}" />
<c:if test="#{not empty node.children}">
<ul class="node-level-#{level}">
<c:forEach items="#{node.children}" var="node">
<ui:include src="treeNode.xhtml">
<ui:param name="level" value="#{level + 1}" />
</ui:include>
</c:forEach>
</ul>
</c:if>
</li>
</cc:implementation>
Every Node has the properties label, checked, and children (the latter being a list of Nodes).
I'm getting it all nicely rendered, but when I try to change values, by checking the checkboxes and submitting the form, it doesn't get stored. Next time I refresh the page I still get the initial state (all checkboxes unchecked).
What am I doing wrong?
I've comments that are displayed from an <ui:repeat> tag. An user can reply to to all comments with input fields that are displayed individually for each comment. However I can't manage to pass the value of the comment to the bean that deals with it. I thought about passing the UIInput value as described in the first answer [by balusC here][1]. The problem is that the content is empty because the action button is bound to every UIInput ( I believe so). I should sort of have a personal bound between each button and my UIInput. I think the code is gonna be more self-explanatory hopefully. :
<ui:repeat var="post" value="#{watchThread.listeFirstPosts}">
<h:outputText value="#{post.user.username}"/>
<div class="postContent">
<h:outputText value="#{watchThread.sanitize(post.content)}" />
</div>
<div class="replyAction">
<h:form>
<div class="inputAreaPostFast">
<p:inputTextarea id="reply2" value="#{watchThread.replyContent}" binding="#{inputReply}"/>
<div class="replyBtn">
---------- Here the third param is the issue --------------
<p:commandButton action="#{watchThread.sendReply(userNav.user, post, inputReply.value)}"/>
</div>
</div>
</h:form>
</div>
</ui:repeat>
Edit: This is the code that worked for me. (I think) I had a form that had no business of being in my ui:repeat tag. Also to bind the input see the answer/comment of balusC below. However I had to let the form tags inside and not outside as suggested.
<ui:repeat var="post" value="#{watchThread.listeFirstPosts}">
<div class="replyBlock">
<h:panelGroup id="username" style="cursor:pointer;">
<div class="profileimgForum">
<h:graphicImage styleClass="profilepicForum" value="/images/#{post.user.profilePic}" rendered="#{!empty post.user.profilePic}"></h:graphicImage>
<h:graphicImage styleClass="profilepicForum" library="images" name="profilepic1.jpg" rendered="#{empty post.user.profilePic}"></h:graphicImage>
</div>
<span class="posterTitle">
<h:outputText value="#{post.user.username}"></h:outputText> </span>
<h:outputText value=" #{watchThread.formatTimeReply(post.datePost)}"/>
</h:panelGroup>
<div class="replyContent">
<h:outputText value="#{watchThread.sanitize(post.content)}" style="pointer:cursor;"/>
</div>
<div class="replyAction">
<p:inplace id="facet" effect="none">
<f:facet name="output">
#{strings.Reply}
</f:facet>
<f:facet name="input">
<h:form>
<div class="inputAreaPostFast">
<p:inputTextarea id="reply2" cols="70" rows="7" value="#{watchThread.replyContent}" binding="#{inputReply}" maxlength="500"/>
<div class="replyBtn">
<p:commandButton update="growl" style=" margin-right: 2em;" value="#{strings.Reply}" action="#{watchThread.sendReply(userNav.user, post, inputReply)}"/>
</div>
</div>
</h:form>
</f:facet>
</p:inplace>
</div>
<ui:repeat var="areply" value="#{post.posts}">
#{areply.content} ----
</ui:repeat>
</div>
<!-- ####### Dialog username ######## -->
<p:overlayPanel for="username" id="usernamePanel" dynamic="true" showCloseIcon="true" appendToBody="1" dismissable="false">
<div class="dlgTopBlock">
<div class="dlgPicBlock">
<h:graphicImage styleClass="profilePicDialog" value="/images/#{post.user.profilePic}" rendered="#{!empty post.user.profilePic}"/>
<h:graphicImage styleClass="profilePicDialog" library="images" name="profilepic1.jpg" rendered="#{empty post.user.profilePic}"/>
</div>
<div class="dlgTopSubBlock"><div class="dlgTopTitle">
<h:graphicImage styleClass="flag" library="images/flags"
name="#{post.user.countryBean.iso2}.png"
rendered="#{! empty post.user.countryBean.iso2}"/>
<h:link value="#{post.user.username}" outcome="/user.xhtml">
<f:param name="username" value="#{post.user.username}"></f:param>
</h:link>
</div>
<div class="dlgRep">
<h:outputText value="#{post.user.reputation} #{strings.repPoints} "></h:outputText>
</div>
<div class="dlgTopStatus">
<h:outputText value="#{post.user.status}"/></div>
</div>
</div>
<div class="btnDlg">
<h:panelGroup rendered="#{userNav.isUserConnected and userNav.username != post.user.username}">
<p:commandButton value="#{usernavmsg.SendPM}" icon="ui-icon-mail-closed" onclick="PF('sendMsgDlg').show();"></p:commandButton>
<p:commandButton id="addFriend" value="#{friendBean.btnText}" action="#{friendBean.addFriend()}" update="growl addFriend" icon="#{friendBean.icon}"
rendered="#{friendBean.rendered}"/>
</h:panelGroup>
</div>
<p:dialog header="#{usernavmsg.SendingTo}: #{post.user.username}"
dynamic="true" modal="false" widgetVar="sendMsgDlg" minHeight="40">
<h:form>
<p:inputTextarea value="#{pMbean.title}" rows="1" cols="110" id="title" >
<f:passThroughAttribute name="placeholder" value="#{usernavmsg.Title}"/>
</p:inputTextarea><br/><br/>
<p:inputTextarea value="#{pMbean.message}" id="msg" rows="10" cols="110" autoResize="true">
<f:passThroughAttribute name="placeholder" value="#{usernavmsg.YourMsg}"/>
</p:inputTextarea><br/>
<p:commandButton style="float:right;" value="#{usernavmsg.Send}" action="#{pMbean.send(post.user)}"
onclick="PF('sendMsgDlg').hide();" icon="ui-icon-mail-closed" update="growl"></p:commandButton>
</h:form>
</p:dialog>
</p:overlayPanel>
<!-- ######### End of dialog user ######## -->
</ui:repeat>
The only visible mistake is the below:
<ui:repeat value="#{watchThread.listeFirstPosts}" var="post" ...>
<p:inputTextarea ... value="#{watchThread.replyContent}">
You're basically binding the value of the input field of every iteration to one and same backing bean property. So, every iteration round will override the value of the previous iteration round. This is not right. The value of the input field must be bound to the current iteration round. I.e. it should basically have been as below:
<ui:repeat value="#{watchThread.listeFirstPosts}" var="post" ...>
<p:inputTextarea ... value="#{post.replyContent}">
But, in this construct you actually don't need it. Just get rid of the value attribute altogether and rely on binding as you initially wanted to use.
Then, there's another mistake as pointed out in the comments: nesting forms. This is illegal in HTML. Don't write JSF code in such way that it produces illegal HTML. There would be another potential cause in form of a bug in Mojarra which only exposes in case you're using <f:ajax> in a nested <ui:repeat>. The solution to that would be using a fullworthy UIData component, such as <h:dataTable> or one of PrimeFaces grids.
In my code the CommandButton action does not work when I put it in the second or third PanelGroup. It only works when I put in the first PanelGroup. My code is shown below. What may be the problem?
<ui:define name="pageLocations">
<h:panelGroup layout="block" id="pageLocations">
<c:if test="#{param.typeTab == null && param.relationTab == null && param.propertyTab == null}">
<ui:param name="defaultHeaderTitle" value="Properties"/>
<!--<h:outputText value="Properties" />-->
</c:if>
<p class="breadcrumb">Admin <ezcomp:out value="raquo" />
Fields <ezcomp:out value="raquo" />
<strong>
#{param.pageHeaderTitle}#{defaultHeaderTitle}
</strong>
</p>
</h:panelGroup>
</ui:define>
<!--<ui:define></ui:define>-->
<ui:define name="pageHeaderTitle">
<h:outputText value="#{param.pageHeaderTitle}#{defaultHeaderTitle}" id="soso" />
</ui:define>
<ui:define name="sidemenu">
<ui:param name="fieldsPage" value="active"/>
</ui:define>
<ui:define name="admin-content">
<h:form prependId="false">
<h:panelGroup layout="block" class="well well-small" id="mainPanel">
<h:panelGroup layout="block" class="row-fluid">
<div class="span1"><ezcomp:out value="nbsp"/></div>
<div class="span4">
<ul class="nav nav-pills" style="margin-top:4px;margin-bottom:0">
<c:if test="#{param.typeTab == null && param.relationTab == null && param.propertyTab == null }">
<ui:param name="default" value="active"/>
<ui:param name="defaultHeaderTitle" value="Properties"/>
</c:if>
<li class="#{param.propertyTab} #{default}">
<p:commandLink value="Properties" immediate="true" update="mainPanel,fields,:soso,:pageLocations" >
<f:param name="propertyTab" value="active"/>
<f:param name="pageHeaderTitle" value="Properties"/>
</p:commandLink>
</li>
<li class="#{param.typeTab}">
<p:commandLink value="Types" immediate="true" async="true" update="mainPanel,fields,:soso,:pageLocations" >
<f:param name="typeTab" value="active"/>
<f:param name="pageHeaderTitle" value="Types"/>
</p:commandLink>
</li>
<li class="#{param.relationTab}">
<p:commandLink value="Fields Relations" immediate="true" update="mainPanel,fields,:soso,:pageLocations">
<f:param name="relationTab" value="active"/>
<f:param name="pageHeaderTitle" value="Fields Relations"/>
</p:commandLink>
</li>
</ul>
</div>
</h:panelGroup>
</h:panelGroup>
<h:panelGrid id="fields" width="100%">
<h:panelGroup layout="block" rendered="#{param.propertyTab == 'active' || default == 'active' }">
<ui:include src="property/properties.xhtml"></ui:include>
</h:panelGroup>
<h:panelGroup layout="block" rendered="#{param.typeTab == 'active'}">
<ui:include src="type/types.xhtml"></ui:include>
</h:panelGroup>
<h:panelGroup layout="block" rendered="#{param.relationTab == 'active'}">
<ui:include src="fields-relations.xhtml"></ui:include>
<h:commandButton value="Create" actionListener="#{fieldsRelationsController.createRelations()}" >
<f:ajax listener="#{fieldsRelationsController.createRelations()}"/>
</h:commandButton>
</h:panelGroup>
</h:panelGrid>
</h:form>
</ui:define>
It's because those panel groups are conditionally rendered based on a request parameter which is not present anymore in the subsequent POST request initiated by the command button.
When JSF processes the POST request, the rendered condition is also re-evaluated. Due to lack of the request parameter, it evaluates false, so the command button component won't be processed, so the associated action won't be invoked.
To fix this, you need to pass the request parameter responsible for calculating the result of the rendered attribute in a <f:param> of the command button.
<h:commandButton value="Create" action="#{fieldsRelationsController.createRelations}">
<f:ajax />
<f:param name="relationTab" value="active"/>
</h:commandButton>
(note that I've replaced the actionListener by action and removed the double f:ajax listener as well, I don't know what you were thinking, but I'll assume it to be a leftover of unthoughtful shoots in the dark)
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 5.
i want to use the JSTL function contains in JSF2
here's what i tried but the condition always evaluates to false, i think i am missing something:
<c:forEach items="#{myBean.toSendCheckBoxes}" var="entry" varStatus="loop">
<input type="checkbox" name="myArray" value="#{entry.value}" />
<c:choose>
<c:when test="#{fn:contains(entry.key,'g')}">
<ice:graphicImage url="/resources/images/image1.bmp" />
<b>#{entry.key}</b>
</c:when>
<c:otherwise>
<ice:graphicImage url="/resources/images/image2.bmp" />
<span>#{entry.key}</span>
</c:otherwise>
</c:choose>
</c:forEach>
if there's another JSF way to accomplish that, would be very nice to mention it, thanks in advance.
More JSFish way to do it would be
<ui:repeat value="#{myBean.toSendCheckBoxes}" var="entry" varStatus="loop">
<input type="checkbox" name="myArray" value="#{entry.value}" />
<ui:fragment rendered="#{fn:contains(entry.key,'g')}">
<ice:graphicImage url="/resources/images/image1.bmp" />
<b>#{entry.key}</b>
</ui:fragment>
<ui:fragment rendered="#{!fn:contains(entry.key,'g')}">
<ice:graphicImage url="/resources/images/image2.bmp" />
<span>#{entry.key}</span>
</ui:fragment>
</ui:repeat>
or even
<ui:repeat value="#{myBean.toSendCheckBoxes}" var="entry" varStatus="loop">
<input type="checkbox" name="myArray" value="#{entry.value}" />
<ice:graphicImage url="#{fn:contains(entry.key,'g') ? '/resources/images/image1.bmp' : '/resources/images/image2.bmp'}" />
<span class="#{fn:contains(entry.key,'g') ? 'bold-style' : ''}">#{entry.key}</span>
</ui:repeat>
but I would expect your condition to evaluate properly nevertheless.
I use t:dataList (myfaces tomahawk xmlns:t="http://myfaces.apache.org/tomahawk") for looping over elements to have correct identifier generation and rendered attribute for different displaying of elements in the list.
<t:dataList layout="simple" value="#{myBean.toSendCheckBoxes}" var="entry">
<input type="checkbox" name="myArray" value="#{entry.value}" />
<h:panelGroup rendered="#{fn:contains(entry.key,'g')}">
<ice:graphicImage url="/resources/images/image1.bmp" />
<b>#{entry.key}</b>
</h:panelGroup>
<h:panelGroup rendered="#{not fn:contains(entry.key,'g')}">
<ice:graphicImage url="/resources/images/image2.bmp" />
<span>#{entry.key}</span>
</h:panelGroup>
</t:dataList>
I've had issues with dynamic element rendering (show/hide rows) within ui:repeat, so I had to use t:dataList instead.