Ordinal JSF-paging with breadcrumbs composite components - jsf-2

I want to split a big form into several composite components (since I reuse the CCs). One major page (business process page) must allow a user to navigate across 4 CCs. Not necessarily in a sequential order. The user may use the breadcrumbs of the major page to jumb from 1 to 4 - or - use the arrows to navigate sequentially back and forth.
Requirements:
All navigation items (arrows and breadcrumbs) must be placed on the major page.
The major page must never be reloaded. The CCs only.
The user is able to leave the site temporarily without losing the data provided (since there are many fields on these pages).
What's the top-notch approach?
Shell I create #SessionScoped manged bean (controller), which renders CCs instance within a h:panelGroup?
<h:panelGroup id="businessProcessForm" layout="block">
<my:composite rendered="#{controllerBean.navigateToCC}" />
</h:panelGroup>
<h:commandButton value="load previous CC" action="#{controllerBean.setNextCC('CC1')}">
<f:ajax render="businessProcessForm" />
</h:commandButton>
<h:commandButton value="load next CC" action="#{controllerBean.setNextCC('CC3')}">
<f:ajax render="businessProcessForm" />
</h:commandButton>
How do I set the links for the breadcrumbs (they must be created according to the number of CCs)?
How do I set the previous/next links?
How do I populate the h:panelGroup by the arrows or breadcrumbs?
Any solid answers (including Richfaces)?

Providing an answer very late. If you have better alternative of implementing Breadcrumbs in JSF and RichFaces please do let me know.
RichFaces itself does not provide any kind of BreadCrumbs components like the one provided by Trinidad from MyFaces.
An alternative to create a breadcrumb like structure would be to dynamically add images and links in and bind this grid to the backing bean. As and when user navigate from one page to another particularly after doing certain kind of operation, a new component and a corresponding image can be added into this grid and then grid can be rerendered

Related

Primefaces/JSF2 : updating change ui:insert dynamically [duplicate]

I'm just learning JSF 2 thanks to this site I had learned a lot in such a short time.
My question is regarding how to implement a common layout to all my JSF 2 pages and have only the content part of the page refresh not the whole page whenever I click a link/menu from a different panel. I am using the Facelets approach it does what I want except that each time I click a link from a panel (e.g. menu items from left panel) the whole page is refreshed. What I am looking for is a way to refresh only the content part of my page. To illustrate this below is my target pagelayout.
Did not post my code because I'm not sure if Facelets can do this . Are there other approach more suited for my requirement other than Facelets?
A straightforward approach would be the following view:
<h:panelGroup id="header" layout="block">
<h1>Header</h1>
</h:panelGroup>
<h:panelGroup id="menu" layout="block">
<h:form>
<f:ajax render=":content">
<ul>
<li><h:commandLink value="include1" action="#{bean.setPage('include1')}" /></li>
<li><h:commandLink value="include2" action="#{bean.setPage('include2')}" /></li>
<li><h:commandLink value="include3" action="#{bean.setPage('include3')}" /></li>
</ul>
</f:ajax>
</h:form>
</h:panelGroup>
<h:panelGroup id="content" layout="block">
<ui:include src="/WEB-INF/includes/#{bean.page}.xhtml" />
</h:panelGroup>
With this bean:
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
private String page;
#PostConstruct
public void init() {
page = "include1"; // Default include.
}
// +getter+setter.
}
In this example, the actual include templates are include1.xhtml, include2.xhtml and include3.xhtml in /WEB-INF/includes folder (folder and location is fully free to your choice; the files are just placed in /WEB-INF in order to prevent direct access by guessing the URL in browser's address bar).
This approach works in all MyFaces 2.x versions, but requires in case of Mojarra a minimum of 2.3.x. In case you're using a Mojarra version older than 2.3.0, then this all fails when the <ui:include> page in turn contains a <h:form>. Any postback will fail because it is totally missing the view state. You can solve this by upgrading to minimally Mojarra 2.3.0 or with a script found in this answer h:commandButton/h:commandLink does not work on first click, works only on second click. Or, if you're already using PrimeFaces and exclusively use <p:xxx> ajax, then it's already transparently taken into account.
Also, make sure that you're using minimally Mojarra 2.1.18 as older versions will fail in keeping the view scoped bean alive, causing the wrong include being used during postback. If you can't upgrade, then you'd need to fall back to the below (relatively clumsy) approach of conditionally rendering the view instead of conditionally building the view:
...
<h:panelGroup id="content" layout="block">
<ui:fragment rendered="#{bean.page eq 'include1'}">
<ui:include src="include1.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{bean.page eq 'include2'}">
<ui:include src="include2.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{bean.page eq 'include3'}">
<ui:include src="include3.xhtml" />
</ui:fragment>
</h:panelGroup>
The disadvantage is that the view would become relatively large and that all associated managed beans may be unnecessarily initialized even though when they would not be used as per the rendered condition. See also JSTL in JSF2 Facelets... makes sense? for an in depth explanation on <ui:include src="#{...}"> vs <x:someComponent rendered="#{...}">.
As to positioning of the elements, that's just a matter of applying the right CSS. That's beyond the scope of JSF :) At least, <h:panelGroup layout="block"> renders a <div>, so that should be good enough.
Last but not least, this SPA (Single Page Application) approach is not SEO friendly. All the pages are not indexable by searchbots nor bookmarkable by endusers, you may need to fiddle around with HTML5 history in client and provide a server side fallback. Moreover, in case of pages with forms, the very same view scoped bean instance would be reused across all pages, resulting in unintuitive scoping behavior when you navigate back to a previously visited page. I'd suggest to go with templating approach instead as outlined in 2nd part of this answer: How to include another XHTML in XHTML using JSF 2.0 Facelets? See also How to navigate in JSF? How to make URL reflect current page (and not previous one).
If you only want to refresh part of the page, there are only 2 ways to go (for the web in general, not just JSF). You have to use frames or Ajax. JSF 2 supports ajax natively, check out the f:ajax tag to update just 1 component without reloading the entire page.
Netbeans provides a wizard that create the proposed layout with minimal effort using JSF. So, the best way to start is take a look at Facelets Template Wizard and look at the generated source.

wrong ids when render h:panelGroup's inside c:forEach

I have page where I render some h:panelGroup panels. Those panels are realized as plugins registered in a plugin registry on startup.
Part of the plugins api is a custom jsf component where I get the registered plugins for extension point and include their facelet templates by path:
<c:forEach items="#{pluginRegistry.getPlugins(point)}" var="extension">
<ui:include src="#{extension.path}" />
</c:forEach>
The page where I include the panels looks like:
<h:panelGrid id="dashboard" columns="3">
<cmf:insertPageFragments point="dashboardExtensionPoint" />
</h:panelGrid>
For every panel there are facelet templates like the one below:
<rich:panel id="caseDetailsPanel" header="panel label">
<!-- panel content -->
</rich:panel>
Now, the problem is that the very first panel in the list returned by the pluginsRegistry is rendered in the page with the provided id like formId:caseDetailsPanel for example. The rest of them have generated ids like formId:j_idt223 !!! Obviously if I want to rerender some of the panels, I can't do that.
That happens when environment is jboss AS 7.1 with JSF 2.1, richfaces 4.2.3.Final.
When deployed on jboss-eap-6.1 everything looks fine but for now I can't use this jboss version.
Any suggestions on how to workaround this issue?
There can not be multiple JSF components with the same ID. Each JSF component must have an unique ID. When dynamically creating JSF components using JSTL, you need to manually assign and ensure an unique ID, otherwise JSF will discard the provided ID and autogenerate an unique ID.
There are several ways to achieve this, depending on the concrete functional requirement and the existing code.
Use use the iteration index of <c:forEach>.
<c:forEach ... varStatus="loop">
...
<rich:panel id="caseDetailsPanel_#{loop.index}" ...>
This will generate caseDetailsPanel_0, caseDetailsPanel_1, etc depending on the current iteration index.
Use the unique identifier of the currently iterated item. It isn't clear based on the information provided so far if you have any, so here's just a fictive example assuming that the class behind #{extension} has an id property representing the technical DB identifier.
<c:forEach ... var="extension">
...
<rich:panel id="caseDetailsPanel_#{extension.id}" ...>
Wrap #1 or #2 if necessary in a <f:subview> with an unique identifier, so that you don't need to modify the includes.
<c:forEach ... varStatus="loop">
<f:subview id="panel_#{loop.index}">
<ui:include ... />
The <f:subview> creates a new NamingContainer around it, so you end up getting formId:panel_0:caseDetailsPanel, formId:panel_1:caseDetailsPanel and so on.
A completely different alternative would be to use <ui:repeat> instead of <c:forEach>. The <ui:repeat> does not run during view build time, but during view render time. This way there's physically only one <rich:panel id="caseDetailsPanel"> component in the component tree which is reused multiple times during generating HTML whereby JSF will take care of generating the right IDs with the <ui:repeat> index like so formId:repeatId:0:caseDetailsPanel. However, this in turn may cause trouble with <ui:include> as it also runs during view build time and thus can't get the #{extension} at hands.

<ui:include> with dynamic src ... complete madness [duplicate]

This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 5 years ago.
I'm having a massive problem getting an extremely simple task done in JSF. The problem: I have objects, who have aggregated properties that can vary in type from object to object. Depending on the type of the property, I want to use a different set of input fields.
The subtype components reside in frameworks and get loaded on demand. To this end, I use the following code:
<h:panelGroup id="zusatzdaten">
<fieldset class="clear">
<legend>#{tickerUI.ticker.tickerDescription.label}
(#{tickerUI.ticker.tickerDescId})
</legend>
<h:panelGroup rendered="#{tickerUI.editComponentName != null}">
<ui:include src="#{tickerUI.editComponentName}"/>
</h:panelGroup>
</fieldset>
</h:panelGroup>
The name of the component comes out of TickerUI, which is of #SessionScope. Now the dazzling bit: when it first loads, the correct subcomponent is displayed. However, when using a link in the navigation, which should lead to including a different component, the content is NOT updated! This results in an error, because the data is now a different subtype, but the form components are still from the previous one.
When going back from the error and clicking the link again, the correct component is displayed. I logged the value of editComponentName and the correct values are returned. This is very confusing. Why is including the wrong content when the getter returns the correct component name to the 's src attribute?
Help greatly appreciated.
Actually your problem is a classic view build vs view render time problem/misunderstanding. More specifically, the view is built on every new request and reconstructed from a previously saved state on postbacks. Later the view is rendered to produce HTML content.
Now, as <ui:include> is a tag handler, or in official terms a view build time tag, when you first request the page, its value attribute is evaluated, and the included page makes its way into the view. Upon postback, contrary to what you might expect, the incuded contents are already there, in the reconstructed view. So, it is an expected behaviour that you'll have the exact part of view rendered.
As to the solution, you could just simply include an exhaustive list of static includes that are wrapped in conditionally rendered <ui:fragment> JSF components. For simplicity, all content could be places within a container like <h:panelGroup> that's always rendered for ease of AJAX updates. The code could be assembled as follows:
<h:panelGroup id="ui">
<ui:fragment rendered="#{bean.condition1}">
<ui:include src="/path/to/file1.xhtml"/>
</ui:fragment>
...
<ui:fragment rendered="#{bean.condition_n}">
<ui:include src="/path/to/file_n.xhtml"/>
</ui:fragment>
</h:panelGroup>
A classical dilemma, it seems. BalusC's blog gave the solution for my case in the form of a configuration parameter in web.xml:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>

Unique ID for JSF composite component inside dataTable

I am using PrimeFaces 3.2 with JSF 2 in a glassfish 3.1.2.
I have a <p:dataTable> which displays search results containing information on different issues (the issues are assigned to users).
If somebody clicks on the icon of the assigned user a <p:dialog> pops up.
The user icon and the according dialog are implemented using a composite.
Inside the composite I am using some jQuery functions which need a unique ID for each dialog component - I am not able to find a way to solve this problem.
My investigations so far:
I need to set the widgetVar attribute, which works fine as long as I have only one user in the list but it seems that inside a dataTable the widgetVar is not unique for many composites.
Since the user can be displayed more than one time inside the search result, I am not able to setup an widgetVar like this:
<ui:param name="myWidgetVar" value="widget_#{user.id}" />
and use it this way
<p:dialog widgetVar="#{myWidgetVar}">
also using #{cc.id} does not resolve the problem 'cause it only returns the id of the widget without the naming-container part which is always the same.
I need the complete id as displayed in html (e.g.: form:jdt123:dialog:456) - how can I get this?
Does anyone know what to do?
Thanks Pete
My solution is using #{cc.clientId} which I did not know yet.
This gives me the complete html element id constisting of the series of identifiers glued with the UINamingContainer#getSeparatorChar (e.g.: form:jdt123:dialog:jdt456)
Thanks for the answer. You solved my unrelated problem, but I think I stumbled on another solution for your problem.
From the PrimeFaces user guide (http://primefaces.org/documentation.html):
8.2 EL Functions
PrimeFaces provides built-in EL extensions that are helpers to common use cases.
Common Functions
Component
<h:form id="form1">
<h:inputText id="name" />
</h:form>
//#{p:component(‘name’)} returns ‘form1:name’
WidgetVar
<p:dialog id="dlg">
//contents
</p:dialog>
<p:commandButton type="button" value="Show" onclick="#{p:widgetVar(‘dlg’)}.show()" />
It looks like you would be most interested in the WidgetVar implementation.

Close all expanded rows inside a ui:repeat

I have a ui:repeat of auction items inside a table. It is a regular html table because when you click on a Bid commandLink a row opens right under the selected auction item, and displays a bidding component. The Bid commandLink is using ajax like this:
<f:ajax listener="#{bean.addBidView(lot)}" render="bidView" />
addBidView is updating a map of auctionItems and that's how the correct selected row opens up. When the user clicks on the same Bid link again, the addBidView figures it out and closes the bidding component.
Now let's say the user clicks on several rows, and thus opens several bidding components. They all open, but only one is active. So here's my question: how do I get the entire loop to render when I use the above ajax, so that opening a bidding component would automatically close the other open ones. (the auction items map is reflecting the correct bidding component, but only this one bidding area is rendered, so the others aren't effected. If I'm refreshing the whole page, the display is correct, and the problem only exists when I'm using ajax.)
This is the structure of the elements on the page (all this is inside an h:form:)
<table id="bidstable">
<h:panelGroup id="entireLoop">
<ui:repeat id="repeatAuctionItems" var="auctionItem" varStatus="status" value="#{bean.auctionItems}">
<td>... a bunch of td's with the auction item values ... and then:
<h:commandLink id="bid" rendered="#{some conditions}">
<f:ajax listener="#{bean.addBidView(auctionItem)}" render="bidView"/>
</h:commandLink>
</td>
</ui:repeat>
</h:panelGroup>
</table>
I tried pretty much every combination of elements in the "render" -- for example:
render="bidView bidsTable"
or
render="bidView bidsTable:entireLoop:repeateAuctionItems"
and every other trail I can think of. Nothing works. Any ideas?
You have specified a client ID in the <f:ajax render> attribute which is relative to the current NamingContainer component. Client IDs which do not start with the naming container separator character, which defaults to :, are relative to the current NamingContainer component. The <ui:repeat> is such a component. So the client ID has to refer a child of <ui:repeat>. But the component which you are trying to reference is actually outside the <ui:repeat>. You'd need to reference it with an absolute client ID instead.
To find the absolute client ID, you need to open the page in the browser, rightclick and do View Source and then locate the generated HTML of the closest parent JSF component outside <ui:repeat>, which is in your particular case the <h:panelGroup id="entireLoop">. It'll look something like:
<span id="someId:possiblyOtherId:entireLoop">
Take exactly this client ID and prefix it with : for usage in <f:ajax render>.
<f:ajax ... render=":someId:possiblyOtherId:entireLoop" />
If it contains a dynamically generated ID like j_id_123, then you'd need to give all parent NamingContainer components like <h:form> a fixed ID like <h:form id="someId">.
Note that it is not possible to reference the client ID of a plain HTML element like <table id="bidsTable">. It has to be a fullworthy JSF component like <h:dataTable id="bidsTable">.

Resources