I've the following xhtml page, that is wrapped in the major part of the other pages in my project:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title></title>
</h:head>
<h:form>
<p:menubar>
<p:menuitem value="Home" url="/protected/personal/HomeCalendar.xhtml" icon="ui-icon-home"/>
<p:menuitem value="#{topbarBean.username}" url="#" style="text-decoration: underline" />
<f:facet name="options">
<p:inputText style="margin-right:20px" placeholder="Search" value="#{searchBean.searched}"/>
<p:commandButton action="#{searchBean.search()}" type="submit" icon="ui-icon-search" />
</f:facet>
</p:menubar>
</h:form>
</ui:composition>
This is the navigation rule that I've wrote:
<navigation-rule>
<from-view-id>/Components/TopBar.xhtml</from-view-id>
<navigation-case>
<from-action>#{searchBean.search()}</from-action>
<from-outcome>searchingResults</from-outcome>
<to-view-id>/protected/SearchResults.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
and this the referred Bean:
#RequestScoped
#ManagedBean
public class SearchBean implements Serializable {
private String searched;
private final String resultsOutcome = "searchingResults";
private List<User> users;
private List<Event> events;
#EJB
UserFacade uf;
#EJB
UserManager um;
#EJB
EventFacade ev;
#PostConstruct
public void init(){
try {
setEvents(um.getEventsVisibilityMasked(um.getLoggedUser().getId()));
}
catch (NotFoundException ex) {
Logger.getLogger(SearchBean.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void setSearched(String searched) {
this.searched = searched;
}
public String getSearched() {
return searched;
}
public void search() {
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, resultsOutcome);
}
public List<User> getUsers(){
return users;
}
public void setUsers(List<User> users){
for(User user:users)
this.users.add(user);
}
public List<Event> getEvents(){
return events;
}
public void setEvents(List<Event> events){
for(Event event:events)
this.events.add(event);
}
}
The error is the following one:
JSF1064: Unable to find or serve resource, /protected/personal/searchingResults.xhtml.
This path is not specified anywhere, if it could be helpful I've the following structure :
-Index,xhtml
-Web Pages { components , protected}
-components{TopBar.xhtml}
-protected {event,persona,user,SearchResults.xhtml}
-event{eventCreate,eventPage,eventEdit}
-personal{HomeCalendar,ManageSettings,ManageInvitations}
I don't understand if the problem regards the navigation-rule or the next xhtml page.
That can happen if JSF can't find the matching navigation rule. It'll then switch to implicit navigation. I.e. the outcome will be used as the actual view ID, relative to the current context.
Apparently the current view ID is sitting somewhere in /protected/personal. An outcome of searchingResults which doesn't match any navigation rule will then trigger an implicit navigation to /protected/personal/searchingResults.xhtml.
You've 2 options:
Fix the current view ID. The <from-view-id>/Components/TopBar.xhtml</from-view-id> is apparently wrong. You can find out the right one as follows:
System.out.println(FacesContext.getCurrentInstance().getViewRoot().getViewId());
It's usually the one matching the context-relative URI in browser's address bar as long as you don't use POST for page-to-page navigation. Use this value in <from-view-id>.
Get rid of navigation cases altogether and rely on implicit navigation. Remove the <navigation-case> altogether from faces-config.xml and change the outcome value and action method as below:
private final String resultsOutcome = "/protected/SearchResults.xhtml";
public String search() {
return resultsOutcome;
}
The NavigationHandler approach was also quite clumsy. Even with navigation cases, just return the outcome outright instead of fiddling with NavigationHandler.
See also:
Communication in JSF 2 - Implicit Navigation
What is the difference between redirect and navigation/forward and when to use what?
Differences between action and actionListener
Related
I have a table and in each row user can click on a link which triggers book availability check. So I have a commandLink with action and it works, but this action is executed every time user clicks on a link. I want it to be available only once. Also I don't want to hide link after click as it has onclick code which hides and shows details. Is it possible to remove action from commandlink after executing action?
The answer covered in Is it possible to use EL conditional operator in action attribute? is one of the ways that you can solve this. With that being said, since the release of JSF 2.2, there are also other alternatives. While removing the action attribute in JSF is problematic (it can be done with some trickery) - another solution is to use actionListeners together with an f:event binding that is connected to the preValidate event. This allows you to remove any of the connected actionListeners whenever you choose to do so.
Here is a complete solution with an event listener that modifies the component prior to it being processed for the view. Basically, you can do something like this;
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Dynamic action demo</title>
</h:head>
<h:body>
<h:form>
<h:dataTable var="row" value="#{removeActionBackingBean.rows}">
<h:column>#{row.primaryColumn}</h:column>
<h:column>#{row.hasBeenClicked}</h:column>
<h:column>
<h:commandButton actionListener="#{removeActionBackingBean.onPressed(row)}">
<f:attribute name="clicked" value="#{row.hasBeenClicked}"/>
<f:event listener="#{removeActionBackingBean.onModify}" type="preValidate" />
<f:ajax event="click" render="#form" />
</h:commandButton>
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
For the backing bean, here is a solution with a complete model (using Lombok);
#Data
#Named
#ViewScoped
public class RemoveActionBackingBean implements Serializable {
private List<Row> rows;
#PostConstruct
private void init() {
rows = new ArrayList<>();
for (int i = 0; i < 10; i++) {
rows.add(new Row(RandomStringUtils.randomAscii(10)));
}
}
public void onPressed(Row row) {
row.hasBeenClicked = true;
System.out.println(String.format("'%s' has been pressed!", row.primaryColumn));
}
public void onModify(ComponentSystemEvent event) {
final boolean isRowClicked = (boolean) event.getComponent().getAttributes().get("clicked");
if (isRowClicked) {
for (ActionListener al : ((UICommand) event.getComponent()).getActionListeners()) {
((UICommand) event.getComponent()).removeActionListener(al);
}
}
}
#Data
#RequiredArgsConstructor
public class Row {
private #NonNull String primaryColumn;
private boolean hasBeenClicked;
}
}
The key sections to look at is f:event and the onModify() method binding. As you can see, we simply check if a certain "row" is considered as clicked - if this is the case, we clear all the actionListeners currently defined on the component. Effectively, there will be no actionEvent called when the button is pressed.
While the above solution modifies the actionListeners of a button, it can be adopted and used for other types of components and when you want to modify certain attributes of a component based on some condition - so it's extremely useful to know this trick.
I am going through an example in Anghel Leonard book Mastering Java Server Faces 2.2.
The author demonstrates with an example on how preserve data for the next request on redirection when the bean is made is #RequestScoped.
This is the code for index.xhtml-
<h:body>
<f:metadata>
<f:event type="preRenderView"
listener="#{playersBean.pullValuesFromFlashAction}"/>
</f:metadata>
<h:messages />
<h:form>
Name: <h:inputText value="#{playersBean.playerName}"/>
Surname: <h:inputText value="#{playersBean.playerSurname}"/>
<h:commandButton value="Register"
action="#{playersBean.addValuesToFlashAction()}"/>
</h:form>
</h:body>
terms.xhtml-
<h:body>
<h:messages />
Hello, <h:outputText value="#{flash.keep.playerName} #{flash.keep.playerSurname}"/>
<br/><br/>Terms & Conditions ... ... ... ... ...
<h:form>
<h:commandButton value="Reject"
action="#{playersBean.termsRejectedAction()}" />
<h:commandButton value="Accept"
action="#{playersBean.termsAcceptedAction()}" />
</h:form>
</h:body>
done.xhtml-
<h:head>
<title></title>
</h:head>
<h:body>
<f:metadata>
<f:event type="preRenderView"
listener="#{playersBean.pullValuesFromFlashAction}"/>
</f:metadata>
<h:messages />
<h:outputText value="#{playersBean.playerName} #{playersBean.playerSurname}"/>
successfully registered!
</h:body>
And the backing bean-
#ManagedBean
#RequestScoped
public class PlayersBean {
private final static Logger logger = Logger.getLogger(PlayersBean.class.getName());
private String playerName;
private String playerSurname;
public PlayersBean() {
}
public String getPlayerName() {
return playerName;
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
public String getPlayerSurname() {
return playerSurname;
}
public void setPlayerSurname(String playerSurname) {
this.playerSurname = playerSurname;
}
public String addValuesToFlashAction() {
Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
flash.put("playerName", playerName);
flash.put("playerSurname", playerSurname);
return "terms?faces-redirect=true";
}
public void pullValuesFromFlashAction(ComponentSystemEvent e) {
Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
playerName = (String) flash.get("playerName");
playerSurname = (String) flash.get("playerSurname");
}
public String termsAcceptedAction() {
Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
flash.setKeepMessages(true);
pullValuesFromFlashAction(null);
//do something with firstName, lastName
logger.log(Level.INFO, "First name: {0}", playerName);
logger.log(Level.INFO, "Last name: {0}", playerSurname);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Terms accepted and player registered!"));
return "done?faces-redirect=true";
}
public String termsRejectedAction() {
Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
flash.setKeepMessages(true);
pullValuesFromFlashAction(null);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Terms rejected! Player not registered!"));
return "index?faces-redirect=true";
}
}
I think there was no need for these 2 lines-
flash.setKeepMessages(true);
pullValuesFromFlashAction(null);
as flash.keep on terms.xhtml page serves the same purpose.
The author seems to be confused here.
Or am I completely wrong & they do definitely serves a purpose over here.
The code chosen by the author is a bit confusing, from my point of view, however, it does what it is intended to do.
flash.setKeepMessages(true);
This line tells JSF you want to keep the FacesMessage in the flash scope. That's a requirement when making a redirection, because more than one subsequent requests are involved and the messages would die from one request to another otherwise. The line is necessary.
pullValuesFromFlashAction(null);
The line does nothing special, just grabs the name and the surname from the flash scope. It's just right to call it from preRenderView to load bean data from the flash scope, but it's redundant to invoke it from the action methods (unless you want to do some logging to see parameters are properly set). Anyway, if you're starting with JSF I encourage you to use JSF 2.2 which has a parameterless replacement for the preRenderView methods, called viewAction.
Last but not least, I would recommend you going through an answer I made some time ago showing an example for dealing with the flash scope, you might find it interesting.
See also:
JSF Keep Messages docs
How to show faces message in the redirected page
We are migrating a JSF 2.1 application, from JBoss AS 7.2 to Wildfly and thus JSF 2.2. The problem We're having is the following: We have a compositecomponent that is included in a #ViewScoped bean. The component has to retain its value through multiple requests, so a Request Scoped bean is not a solution.
The exception we're getting is a multiple component id one. After the request JSF starts to render the component for the second time, and fails.
I made a simple demo for this:
MyViewBean.java
#ViewScoped
#Named
public class MyViewBean implements Serializable {
private Component component;
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
public String increment(){
component.setCounter(component.getCounter()+1);
return "";
}
}
Component.java
#FacesComponent(value = "composite")
public class Component extends UINamingContainer {
private Integer counter = 0;
public Integer getCounter() {
return counter;
}
public void setCounter(Integer counter) {
this.counter = counter;
}
}
compositeTest.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
template="/WEB-INF/templates/default.xhtml"
xmlns:pelda="http://xmlns.jcp.org/jsf/composite/component">
<ui:define name="content">
<h1>Composite component Test!</h1>
<h:form>
<pelda:composite binding="#{myViewBean.component}" />
<h:commandButton action="#{myViewBean.increment()}" value="Push me!"/>
</h:form>
</ui:define>
</ui:composition>
composite.xhtml
<cc:interface componentType="composite">
</cc:interface>
<cc:implementation>
<h:outputText id="id_hello" value="Helloka" />
<h:outputText id="id_counter" value="#{cc.counter}" />
</cc:implementation>
</html>
How to achieve that the counter can be incremented (with #RequestScoped bean it resets) and won't fail with idUniqueness error? We're using Mojarra 2.2.8 (Default in wildfly), also tried with Mojarra 2.2.12 (the latest as per writing this).
Thanks in advance!
UIComponent instances are inherently request scoped. You should never reference UIComponent instances beyond the request scope. Carefully read How does the 'binding' attribute work in JSF? When and how should it be used? for an elaborate explanation on that.
You only want to save its state in the JSF state via the inherited getStateHelper() method. This acts basically as the view scope.
#FacesComponent(value = "composite")
public class Component extends UINamingContainer {
public Integer getCounter() {
return (Integer) getStateHelper().eval("counter", 0);
}
public void setCounter(Integer counter) {
getStateHelper().put("counter", counter);
}
}
Don't forget to get rid of the binding attribute in the view.
See also:
How to save state when extending UIComponentBase
I'm trying to set up a voting app that will display whether the user is able to vote or not using an if statement in my bean class but this Unable to find matching navigation case with from-view-id '/home.xhtml' for action '#{user.checkAge(user.age)}' with outcome 'Invalid User, Please Try Again!!!'. Im not very understanding of Java Server Faces yet and ive tried messing around with the config files and googling the error but i cant fix it. Can anyone help me please.
Here is my code:
Home.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<head>
<title>Results Page</title>
</head>
<body>
<h:form>
Name: <h:outputText id="outTxt" value="#{user.name}"/><br></br>
Age: <h:outputText id="outTxt2" value="#{user.age}"/><br></br>
<h:commandButton id="cmdBtn" value="Check" action="#{user.checkAge(user.age)}"/>
</h:form>
</body>
</html>
index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Home Page</title>
</h:head>
<h:body>
<h:body>
<h:form>
Name: <h:inputText id="inTxt" value="#{user.name}"/><br></br>
Age: <h:inputText id="inTxt2" value="#{user.age}"/><br></br>
<h:commandButton id="cmdBtn" value="Check" action="home"/>
</h:form>
</h:body>
</h:body>
</html>
User.java
package MyPackage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class User
{
private String name;
private int age;
private String msg;
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String checkAge(int checkAgeVoter)
{
if(checkAgeVoter >= 18)
{
msg = "Valid User, Access Granted!!!";
}
else
{
msg = "Invalid User, Please Try Again!!!";
}
return msg;
}
}
User.checkAge() is used as JSF action. Therefore its return value is interpreded as "outcome" which is fed into JSF navigation logic. JSF tries to look up a navigation rule for the current view (/home.xhtml) and your "outcome" to find out which page to display next. It can not find any.
You might want to return null from checkAge (or make the method void) causing JSF navigation to display the current page again. However, I did not see any place where User.msg would be displayed. So you might want to navigate to another page which does that and return the proper outcome for a navigation rule. (Note: Your user inputs data in index.html so you might want to reference your input validating action there and return "/home.xhtml".) For more details on navigation in modern JSF, see Faces Navigation not really working in JSF2
Note that there is a mechanism for displaying messages in JSF based on the tags h:messages (and h:message) and FacesContext.getCurrentInstance().addMessage() (and validation). For an example, see http://www.javabeat.net/2008/04/display-error-messages-in-jsf-hmessages-part-1-2/
You might try:
In home.xhtml: After <f:form> add Msg: <h:outputText value="#{user.msg}"/><br/>.
In index.xhtml: Replace action="home" with action="#{user.checkAge(user.age)}".
In User.java: In checkAge() replace return msg; with return "home"; (or "/home.xhtml").
Assuming index.html is your starting page, this checks entered data and navigates to home page (assuming action="home" already worked).
This is not the best way to do it, but just to well inderstand how the navigation goes on by the famous configuration file faces-config :
First, you can omit the msg attribute, and modify the business method as :
public String checkAge(int checkAgeVoter) {
if (checkAgeVoter >= 18) return "granted";
else return "denied";
}
With this faces-config :
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
version="2.0">
<navigation-rule>
<from-view-id>/index</from-view-id>
<navigation-case>
<from-outcome>home</from-outcome>
<to-view-id>/home.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/home.xhtml</from-view-id>
<navigation-case>
<from-outcome>granted</from-outcome>
<to-view-id>/votepage.xhtml</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>denied</from-outcome>
<to-view-id>/errorpage.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
And adding 2 test pages: votepage.xhtml in case of success, and errorpage.xhtml else.
I have the following problem. When I click the button "Enviar", this calls another method that is associated to a selectOneMenu (in the attribute
valueChangeListener called "validarSelect"), and later, calls the method that this button has associated in the attribute actionListener called "validarBoton".
I wonder, why this happens. I expect the valueChangeListener to be not called since I have not changed the dropdown.
This is my page JSF:
<?xml version='1.0' encoding='windows-1252'?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html">
<html xmlns="http://www.w3.org/1999/xhtml">
<h:head></h:head>
<h:body>
<h:form>
<h:commandButton value="Enviar..." id="validar" actionListener="#{Domiciliacion.validarBoton}"/>
<h:selectOneMenu valueChangeListener="#{Domiciliacion.validarSelect}"
binding="#{Domiciliacion.selectCombo}">
<f:selectItems value="#{Domiciliacion.lista}"/>
<f:ajax event="valueChange" render="#this"/>
</h:selectOneMenu>
</h:form>
</h:body>
</html>
And this, is the ManagedBean:
package domiciliaciontest;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
#ManagedBean(name = "Domiciliacion")
#ViewScoped
public class MB0001 {
private HtmlSelectOneMenu selectCombo;
private List<String> lista = new ArrayList<String>();
public MB0001() {
super();
System.out.println("Entro al constructor...");
lista.add("Caracas");
lista.add("Bogota");
lista.add("Santiago");
}
public void validarBoton(ActionEvent actionEvent) {
System.out.println("Entro a validarBoton...");
// Add event code here...
}
public void validarSelect(ValueChangeEvent valueChangeEvent) {
// Add event code here...
System.out.println("Entro a validarSelect...");
}
public void setSelectCombo(HtmlSelectOneMenu selectCombo) {
this.selectCombo = selectCombo;
}
public HtmlSelectOneMenu getSelectCombo() {
return selectCombo;
}
public void setLista(List<String> lista) {
this.lista = lista;
}
public List<String> getLista() {
return lista;
}
}
this is the output when I click the button "Enviar":
Entro a validarSelect...
Entro a validarBoton...
The valueChangeListener method will be invoked when the submitted value is different from the initial value, regardless of whether you have changed it yourself or not. So, if the currently submitted value (which is "Caracas" in your case) is different from the initial value (which is null in your case), then the valueChangeListener method will be invoked.
See also:
When to use valueChangeListener or f:ajax listener?
Best way to add a "nothing selected" option to a selectOneMenu in JSF
Unrelated to the concrete problem, seeing this in combination with binding attribute gives me the impression that you're trying to achieve something which you've read in an article or answer targeted on JSF 1.x. This is namely recognizeable as part of a hack to populate child dropdowns in JSF 1.x. You do not need this approach for JSF 2.x. Further, your method names with "validar" ("validate") are misleading. Don't you actually need a fullworthy Validator? But as said, that's a different problem.
See also:
Make multiple dependent / cascading selectOneMenu dropdown lists in JSF