Control generated ID of ui:repeat - jsf

Sorry for the simple question. I am learning to use JSF 2.2 to create a form and trying to keep it close to plain HTML5 as possible. I have an ui:repeat generated list that goes like this:
<ul id="nameLst">
<ui:repeat var="d" value="#{controller.names}" varStatus="status">
<li>
<input
jsf:id="nameTxt"
jsf:binding="#{nameTxt}"
jsf:value="#{d}"
type="hidden" />#{d}
</li>
</ui:repeat>
</ul>
It gets rendered like this:
<ul id="nameLst">
<li>
<input id="j_idt14:0:nameTxt" name="j_idt14:0:nameTxt" value="Name1" type="hidden">
Name1
</li>
<li>
<input id="j_idt14:1:nameTxt" name="j_idt14:1:nameTxt" value="Name2" type="hidden">
Name2
</li>
</ul>
Now, I am trying to add names using JavaScript only to this list. Problem is, how can I control this generated id, so I can use it in JavaScript. And mainly, if the list starts empty, how do I generate this id so it can be correctly posted back to the managed bean.

how can I control this generated id, so I can use it in JavaScript
Just give it a fixed id.
<ui:repeat id="names" ...>
Alternatively, use jsfc attribute to turn <ul> into an <ui:repeat>.
<ul id="names" jsfc="ui:repeat" value="#{bean.names}" var="name">
<li>
<input jsf:id="name" type="hidden" value="#{name}" />
</li>
</ul>
And mainly, if the list starts empty, how do I generate this id so it can be correctly posted back to the managed bean
Use JSF for this instead of JS. An example can be found here: How to dynamically add JSF components
See also:
How can I know the id of a JSF component so I can use in Javascript
JavaServer Faces 2.2 and HTML5 support, why is XHTML still being used

Related

Conditionally set <ul class> of <h:messages> depending on message severity

In JSF 1.2 I would like to have the HTML of <h:messages> like the following one
<ul id="messages" class="warnmessage">
<li>Unable to link ...</li>
<li>Unable to link ..</li>
</ul>
OR
<ul id="messages" class="errorMessage">
<li>Unable to link ...</li>
<li>Unable to link ..</li>
</ul>
depending on what severity of the message is. What is blocking me is that <h:messages> cannot render this html with different class assignment on <ul> level, it can be only assigned on <li> level. I would like to know if it's possibile to use this mechanism and do something like :
<h:messages styleClass="errormessage" id="errormessages" rendered="#{not empty Bean.errorMessages}"/>
<h:messages styleClass="warnmessage" id="warnmessages" rendered="#{not empty Bean.warnMessages}"/>

JSF too many commandLinks (h:form) lead to ViewExpiredException

I am having a JSF application which creates and presents about 50 reports. These reports are rendered PNGs and under the pictures a table is displayed.
This table uses a RichFaces 4 togglePanel with switchType="client". The togglePanel is just for collapsing and expanding the table.
<h:form>
<rich:togglePanel id="#{param.reportWrapper}_togglePanel"
stateOrder="opened,closed" activeItem="opened" switchType="client">
<rich:togglePanelItem name="closed">
<h:panelGroup>
<div class="myclass">
<ul class="container-icons">
<li>
<h:commandLink styleClass="container-max" value="maximieren">
<rich:toggleControl targetPanel="#{param.reportWrapper}_togglePanel" targetItem="#next" />
</h:commandLink>
</li>
</ul>
<h3>My Heading</h3>
</div>
</h:panelGroup>
</rich:togglePanelItem>
<rich:togglePanelItem name="opened">
<h:panelGroup>
<div class="myclass">
<ul class="container-icons">
<li>
<h:commandLink styleClass="container-min" value="minimieren">
<rich:toggleControl targetPanel="#{param.reportWrapper}_togglePanel" targetItem="#prev" />
</h:commandLink>
</li>
</ul>
<h3>Another Heading</h3>
<div class="scrolling-table-content">
<rich:dataTable>
// ...
</rich:dataTable>
</div>
</div>
</h:panelGroup>
</rich:togglePanelItem>
</rich:togglePanel>
</h:form>
The problem is, that I sometimes get a ViewExpiredExceptions when the reports are loaded. My numberOfLogicalViews and numberOfViewsInSession is 14. I dont want to set it to 50, because of memory issues and because it should not be necessary as only one report is really shown at the same time.
I tried to remove the h:form tags, which are seen as logicalView. In my opinion the togglePanel is not the item, which needs the form because it's switch type is client (not server and ajax, which need form tags). But the command link does need the form tag, because if I remove it, an error occurs saying "this link is disabled as it is not nested within a jsf form".
So I tried to replace the commandLink with a commandButton. This worked fine first and the form wasnt necessary anymore. But somehow the behaviour is completely random now. Sometimes the tables can be expanded, sometimes nothing happens when i click the expand button. When i add form tags again, it works fine, but doesnt solve my ViewExpiredException.
Hope, somebody can help me out here...
Thanks for your help!
Buntspecht
If you only need to switch the panel you can use <a4j:commandLink> that lets you limit the execution scope (so it won't submit the whole form). Or you can remove the command components altogether and just use JavaScript API of the togglePanel:
<a onclick="#{rich:component('panelId')}.switchToItem(#{rich:component('panelId')}.nextItem())">Next</a>
Thank you very much for your help Makhiel. I finally managed to solve the problem with the commandButtons solution. I can't explain why, but the IDs of my togglePanelItems got duplicated in the different reports.
Giving every togglePanelItem a unique ID like
<rich:togglePanelItem name="closed" id="#{param.reportWrapper}_opened">
and
<rich:togglePanelItem name="opened" id="#{param.reportWrapper}_closed">
solved the problem...
So now we got rid of all the h:form tags and thus have about 50 logical views less! :)

Dynamic menu in JSF

I am trying to implement a dynamic menu with JSF2/RichFaces. The idea is simple, the menu is defined in the database, and depends on the connected user.
The menu is correctly generated, but the action is not working, and I don't understand why.
This is my JSF code:
<c:forEach items="${sessionMenu.menus}" var="menu"><!-- Menus -->
<li>
${menu.label}
<ul class="sub-menu" id="sub-${menu.label}">
<c:forEach items="${menu.submenus}" var="submenu"><!-- Sub menus -->
<li>
<a4j:commandLink value="${submenu.label}" action="${sessionMenu.action}"/>
</li>
</c:forEach>
</ul>
</li>
</c:forEach>
${sessionMenu.action} returns a string which should redirect to another page (defined as a navigation-case in the faces-config file).
I also tried to replace the action with a hard-coded string, and it works, I guess that the problem arrives with the EL which is not correctly interpreted.
If some one can help me to understand what is wrong?
Edit:
Here is an example of the links generated:
<a id="mainForm:j_id_g_0_2_0_2" onclick="RichFaces.ajax("mainForm:j_id_g_0_2_0_2",event,{"incId":"1"} );return false;" name="mainForm:j_id_g_0_2_0_2" href="#">Screen</a>
This one was generated dynamically and it does nothing;
<a id="mainForm:j_id_g_0_2_0_4" onclick="RichFaces.ajax("mainForm:j_id_g_0_2_0_4",event,{"incId":"1"} );return false;" name="mainForm:j_id_g_0_2_0_4" href="#">Screen</a>
And this one used a static string and the redirection is working properly.
These 2 links were generated in the same page and on the same request.
Finally, I got it working, I just changed the action value to the method name as it is defined in my bean, that means:
<a4j:commandLink value="#{submenu.label}" action="#{sessionMenu.getAction()}" />
If some one can explain why, I am curious.

Prevent <ui:repeat> from being inserted in generated html

I'm trying to combine JSF2.2 with bootstrap 3.2.0 components and got stuck when trying to use the button group with a dynamically generated list of labels. I traced the problem down to the ui:repeat tag being included in the generated html and thus preventing the correct css styling of bootstrap.
The xhtml code is:
<div class="btn-group">
<ui:repeat var="role" value="#{editUserView.allRoles}">
<button class="btn btn-success" type="button">#{role.name}</button>
</ui:repeat>
</div>
and the out put html is this:
<div class="btn-group">
<ui:repeat><button class="btn btn-success" type="button">Label1</button></ui:repeat>
<ui:repeat><button class="btn btn-success" type="button">Label2</button></ui:repeat>
<ui:repeat><button class="btn btn-success" type="button">Label3</button></ui:repeat>
<ui:repeat><button class="btn btn-success" type="button">Label4</button></ui:repeat>
<ui:repeat><button class="btn btn-success" type="button">Label5</button></ui:repeat>
</div>
Is there a way to prevent the JSF tag from the xmlns:ui="http://xmlns.jcp.org/jsf/facelets namespace being printed in the generated html WITHOUT having to write custom components?
Update
Just for curiosity, the buttons are being displayed as normal buttons (with spacing in between them) and not as desired in the group button all together style.
This is recognizable as Mojarra issue 2900 which was fixed in 2.2.2. It's currently already at 2.2.8. So, just upgrading Mojarra to latest version should do.
That said, the <ui:repeat> is not a taghandler, but a true UI component. I fixed your question title. The <c:forEach> is a true taghandler. See also JSTL in JSF2 Facelets... makes sense? for the difference.

Composite components & ID

I want to implement some javas cript into my JSF composite component, but I have problem with id. My java script with:
document.getElementById("myForm:customerId")
does not work, because the id is wrong. I have JSF composite component:
<composite:implementation>
<div id="element_customer">
<h2 class="element_title">Customer</h2>
<h:form id="myForm">
<h:inputText id="customerId" value="#{cc.attrs.customerId}"/>
</h:form>
</div>
</composite:implementation>
and HTML output is:
<div id="element_customer">
<h2 class="element_title">Customer</h2>
<form id="j_idt44:myForm" name="j_idt44:myForm" method="post" ... >
<input type="hidden" name="j_idt44:myForm" value="j_idt44:myForm" />
<input id="j_idt44:myForm:customerId" ... name="j_idt44:myForm:customerId" />
</form>
</div>
Why is "j_idt44" used in HTML output?
Composite components are NamingContainer components like <h:form>, <h:dataTable>, etc. This allows you to have multiple of them in the same view without conflicting IDs.
You need to give the composite component a fixed ID as well. E.g.
<my:composite id="someId" />
I'd also suggest to use <div id="#{cc.id}"> instead of <div id="element_customer">. It will then become someId with the above example.
Unrelated to the concrete problem, this isn't entirely the right purpose of a composite component. A composite component is intented to be of the same kind of <h:inputText>, etc. You seem to rather want a tag file or maybe an include file. See also When to use <ui:include>, tag files, composite components and/or custom components?

Resources