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.
Related
This example is from a book on JSF. The excercise is to refactor the following Facelets code while eliminating <c:if> and fn:toUpperCase(). Usage of <c:forEach> is allowed.
#{myBean.numbers} returns String["one","two","three"]
As the book is on JSF and not on Java, I suppose the existing Java-code is not to be touched. But I can't think of another way to do this solely in Facelets.
<c:forEach var="item" items="#{myBean.numbers}">
<c:if test="#{not fn:endsWith(item,'o')}">
#{item}
</c:if>
<c:if test="#{fn:endsWith(item,'o')}">
#{fn:toUpperCase(item)}
</c:if>
</c:forEach>
Only thing I can think of is using a converter that conditionally uses String#toUpperCase() and then I still do not understand why use of <c:forEach> should still be allowed:
<ui:repeat var="item" value="#{myBean.numbers}">
<h:outputText value="#{item}" converter="conditionalConverter"/>
</ui:repeat>
Is there a more "Facelets way" to do this (and still a need to use <c:forEach>)?
UPDATE:
Instead of <c:if> one could still use e.g. <h:outputPanel> and it's rendered-attribute, but there is still no Java-less replacement for fn:toUpperCase().
I am asking for learning purposes only. I suppose the <ui:repeat>-solution with a converter is the cleanest and represents most how JSF is supposed to be used. Do you think so, too?
As to <c:if>, the JSF alternative to JSTL <c:if> is the rendered attribute on any component. For example, <h:panelGroup> or <h:outputText>. Those components doesn't generate additional markup if there are no attribtues specified which should end up in HTML, like id or styleClass, otherwise they generate a <span>.
Here's an example of both:
<h:panelGroup rendered="#{not fn:endsWith(item,'o')}">
#{item}
</h:panelGroup>
<h:outputText value="#{fn:toUpperCase(item)}" rendered="#{fn:endsWith(item,'o')}" />
As to fn:toUpperCase(), JSF has no alternative. I'm not sure why you would need a JSF alternative as it's essentially not a tag, but a simple EL function which is perfectly usable in both JSTL and JSF tags. In any case, you could if necessary throw in CSS text-transform: uppercase. As this takes place entirely client side, your only problem may be the browser support.
<h:outputText value="#{item}" style="text-transform: uppercase" />
(note: this is just an example, the normal practice is to put styles in its own .css file which you load by <h:outputStylesheet>)
<h:outputText value="#{item}" styleClass="uppercased" />
I suppose the -solution with a converter is the cleanest and represents most how JSF is supposed to be used. Do you think so, too?
I'm a big fan of "Use the right tool for the job". Use JSTL tags to conditionally build the JSF component tree. Use JSF components to generate HTML. That's it. See also JSTL in JSF2 Facelets... makes sense?
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.
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>
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
I just started out using JSF 2.0 but I found myself mixing JSF tags with standard HTML tag
to achieve the desired layout.
Although I am using the facelets to layout my pages, but I think I cant help but mix components.
<ui:define name="content">
<h:form>
<h:commandButton value="Search" action="#{myBean.handleSearch}"/>
<h:commandButton value="Reset" action="#{myBean.handleReset}"/>
<div>
<!-- Some JSF component -->
</div>
I have been thinking if I have been running into bad practice. Any source of info for this?
Thanks
This is not a bad practice. This is perfectly fine. The only reason to use a JSF component is to have access to it in the JSF component tree. It would be a more poor practice to use <h:panelGroup layout="block"> instead of <div> here. But if it was a container of which you'd like to ajax-update its content, then a <h:panelGroup id="foo" layout="block"> would have been perfectly fine as would an 'html5-friendly' <div jsf:id="foo"> with the xmlns:jsf="http://xmlns.jcp.org/jsf" namespace.
For a bit of history, you may find this useful: JSF vs HTML(JSP) for enterprise portals UI layer. Which one to Choose? and WHY?