dynamic view injection or routing - jsf

i have a JSF projekt and in there i have different views, which are backed by ManagedBeans.
What i would like to achieve is to change some views while others stay where they are. this has to happen dynamically. In other words. I want to inject and remove views from an xhtml page without a page refresh. I have no clue how to achieve this.
Even better would be a dynamic view injection based on urls. angularjs does that very well.
But even without routing it would be great.
Thanks in advance.
Here is an example in pseudo code:
<nav>
<h:link action="navigationBean.changeView(view1)" method="ajax">Link1</h:link>
<h:link action="navigationBean.changeView(view2)" method="ajax">Link2</h:link>
</nav>
<h:viewContainer>
// view selected by clicking the nav links should be injected here without page reload
</h:viewContainer>

What you ask is better done using Facelet templating. You'll be able that way to have a page template with the shared content (the navigation menu in your case) and make the rest of the views inherit from it.
What can I see from your suggested solution is that you're abusing the POST calls. #{fragmentsPresenter.changeView('viewOne')} doesn't make sense just because you already know where you want to go to when you press that link (to viewOne), So you'll be better using plain links for that.
Here you've got an example showing how to handle navigation in a proper way. Let's suppose you've got a view controller even you won't need it in most of the cases:
ViewController.java
/**
* Give the scope you want to your bean depending on what are your operations
* oriented to. This example could be #ApplicationScoped as well
*
* #author amaeztu
*
*/
#ManagedBean
#SessionScoped
public class ViewController {
/**
* Just gets the current view path and switches to the other one
*
* #return
*/
public String changeView() {
String viewId = FacesContext.getCurrentInstance().getViewRoot()
.getViewId();
if (viewId.equals("/view1.xhtml")) {
return "/view2";
} else {
return "/view1";
}
}
}
This controller's job is just to check what view are you coming from and switch to the other one. It's pointless to perform a POST request (to send a form) just to navigate to the other view, while you could evaluate it before page rendering.
Here you've got how the template view is built:
template.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head />
<h:body>
<h:form>
<!-- Use POST requests like this only when you have
to determine the destination dinamically at server side -->
<h:commandButton value="Switch View with POST"
action="#{viewController.changeView}" />
<br />
</h:form>
<!-- For plain navigation, use GET requests -->
<h:link value="Go to view 1" outcome="/view1" />
<br />
<!-- Determine where we are at page rendering time
and evaluate the other view path -->
<h:link value="Switch view without POST"
outcome="#{view.viewId eq '/view1.xhtml' ? '/view2' : '/view1'}" />
<br />
<br />
<ui:insert name="content" />
</h:body>
</ui:composition>
This template page defines a shared button/link set and calls for content. I've implemented different kind of navigation options. Using <h:link /> is, in this case, the most straight-forward way. Check the second link, here we evaluate the current view id when it gets rendered and a link to go to the opposite one is created. Cool, isn't it?
Now here it is the implementation of the child views:
view1.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" template="/template.xhtml">
<ui:define name="content">
<!-- Here you could have some #ViewScoped
bean managing the content i.e. #{view1Bean} -->
View 1
</ui:define>
</ui:composition>
view2.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" template="/template.xhtml">
<ui:define name="content">
View 2
</ui:define>
</ui:composition>
You'll be able to type their address in your browser and see them, that's what's called bookmarkable ;-)
See also:
Get current page programmatically

Ok, i solved it as follows:
my View:
<h:body>
<nav>
<h:form>
<h:commandLink action="#{fragmentsPresenter.changeView('viewOne')}">
viewOne
<f:ajax execute="#this" render="fragment-container" />
</h:commandLink>
<h:commandLink action="#{fragmentsPresenter.changeView('viewTwo')}">
viewTwo
<f:ajax execute="#this" render="fragment-container" />
</h:commandLink>
</h:form>
</nav>
<h:panelGroup id="fragment-container">
<ui:fragment rendered="#{fragmentsPresenter.activeView('viewOne')}">
<div>i am view one!</div>
</ui:fragment>
<ui:fragment rendered="#{fragmentsPresenter.activeView('viewTwo')}">
<div>i am view Two!</div>
<ui:include src="fragment.xhtml"/>
</ui:fragment>
</h:panelGroup>
and my ManagedBean:
#ManagedBean
#SessionScoped
public class FragmentsPresenter {
private String activeView;
public void setActiveView(String viewName) { this.activeView = viewName; }
public String getActiveView() { return this.activeView; }
public FragmentsPresenter() { this.activeView = "viewOne"; }
public void changeView(String viewName) { this.activeView = viewName; }
public boolean activeView(String viewName) {
return activeView.equals(viewName);
}
}

Related

How to display a wait indicator for f:viewAction?

I have a JSF page that loads the properties of an object (for which the id is passed in the URL). The loading can last more seconds, so I would like to display a wait/busy indicator or a "Loading..." message.
This is done using "viewAction"
<f:metadata>
<f:viewAction action="#{myBean.loadParams}" />
</f:metadata>
Is there a simple way to accomplish this goal? I'm using Primefaces.
PrimeFaces has already a component ready for that: the <p:outputPanel deferred="true">. You only need to make sure that the #{heavyBean} is only referenced in a component (and thus definitely not in a tagfile like <c:xxx> for the reasons explained here) within the <p:outputPanel> and not somewhere else.
...
#{notHeavyBean.property}
...
<p:outputPanel deferred="true">
...
#{heavyBean.property}
...
</p:outputPanel>
...
#{anotherNotHeavyBean.property}
...
Then you can do the heavy job in its #PostConstruct method. Do the job you originally did in <f:viewAction> there in the #PostConstruct.
#Named
#ViewScoped
public class HeavyBean implements Serializable {
#PostConstruct
public void init() {
// Heavy job here.
}
// ...
}
If you need to access properties of other beans, simply #Inject those beans in the HeavyBean. E.g. in case you needed the ID view param:
<f:viewParam name="id" value="#{notHeavyBean.id}" />
#Inject
private NotHeavyBean notHeavyBean; // Also #ViewScoped.
#PostConstruct
public void init() {
Long id = notHeavyBean.getId();
// Heavy job here.
}
The <p:outputPanel> already comes with an animated gif. You can easily customize it via CSS.
.ui-outputpanel-loading {
background-image: url("another.gif");
}
I would like to propose also this simple approach:
one "landing" page (the page where we first navigate in) with a wait indicator and an autoRun remoteCommand with an event that read the parameter "param" from the URL and save it in the bean.
the remoteCommand does a redirect to another page (where the long-running method loadParams is executed)
In this way the wait indicator is shown until the second page is ready to be displayed.
Do you see any weaknesses?
Here the landing page:
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
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:p="http://primefaces.org/ui">
<h:head>
...
</h:head>
<f:metadata>
<f:event type="postAddToView" listener="#{notHeavyBean.readProperty}" />
<f:viewParam name="param"/>
</f:metadata>
<h:body>
<p:outputPanel layout="block">
<i class="fa fa-circle-o-notch fa-spin layout-ajax-loader-icon" aria-hidden="true" style="font-size: 40px;position: relative;top: 50%;left: 50%;"></i>
</p:outputPanel>
<h:form>
<p:remoteCommand action="#{notHeavyBean.redirect}" autoRun="true"/>
</h:form>
</h:body>

How JSTL tags evaluated in JSF 2.1 for the below

I have the below Test.xhtml where i can select mode as Sea/Air. I don't want to load the all the 4 pages(Page1.xhtml, Page2.xhtml, Page3.xhtml, Page4.xhtml) into the jsf view tree. In my scenario, mode once selected and saved that can not be changed to other mode. The saved mode will be shown as view mode. Since at any point of time i need only 2 files (Page1.xhtml, Page2.xhtml (or) Page3.xhtml, Page4.xhtml)... I am using the JSTL tag handler for dynamic including the pages. the below thing is working fine. But i am not able to understand how the rendering working.
Test.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head><title>JSF And JSTL</title>
<link href="../css/styles.css"
rel="stylesheet" type="text/css"/>
</h:head>
<h:body>
<div align="center">
<h:form id="form1">
<h:panelGrid columns="2">
<h:outputLabel value="Select Mode"/>
<h:selectOneMenu id="selectPageId" value="#{testBean.selectedMode}">
<f:selectItem itemLabel="Select" />
<f:selectItem itemLabel="Sea" itemValue="sea" />
<f:selectItem itemLabel="Air" itemValue="air" />
<f:ajax execute="#this" render="pageGroup"/>
</h:selectOneMenu>
</h:panelGrid>
<h:panelGroup id="pageGroup">
<c:if test="#{'sea' eq testBean.selectedMode}">
<ui:include src="Page1.xhtml" />
<ui:include src="Page2.xhtml" />
</c:if>
<c:if test="#{'air' eq testBean.selectedMode}">
<ui:include src="Page3.xhtml" />
<ui:include src="Page4.xhtml" />
</c:if>
</h:panelGroup>
</h:form>
</div>
</h:body>
</html>
TestBean.java
package com.test;
#ManagedBean(name = "testBean")
#ViewScoped
public class TestBean {
private String selectedMode;
public String getSelectedMode() {
return selectedMode;
}
public void setSelectedMode(String selectedMode) {
this.selectedMode = selectedMode;
}
}
Updated Again
Please help to clarify the below.
When i send a request first time to access the Test.xhtml, the jsf view (UIViewRoot) gets created. Since testBean.selectedMode by default is null.. this created view would not contain any component details of <c:if> and <ui:include> which are inside of <h:panelGroup id="pageGroup">.
After that when i send a second request(ajax postback) by selecting the mode as sea, the UIViewRoot gets created again for the request in the Restore View Phase. While UIViewRoot creation for this request in Restore View Phase, the tag handlers(<c:xxx>, <f:xxx>, <ui:include>) will get executed.
Since the selected value 'sea' will be updated to model(TestBean.java) only in Update Model Values Phase, in the Restore View Phase the EL #{testBean.selectedMode} will be evaluated to null.
So in my example Test.xhtml page the both tag handlers (<c:if test="#{'sea' eq testBean.selectedMode}"> and <c:if test="#{'air' eq testBean.selectedMode}">) will be evaluated to false. So the UIViewRoot does not contain any information related to the components inside <h:panelGroup id="pageGroup">.
Even though the UIViewRoot does not contain the components information inside the <h:panelGroup id="pageGroup">, How JSF able to render the included pages(<ui:include src="Page1.xhtml" />,<ui:include src="Page2.xhtml" />) successfully?
I am not sure how JSF rendering happening here. While Rendering, Is JSF re-creating the component tree for the particular rendering part(<h:panelGroup id="pageGroup">).?
Note: The example is working fine when i select mode as sea/air. The corresponding parts are rendering correctly.

Processing of JSTL in JSF [duplicate]

I have the below Test.xhtml where i can select mode as Sea/Air. I don't want to load the all the 4 pages(Page1.xhtml, Page2.xhtml, Page3.xhtml, Page4.xhtml) into the jsf view tree. In my scenario, mode once selected and saved that can not be changed to other mode. The saved mode will be shown as view mode. Since at any point of time i need only 2 files (Page1.xhtml, Page2.xhtml (or) Page3.xhtml, Page4.xhtml)... I am using the JSTL tag handler for dynamic including the pages. the below thing is working fine. But i am not able to understand how the rendering working.
Test.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head><title>JSF And JSTL</title>
<link href="../css/styles.css"
rel="stylesheet" type="text/css"/>
</h:head>
<h:body>
<div align="center">
<h:form id="form1">
<h:panelGrid columns="2">
<h:outputLabel value="Select Mode"/>
<h:selectOneMenu id="selectPageId" value="#{testBean.selectedMode}">
<f:selectItem itemLabel="Select" />
<f:selectItem itemLabel="Sea" itemValue="sea" />
<f:selectItem itemLabel="Air" itemValue="air" />
<f:ajax execute="#this" render="pageGroup"/>
</h:selectOneMenu>
</h:panelGrid>
<h:panelGroup id="pageGroup">
<c:if test="#{'sea' eq testBean.selectedMode}">
<ui:include src="Page1.xhtml" />
<ui:include src="Page2.xhtml" />
</c:if>
<c:if test="#{'air' eq testBean.selectedMode}">
<ui:include src="Page3.xhtml" />
<ui:include src="Page4.xhtml" />
</c:if>
</h:panelGroup>
</h:form>
</div>
</h:body>
</html>
TestBean.java
package com.test;
#ManagedBean(name = "testBean")
#ViewScoped
public class TestBean {
private String selectedMode;
public String getSelectedMode() {
return selectedMode;
}
public void setSelectedMode(String selectedMode) {
this.selectedMode = selectedMode;
}
}
Updated Again
Please help to clarify the below.
When i send a request first time to access the Test.xhtml, the jsf view (UIViewRoot) gets created. Since testBean.selectedMode by default is null.. this created view would not contain any component details of <c:if> and <ui:include> which are inside of <h:panelGroup id="pageGroup">.
After that when i send a second request(ajax postback) by selecting the mode as sea, the UIViewRoot gets created again for the request in the Restore View Phase. While UIViewRoot creation for this request in Restore View Phase, the tag handlers(<c:xxx>, <f:xxx>, <ui:include>) will get executed.
Since the selected value 'sea' will be updated to model(TestBean.java) only in Update Model Values Phase, in the Restore View Phase the EL #{testBean.selectedMode} will be evaluated to null.
So in my example Test.xhtml page the both tag handlers (<c:if test="#{'sea' eq testBean.selectedMode}"> and <c:if test="#{'air' eq testBean.selectedMode}">) will be evaluated to false. So the UIViewRoot does not contain any information related to the components inside <h:panelGroup id="pageGroup">.
Even though the UIViewRoot does not contain the components information inside the <h:panelGroup id="pageGroup">, How JSF able to render the included pages(<ui:include src="Page1.xhtml" />,<ui:include src="Page2.xhtml" />) successfully?
I am not sure how JSF rendering happening here. While Rendering, Is JSF re-creating the component tree for the particular rendering part(<h:panelGroup id="pageGroup">).?
Note: The example is working fine when i select mode as sea/air. The corresponding parts are rendering correctly.

How to initialize a certain MBean first when navigating to a page

I have a page split in 3. First part is a list of links which is bound to a mBean (MLeft), and the second is the current mBean (MCenter) of the page I'm in. MCenter inserts data into MLeft so that the links from the first part are custom to the page I'm currently in. The thing is that when the page is rendered and the links are evaluated MLeft is created before MCenter (because is found earlier in the page) and MCenter doesn't get the chance to insert the links in MLeft, so no links are displayed. I put a call to MCenter before the links using an output text referring a dummy property which is an empty string.
I don't like this workaround, I had this problem in the past too with Seam and #Out and I solved it like this. Is there a better approach?
Maybe you can use follow method:
<f:view beforePhase="#{userMB.verifyUser}" />
the method will be invoked when the page be loaded
I think you need to use templates:
template.xhtml
<ui:composition>
<h:head>
<title>
<ui:insert name="title" />
</title>
<h:outputStylesheet name="css/haleczander.css" />
</h:head>
<h:body>
<div class="left">
<ui:include src="static_links.xhtml />
<ui:repeat value="#{links}" var="link">
<h:outputLink value="#{link}">#{link}</h:outputLink>
</ui:repeat>
</div>
<div class="center">
<ui:insert name="content" />
</div>
</h:body>
</ui:composition>
content1.xhtml
<ui:composition template="template.xhtml">
<ui:define name="title">
Content page 1
</ui:define>
<ui:param name="links" value="#{middle.links}" />
<ui:define name="content">
Blah blah 1
</ui:define>
</ui:composition>
I'm assuming links is a list or an array of strings, but you could make anything of it: a list of custom MyLink object, ... (as long as there is an appropriate getter)
You could also replace middle.links with whatever you like, event a method call like #{middle.getLinks(page1)}
Just make Center a managed property of Left. E.g.
#ManagedBean
#RequestScoped
public class Left {
#ManagedProperty(value="#{center}")
private Center center;
#PostConstruct
public void init() {
// Initialize links based on Center here.
}
// ...
}

modalPanel lazy rendering when displayed

Motivation: I want to reduce the size of the page when is accessed, so I thought that lazy rendering on modalPanels would help. The idea is to render the modalPanel when the user clicks the link that displays it.
I want to lazy render on rich:modalPanel when the link to display it is clicked. To achieve this I've found a way:
Code of the modalPanel, wrapped inside a a4j:outputPanel
<a4j:outputPanel id="a4jPanel">
<rich:modalPanel id="panel" rendered="#{bean.renderPanel}">
<!-- here modalPanel things -->
</rich:modalPanel>
</a4j:outputPanel>
Code of the backing bean (session scope):
public boolean isRenderPanel() {
return renderPanel; //default value is false;
}
public void setRenderPanel(boolean value){
this.renderPanel=value;
}
public setRenderFalse(){
this.setRenderPanel(false);
}
Code of the page where it is invoked:
<a4j:form>
<a4j:jsFunction name="setRenderFalse" action="#{user.setRenderFalse}"/>
<a4j:commandLink value="render and show" oncomplete="Richfaces.showModalPanel('panel');setRenderFalse();" reRender="a4jPanel">
<f:setPropertyActionListener target="#{user.renderPanel}" value="true" />
</a4j:commandLink>
</a4j:form>
Problems:
The modalPanel needs to be wrapped inside an a4j:outputPanel because reRendering directly the modalPanel does not work (I never understood why).
After rendering it, an extra request is needed to set the render value to false (the bean is session scoped). Otherwise if we reload the page there would not be any lazy rendering because the value was set to true.
The backing bean has to handle one property to keep the state for each modalPanel, although this property is set to true whenever the link is clicked and set to false when the request is finished. I've tried to keep the rendered state with JS variables but it does not seem to work (they are just read once the page is loaded and never again).
Any more elegant way to do this?
There is a nice solution regarding your question. All is needed is a way to detect postback and couple of xhtmls.
First of all we need a bean that will help with indication of postback
public class HelperBean {
public boolean isPostback() {
FacesContext context = FacesContext.getCurrentInstance();
return context.getRenderKit().getResponseStateManager().isPostback(context);
}
}
empty.xhtml - for a blank content
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
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:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
</ui:composition>
modal.xhtml - for wrapping the modal definition
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
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:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<rich:modalPanel id="myLazyModal">
<h:outputText value="Modal Content"/>
</rich:modalPanel>
</ui:composition>
lazyModal.xhtml - for handling inclusion of the above xhtmls
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
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:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<a4j:include id="lazyModal" layout="block"
viewId="#{helperBean.postback ? 'modal.xhtml' : 'empty.xhtml'}"/>
</ui:composition>
finally use it
<h:form id="frmTest">
<a4j:include id="lazyModalContainer" layout="block" viewId="lazyModal.xhtml"/>
<a4j:commandButton id="btnSubmit" value="Submit" reRender="lazyModalContainer"
oncomplete="Richfaces.showModalPanel('myLazyModal');"/>
</h:form>
Now when the page is loaded empty.xhtml will be included till btnSubmit is clicked.
Regarding to the problems you mentioned (1):
Re-rendering components with the rendered attribute is a bit catchy. When the rendered expression is evaluated to false no markup is sent back to the client. Therefore, supplying the id of the none rendered component to the reRender attribute will never work cause there is no such id on the client side (DOM).
i think you should make separate xhtml(facelet) of modal panel and use ui:include and than on link click the link no need of boolean property than.
enter code here : <ui:include src="modalPanel path">, <a4j:commandLink id="abclink" oncomplete="#{rich:component('yourPanelname')}.show()" reRender="yourPanelForm"/>
Another solution is to set the render attribute of your modalpanel programmatically in the JSF component tree. So you wont't need an additional backing bean which has to handle one property to keep the state for each modalPanel:
Managed Bean:
public void togglePanel(ActionEvent event) {
UIComponent component = event.getComponent();
String forId = (String) component.getAttributes().get("for");
FacesContext currentInstance = FacesContext.getCurrentInstance();
UIComponent findComponent = ComponentFinder.findComponent(currentInstance.getViewRoot(), forId);
findComponent.setRendered(!findComponent.isRendered());
}
View:
Open the panel:
<a4j:commandLink actionListener="#{myBean.togglePanel}" value="Open">
<f:attribute name="for" value="targetPanelId" />
Close the Panel:
<a4j:commandLink id="closePanel" actionListener="#{myBean.togglePanel}" value="someLabel">
<f:attribute name="for" value="targetPanelId" />
The modalpanel:
<a4j:outputPanel ajaxRendered="true">
<rich:modalPanel id="targetPanelId" width="800" height="500" rendered="false" showWhenRendered="true">

Resources