There is an application scoped bean.
#Named
#ApplicationScoped
public class Bean {
#Inject
private Service service;
private Entity entity; // Getter.
// Entity is periodically fetched by EJB timers on the server side
// which when fetched notifies associated clients through WebSockets.
// Clients then update themselves by sending an AJAX request.
// All of these things collectively form a different chapter.
// Just that update() needs to be invoked, when a client sends a synchronous GET request
public Bean() {}
#PostConstruct
private void init() {
consume();
}
private void consume() {
entity = service.getEntity();
}
public void update() {
consume();
System.out.println("update called.");
}
}
This bean is accessed by a page included in the master template as follows (West.xhtml) :
<html lang="#{localeBean.language}"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:form>
<!-- This is merely a fake attempt to show something like this is expected. -->
<ui:define name="metaData">
<f:metadata>
<f:viewAction action="{bean.update}"/>
</f:metadata>
</ui:define>
<h:outputText value="#{bean.entity.field}"/>
</h:form>
</html>
There is a false attempt to invoke the update() method by using <f:viewAction> from the master template which is against its semantics.
For this to function, the <f:viewAction> needs to be repeated on individual template clients which makes it difficult to maintain, especially when something needs to be changed.
Is there any possibility to avoid <f:viewAction> being repeated all over the place on every template client?
The master page template looks like the following. It is not required. Just assume that the above page is included using <ui:include src=".."/> in the following master template (/WEB-INF/templates/Template.xhtml).
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<f:view locale="..." ...>
<ui:insert name="metaData"></ui:insert>
<h:head>
<title><ui:insert name="title">Default Title</ui:insert></title>
</h:head>
<h:body>
<h:panelGroup layout="block">
<ui:insert name="contentBar">
<!-- The file given above is included here - West.xhtml. -->
<ui:include src="/WEB-INF/template/contents/West.xhtml"/>
</ui:insert>
</h:panelGroup>
<ui:insert name="content">Default contents</ui:insert>
<!--Other content bars.-->
</h:body>
</f:view>
</html>
<f:viewAction> needs to be placed on template clients associated something like the following.
<ui:composition template="/WEB-INF/templates/Template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<ui:define name="title">Page Title</ui:define>
<ui:define name="metaData">
<f:metadata>
<f:viewAction action="#{bean.update}"/>
</f:metadata>
</ui:define>
<ui:define name="content">
<!--Main contents-->
</ui:define>
</ui:composition>
And so forth, <f:viewAction> needs to be repeated on every such template client for it to do its coherent task.
Is there a way to declare <f:viewAction> appropriately at a single place so that there is no need to repeat it on every associated template client?
Related
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>
I'd like to create a master-detail screen with request params and requestScoped beans but the view param doesn't get filled.
The link that invokes the redirect:
<h:form>
<p:dataTable var="visit" value="#{visitBean.findAllVisits()}">
<p:column headerText="mdh">
<p:commandLink action="#{visitDetailBean.seeVisitDetails(visit)}">
<h:graphicImage library="images" name="details.png"/>
</p:commandLink>
</p:column>
....
The method behind it:
public String seeVisitDetails(Visit visit) throws IOException {
return "/pages/mdh-details.xhtml?visitId=" + visit.getId()+ ";faces-redirect=true";
}
The details xhtml page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:viewParam name="visitId" value="#{visitDetailBean.currentVisitId}" />
</f:metadata>
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="content">
<h:outputText value="#{visitDetailBean.currentVisit.name}"/>
test
</ui:define>
</ui:composition>
and last the details Bean:
private long currentVisitId;
public void setCurrentVisitId(long currentVisitId) {
this.currentVisitId = currentVisitId;
}
public long getCurrentVisitId() {
return currentVisitId;
}
public Visit getCurrentVisit() {
return visitService.findVisit(currentVisitId);
}
currentVisitId is always 0.. I actually can't really find it.
When using templating, anything outside <ui:composition> and <ui:define> is ignored. This includes <f:metadata>.
Move it to inside an <ui:define> of the <ui:composition>.
E.g.
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="metadata">
<f:metadata>
<f:viewParam ... />
</f:metadata>
</ui:define>
<ui:define name="content">
...
</ui:define>
</ui:composition>
See also:
When using <ui:composition> templating, where should I declare the <f:metadata>?
Unrelated to the concrete problem, you'd better place masterLayout.xhtml in /WEB-INF folder to prevent direct access.
<ui:composition template="/WEB-INF/templates/masterLayout.xhtml">
See also:
Which XHTML files do I need to put in /WEB-INF and which not?
This question already has answers here:
commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated
(12 answers)
Closed 6 years ago.
I have a page that has a preRender call that prepares everything to be displayed in the page. I'm not sure if it's relevant, but the page recieves a few params from the index.xhtml that precedes the experience.
I have a commandButton that I need to execute a server-side method (an update, to be precise). There is no need for a refresh on the page.
So I'm using ajax. Here's the button's, code
<h:commandButton value="Save">
<f:ajax event="click" listener="#{bean.save}"/>
</h:commandButton>
So far, on the java side, here's the bean's save method
public void save(){
log.debug("Save executed!");
}
I've added some logging to check what's being executed. When I click the button, the only thing that happens is that the preRender method is executed (and not entirely, just a part of it). Nothing else happens. Visually, the page is not refreshed or anything.
I suspect that when I click the button, the page is being refreshed and therefore, the preRender method (called Build()) is executed, but since there are no parameters (remember that the Build requires parameters passed through <f:param>), something bugs out.
Bottom line: I just need to execute the save method when clicking on the button without refreshing or redirecting anything.
Ideas?
--EDIT--
INDEX.XHTML
<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:c="http://java.sun.com/jstl/core">
<ui:define name="body">
<h:link outcome="agreementDetail.xhtml" value="EA-15558">
<f:param name="serviceId" value="EA-15558" />
<f:param name="site" value="NIC" />
</h:link>
</ui:define>
</html>
AgreementDetail.XHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<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:c="http://java.sun.com/jstl/core">
<f:view>
<f:event type="preRenderView" listener="#{agreement.build}"/>
</f:view>
<ui:define name="body">
<f:view>
<h:form>
<h:commandButton value="Save" action="#{agreement.save}">
<f:ajax/>
</h:commandButton><br/><br/>
<h:dataTable value="#{agreement.licenseServerNames}" var="licenseServerName">
<h:column>
<h:inputText value="#{licenseServerName}"/>
</h:column>
</h:dataTable>
</h:form>
</f:view>
</ui:define>
</html>
AgreementBean.java
#ManagedBean(name="agreement")
#RequestScoped
public class AgreementBean {
#ManagedProperty("#{param.serviceId}")
private String serviceId;
#ManagedProperty("#{param.site}")
private String site;
private List<String> licenseServerNames; //GETTERS AND SETTERS OMITTED TO AVOID EXCESS CODE
#PostConstruct
public void build(){
logger.debug("START");
methodOne();
logger.debug("END");
}
public void save(){
logger.debug("SAVE!!!!!");
for(String name : licenseServerNames){
logger.debug("Servername = "+name);
}
}
}
This worked for me."Show" is a boolean that you can set upon successful save.
<h:commandButton id="ajax" value="Save" action="{agreement.save}" >
<f:ajax execute="#form" render="#form" />
</h:commandButton>
<h:outputScript rendered="#{agreement.show}">alert("save");</h:outputScript>
I was trying to see exactly how property injection works with ViewScoped beans.
I inject the List from One to Two without problem. When I try to inject the same List from Two to Three nothing is injected but I think that's the intended behavior (I might be wrong though).
However when I try to inject the selected value from the SelectOneMenu of Two into Three nothing is being injected.
Is there something I am missing or is that the normal behaviour? If so, how can I retrieve that value in Three?
One.java
#ManagedBean
#ViewScoped
public class One implements Serializable {
private List<String> oneList;
#PostConstruct
void init() {
setOneList(new ArrayList<String>());
getOneList().add("aaa");
getOneList().add("bbb");
getOneList().add("ccc");
getOneList().add("ddd");
}
//Getters + setters...
}
one.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition template="/WEB-INF/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:define name="header">
<h:outputText value="ONE" />
</ui:define>
<ui:define name="content">
<h:form>
<p:commandButton value="two" action="two" ajax="false" />
</h:form>
</ui:define>
</ui:composition>
Two.java
#ManagedBean
#ViewScoped
public class Two implements Serializable {
#ManagedProperty("#{one.oneList}")
private List<String> oneList;
private String twoChoice;
//Getter + setters...
}
two.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition template="/WEB-INF/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:define name="header">
<h:outputText value="TWO" />
</ui:define>
<ui:define name="content">
<h:form>
<p:selectOneMenu id="test" value="#{two.twoChoice}">
<f:selectItems value="#{two.oneList}" />
</p:selectOneMenu>
<p:commandButton value="three" action="three" ajax="false" />
</h:form>
</ui:define>
</ui:composition>
Three.java
#ManagedBean
#ViewScoped
public class Three implements Serializable {
#ManagedProperty("#{two.oneList}")
private List<String> oneList;
#ManagedProperty("#{two.twoChoice}")
private String twoChoice;
private String threeChoice;
//Getters + setters...
}
three.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition template="/WEB-INF/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:define name="header">
<h:outputText value="THREE" />
</ui:define>
<ui:define name="content">
<h:outputText value="#{three.twoChoice}" />
</ui:define>
<h:form>
<p:selectOneMenu value="#{three.threeChoice}">
<f:selectItems value="#{three.oneList}" />
</p:selectOneMenu>
</h:form>
</ui:composition>
Managed properties are not intended to work like that. Keep in mind that a #ViewScoped bean is designed to keep alive as long as the view doesn't change, which means at the moment you navigate from one page to another using the non-ajax command button (in fact you're specifying the navigation-case to go in the action attribute), they should be destroyed, so you can't get any value from them.
Normally, I use #ManagedProperty notation to inject broader scope values (for example, a session value in a view scoped bean). So what is the solution for your case?
Actually you have different options:
You can use <f:viewParam /> tag to send the GET parameters while changing the view.
You can use flash scope, which keeps your values in a map that survives to a redirection. Not recomended if you are using Mojarra implementation of JSF, they still have to fix some problems with them.
Take care of the params yourself and set them in some data structure in session scope, after, when you recover them in your destination bean you can remove them.
I have the following page:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
xmlns:thehub="http://java.sun.com/jsf/composite/components/thehub"
template="/templates/masterTemplate.xhtml">
<f:metadata>
<f:viewParam
id="returnToViewParam"
name="returnTo"
value="#{loginMB.returnTo}"
required="true" />
<f:viewParam
id="oauth_verifierViewParam"
name="oauth_verifier"
value="#{loginMB.oauth_verifier}" />
<f:viewParam
id="oauth_tokenViewParam"
name="oauth_token"
value="#{loginMB.oauth_token}" />
<f:event
type="preRenderView"
listener="#{loginMB.preRenderView()}" />
</f:metadata>
<ui:define name="body">
<o:form
id="loginForm"
includeViewParams="true">
<div class="form-vertical well">
<h4>New Users</h4>
<h5>
<h:link outcome="signup">Click here to create an account</h:link>
</h5>
<hr />
<h4>Existing Users</h4>
<h:commandButton
id="googleLoginCommandLink"
styleClass="btn"
action="#{loginMB.redirect()}"
value="Google">
<f:param
name="returnTo"
value="#{param.returnTo}" />
</h:commandButton>
<div class="clearfix"></div>
</div>
</o:form>
</ui:define>
</ui:composition>
And the following bean:
#ManagedBean
#RequestScoped
public class LoginMB implements Serializable {
private static final long serialVersionUID = 1L;
private String returnTo;
public void redirect() {
log.debug("redirect() returnTo:{}", returnTo);
......getter/setters
}
No matter what I do, I can't seem to get returnTo bound once the commandButton is clicked. Since this is a login page, I'd really not like to have LoginMB be a #ViewScoped bean.
Advice? Is there a better way to handle this scenario?
EDIT:
I'm running this on TomEE+ Server v1.5.1 which is served up by MyFaces 2.1.10
Added full page
Clarified the problem: Inside the redirect() function, returnTo is null
Your <f:metadata> is outside <ui:define> and is therefore completely ignored. Add an <ui:insert name="metadata"> to the master template and put the <f:metadata> inside an <ui:define name="metadata"> of the template client.
See also:
When using <ui:composition> templating, where should I declare the <f:metadata>?
Once you fixed that, you can safely remove the <f:param> from the command button. This job is already done by <o:form includeViewParams="true">. If you didn't have it, then the <f:param> would indeed have been mandatory and you'd need to copypaste the same over all command links and buttons in the same form.