I'm developing a web application using JSF 2 and prettyfaces. I annotated one of my #ViewScoped beans with pretty annotations. That's what I have:
#ManagedBean
#ViewScoped
#URLMapping(parentId = "app-list", id = "app-view", pattern = "/detail/#{appId}",
viewId = "/system/manage_app/content/app_detail/app_detail.xhtml")
public class NavegableAppView extends SystemNavegable {
/**
Basically that shows the details of an application which is installed in my system. This bean can be instanced in two ways, passing #{appId} param, which indicates the id of the application which I want to load, or without that param, in this case the bean will recover this id from a #SessionScoped bean.
That's how the page /system/manage_app/content/app_detail/app_detail.xhtml is managing the parameter:
<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/jsp/jstl/core"
xmlns:p="http://primefaces.org/ui"
template="/templates/general_template.xhtml">
<ui:define name="metadata">
<f:metadata>
<f:viewParam id="appId" name="appId"
value="#{navegableAppView._ParamApp}" required="false" />
<f:event type="preRenderView"
listener="#{navegableAppView.initialize}" />
</f:metadata>
</ui:define>
<ui:define name="general_content">
<p:panel>
<!--More stuff-->
The problem here is I want NavegableAppView bean to be created, with or without param. I have tried this way <p:button value="prueba" outcome="pretty:app-view" /> which works but limits me to do nothing more than outcome and also <p:commandButton value="prueba2" action="pretty:app-view" ajax="false" />, which is the equivalent to call an action method and return the navigation case (that's what really I want).
First choice creates the bean properly and loads the value from the session. Second case, is giving me this error:
HTTP 500 - PrettyFaces: Exception occurred while building URL
for MappingId < app-view >, Required value < #{appId} > was null
So my target bean is not getting constructed. I have tried adding the parameter manually to the navigation case: return pretty:app-view?appId=1 and it works, but I want the target bean to recover it from the session itself. Do I have to call a redirect or something like that in my action method?
Pool your ideas.
So you are actually running into one of the "edge" effects of PrettyFaces. The "appId" parameter you have defined is actually treated both a parameter name, and also an EL bean value location for building links.
#URLMapping(parentId = "app-list", id = "app-view", pattern = "/detail/#{appId}",
viewId = "/system/manage_app/content/app_detail/app_detail.xhtml")
When you use the postback-action navigation functionality of PrettyFaces, it requires an EL bean name. Now, it just so happens that since you have not provided one in your #URLMapping annotation, that PrettyFaces is going to try to use the parameter name anyway, and hope that it can find what you want. In this case, obviously, there is no bean value called #{appId}.
You're really mixing two types of functionality that attempt to solve the same problem. Your <f:metadata> and <f:viewParam> definitions are doing the same thing that PrettyFaces does with its path-parameters and action methods. You should use one or the other mechanism. If you really want to mix them, then as you said, you should be invoking actual navigation from your <h:commandButton>, like so:
<p:commandButton value="prueba2" action="#{navegableAppView.goToAppId}" ajax="false" />
Then, you'll need to make sure you return a valid JSF2 navigation string WITH the appId parameter, such as:
public String goToAppId() {
return "/system/manage_app/content/app_detail/app_detail.xhtml?faces-redirect=true&appId=" + appId";
}
PrettyFaces will then understand that you are redirecting to a URL that has been mapped, and will perform outbound rewriting on the URL, and send you to the proper, /detail/#{addId} instead. This is all in the docs.
The final option would simply to remove the <f:metadata> and <f:viewParam>, and use the built-in PrettyFaces functionality for managing view parameters instead. Simply removing the metadata and updating your #URLMapping to this, would solve your problem:
#ManagedBean
#ViewScoped
#URLMapping(parentId = "app-list", id = "app-view", pattern = "/detail/#{appId : navegableAppView._ParamApp}",
viewId = "/system/manage_app/content/app_detail/app_detail.xhtml")
public class NavegableAppView extends SystemNavegable {
#URLAction
public String initialize() {
if ( appId != null ) {
this.item = appsDB.findById(appId);
return null;
}
// Add a message here, "The item {..} could not be found."
return "pretty:app-list";
}
This is how you initialize pages using PrettyFaces. When it comes down to it, if you are using pretty:mappingId navigation, you should only use PrettyFaces features. If you are going to use normal JSF2-style navigation where you specify the view-id and all the parameters of the URL, then you can mix-and match (as long as you are using named path-parameters in your URL mappings.
I hope this helps.
Related
Is there a way to execute a JSF managed bean action when a page is loaded?
If that's relevant, I'm currently using JSF 1.2.
JSF 1.0 / 1.1
Just put the desired logic in the constructor of the request scoped bean associated with the JSF page.
public Bean() {
// Do your stuff here.
}
JSF 1.2 / 2.x
Use #PostConstruct annotated method on a request or view scoped bean. It will be executed after construction and initialization/setting of all managed properties and injected dependencies.
#PostConstruct
public void init() {
// Do your stuff here.
}
This is strongly recommended over constructor in case you're using a bean management framework which uses proxies, such as CDI, because the constructor may not be called at the times you'd expect it.
JSF 2.0 / 2.1
Alternatively, use <f:event type="preRenderView"> in case you intend to initialize based on <f:viewParam> too, or when the bean is put in a broader scope than the view scope (which in turn indicates a design problem, but that aside). Otherwise, a #PostConstruct is perfectly fine too.
<f:metadata>
<f:viewParam name="foo" value="#{bean.foo}" />
<f:event type="preRenderView" listener="#{bean.onload}" />
</f:metadata>
public void onload() {
// Do your stuff here.
}
JSF 2.2+
Alternatively, use <f:viewAction> in case you intend to initialize based on <f:viewParam> too, or when the bean is put in a broader scope than the view scope (which in turn indicates a design problem, but that aside). Otherwise, a #PostConstruct is perfectly fine too.
<f:metadata>
<f:viewParam name="foo" value="#{bean.foo}" />
<f:viewAction action="#{bean.onload}" />
</f:metadata>
public void onload() {
// Do your stuff here.
}
Note that this can return a String navigation case if necessary. It will be interpreted as a redirect (so you do not need a ?faces-redirect=true here).
public String onload() {
// Do your stuff here.
// ...
return "some.xhtml";
}
See also:
How do I process GET query string URL parameters in backing bean on page load?
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
How to invoke a JSF managed bean on a HTML DOM event using native JavaScript? - in case you're actually interested in executing a bean action method during HTML DOM load event, not during page load.
Another easy way is to use fire the method before the view is rendered. This is better than postConstruct because for sessionScope, postConstruct will fire only once every session. This will fire every time the page is loaded. This is ofcourse only for JSF 2.0 and not for JSF 1.2.
This is how to do it -
<html xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:event type="preRenderView" listener="#{myController.onPageLoad}"/>
</f:metadata>
</html>
And in the myController.java
public void onPageLoad(){
// Do something
}
EDIT - Though this is not a solution for the question on this page, I add this just for people using higher versions of JSF.
JSF 2.2 has a new feature which performs this task using viewAction.
<f:metadata>
<f:viewAction action="#{myController.onPageLoad}" />
</f:metadata>
#PostConstruct is run ONCE in first when Bean Created.
the solution is create a Unused property and Do your Action in Getter method of this property
and add this property to your .xhtml file like this :
<h:inputHidden value="#{loginBean.loginStatus}"/>
and in your bean code:
public void setLoginStatus(String loginStatus) {
this.loginStatus = loginStatus;
}
public String getLoginStatus() {
// Do your stuff here.
return loginStatus;
}
Calling bean action from a <p:remoteCommand> will be a good idea, keep attribute autoRun="true"
Example below
<p:remoteCommand autoRun="true" name="myRemoteCommand" action="#{bean.action}" partialSubmit="true" update=":form" />
I have written a piece of code to put two pairs of radio button in a *.xhtml page of a JSF project built in JSF2.0, Jboss As 7.1.1 final and JDK 6.0, PrimeFaces 3.4.2.
<h:panelGrid columns="2">
<h:selectOneRadio value="#{calendarBean.radioFirst}">
<f:selectItem id="morning" itemValue="morning" itemLabel="Morning"/>
<f:selectItem id="afternoon" itemValue="afternoon" itemLabel="Afternoon"/>
</h:selectOneRadio>
<h:selectOneRadio style="padding-left: 170px" value="#{calendarBean.radioSecond}">
<f:selectItem id="noon" itemValue="noon" itemLabel="Noon"/>
<f:selectItem id="endofday" itemValue="endofday" itemLabel="End Of Day"/>
</h:selectOneRadio>
</h:panelGrid>
The Bean is:
private String radioFirst="morning";
private String radioSecond="endofday";
public String getRadioFirst() {
return radioFirst;
}
public void setRadioFirst(String radioFirst) {
this.radioFirst = radioFirst;
}
public String getRadioSecond() {
return radioSecond;
}
public void setRadioSecond(String radioSecond) {
this.radioSecond = radioSecond;
}
I have written a method to invoke on a Action Event where I want to have the values of the radio button selected. when the method invokes I get the value of the radio buttons as "morning" and "endofday" as these two are the initial values of the bean variables.
private String radioFirst="morning";
private String radioSecond="endofday";
But if I change the radio button options, I don't get the updated values, its the same old initial values. How can I get the changed values? Please help!
Three things
Don't use the session scope for this usage scenario. This will go completely wrong when your users open the same page in more than one tab. Use request scope or view scope.
Use a regular action instead of an action listener, and don't provide the radio button values as attributes. This makes no sense. The selectOneRadio components will set the updated values on your backing bean. When your action method is invoked, you will have access to those values via the instance fields of your bean.
Leave out the window.reload. If you need a full page refresh after the action, set ajax to false on the p:commandButton or use an h:commandButton.
You should modify your action button like this :
<p:commandButton actionListener="#{userLeaveBean.addAppliedLeave}" styleClass="apply_button" value="Create Leave">
<f:attribute name="userId" value="#{employee.name}"/>
</p:commandButton>
As #MikeBraun suggested, remove the window.reload(). If you want to refresh something, you could do it by using the update attribute from the p:commandButton.
And modify your bean according to this :
#ManagedBean
#SessionScoped // Could probably be moved to #RequestScoped or #ViewScoped
public class UserLeaveBean
{
#ManagedProperty(value="#{calendarBean}")
CalendarBean calendarBean;
public void setCalendarBean(CalendarBean calendarBean)
{
this.calendarBean = calendarBean;
}
public void addAppliedLeave(ActionEvent event)
{
System.out.println("Radio 1 : " + calendarBean.getRadioFirst());
System.out.println("Radio 2 : " + calendarBean.getRadioSecond());
}
}
The ManagedProperty will get the other bean accessible for you by injecting it inside the current one. Use the proper scope, probably you are using SessionScoped because you are using window.reload().
Important : the ManagedProperty bean must be at least scoped the same length as the other bean.
Read more :
Injecting Managed Beans in JSF 2.0
PrimeFaces Showcase - commandButton
I want to invoke one method through a link from Facelets:
My Facelets code is like:
<h:commandButton value="A" actionListener="#{customerData.searchedCustomerListA}" />
<h:commandLink value="A" actionListener="#{customerData.searchedCustomerListA}"/>
Backing bean code is like:
public void searchedCustomerListA(ActionEvent ae){
customerName = "A";
leftCustomerListAvailable.clear();
if(customerDataBean.getSearchedCustomerList(customerName)!= null)
leftCustomerListAvailable =customerDataBean.getSearchedCustomerList("A");
}
The same code is working for <h:commandButton> but not working for <h:commandLink>. How is this caused and how can I solve it?
The technical difference between <h:commandLink> and <h:commandButton> is that the link uses JavaScript to submit the parent form. So if it doesn't work while a syntactically equivalent button works fine, then that can only mean that either JavaScript is disabled in browser, or that the jsf.js file containing the mandatory helper functions isn't included in the page (which you should easily have noticed by seeing JS errors in the JS console of browser's builtin developer toolset).
So, to fix this problem, you need to verify if JS is enabled in browser and that you've a <h:head> component instead of plain HTML <head> in the template, so that JSF will be able to auto-include the jsf.js file.
Or, if your application's business requirements requires that the application functions as designed with JS disabled, then you should stick to <h:commandButton> and throw in some CSS to make it to look like a link (e.g. remove background, padding, border, inset, etc).
Try this, This sould work.
<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:p="http://primefaces.org/ui">
<h:form>
<h:commandLink type="button" action="#{testBean.tsetLink}">
<h:outputText value="A" />
</h:commandLink>
</h:form>
</html>
ManagedBean
#ManagedBean
#RequestScoped
public class TestBean {
public void tsetLink(){
System.out.println("Link clicked!!!!!!!!!!!!");
}
}
I my case the cause of this issue was a poorly configured url rewriting filter. One of the filters patterns unintentionally matched http://localhost:8080/mysite/javax.faces.resource/jsf.js.xhtml?ln=javax.faces which prevented jsf.js from being loaded. Check this answer: Clicking h:commandLink causes Uncaught ReferenceError: mojarra is not defined.
I'm passing a parameter p1 to another page page.xhtml:
<ui:include src="page.xhtml">
<ui:param name="p1" value="#{someObject}"/>
</ui:include>
Is this possible to evaluate #{p1} inside #PostConstruct method of the backing bean of page.xhtml? Using the following piece of code, #{p1} cannot resolve:
FacesContext currentInstance = FacesContext.getCurrentInstance();
currentInstance.getApplication().evaluateExpressionGet(currentInstance, "#{p1}", String.class);
Why do I need this?
I'm using an xhtml file (say component.xhtml) as a custom UI component. This file has a backing bean from which I should get component data. Since I'm including this xhtml file twice or more in my main JSF page, I want to pass different objects to each of component.xhtml so that my component work with my custom data each time included.
In Mojarra, you can get it as an attribute of the FaceletContext. You can get it in the #PostConstruct of a managed bean which is guaranteed to be referenced/constructed for the first time in the included page (and thus not in the parent page before the <ui:param> is declared in the component tree).
FaceletContext faceletContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
Object p1 = faceletContext.getAttribute("p1");
In MyFaces, the whole FaceletContext isn't available in managed beans as it's discarded by end of view build time and this construct would then not work. To be JSF implementation independent, you might want to consider to set it via <c:set scope="request"> instead. It's then available as a request attribute.
As to the concrete functional requirement, consider creating a comoposite component with a backing component. For some examples, see our composite component wiki page and this blog about using multiple input components in a composite component. See also When to use <ui:include>, tag files, composite components and/or custom components?
The param is not available in the #PostConstruct method; you can use the preRenderComponent event to initialize the parameters inside your backing bean; just put it after the ui:composition of the included page, it will be executed before the rendering of the included page itself.
Following the OP example of a passing a p1 parameter to a page.xhtml template
the main page:
<ui:include src="page.xhtml">
<ui:param name="p1" value="#{someObject}"/>
</ui:include>
page.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
...>
<ui:composition>
<f:event listener="#{backingBean.init(p1)}" type="preRenderComponent"/>
...
</ui:composition>
</html>
BackingBean.java:
#ViewScoped
public class BackingBean{
private Object p1;
public void init(Object value){
this.p1=p1;
}
...
}
the event is fired before the render of the ui:composition tag, that is before the render of page.xhtml
This works for me:
<ui:include src="page.xhtml">
<ui:param name="p1" value="#{someObject}"/>
</ui:include>
page.xhtml:
<c:set var="data" value="#{p1}" scope="request"/>
Your bean:
#ViewScoped
public class ManagedBean{
private Object someObject;
public Object getSomeObject(){
if(someObject== null){
HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
someObject= request.getAttribute("data");
}
return someObject;
}
public void setSomeObject(Object someObject){
this.someObject = someObject;
}}
I'm new to JSF and I want to spend a text when an action is executed successfully. But the output text, it is not when the action is executed successfully.
Here is the view (deactivatePerson.xhtml):
<h:outputText value="#{msg.personIsDeactivate}" rendered="#{isPersonDeactivate}" />
<h:form>
<h:commandButton action="#{controller.deactivate}" value="#{msg.deactivate}" />
</h:form>
Here is the managed bean:
#ManagedBean
#SessionScoped
public class Controller {
private boolean isPersonDeactivate = false;
public String deactivate() {
isPersonDeactivate = false; // Deactivate process...
isPersonDeactivate = true;
return "persondeactivate";
}
//Getter and Setter
}
Here is the faces-config.xml:
<navigation-rule>
<navigation-case>
<from-outcome>persondeactivate</from-outcome>
<to-view-id>/deactivatePerson.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Can someone please tell me what I do wrong here?
You forgot to reference it as a property of the #{controller} managed bean. It's unclear what your getter look like, but boolean properties should have a getter prefixed with is instead of get. The property name itself should preferably not have an is prefix. It should rather be a verb statement.
Thus, more so:
private boolean personDeactivated;
public boolean isPersonDeactivated() {
return personDeactivated;
}
Then you can reference it as follows:
<h:outputText ... rendered="#{controller.personDeactivated}" />
Unrelated to the concrete problem, navigation cases are superfluous since the new JSF 2.0 implicit navigation. Just let your action method return "deactivatePerson" and it'll go to the proper view without needing a <navigation-case>. Make sure that you're reading proper JSF 2.x targeted resources and not JSF 1.x targeted ones.