h:form inside ui:repeat doesn't work as expected - jsf-2.2

For a huge project, I need to build multiple forms on a web page. Can't go into details, but assume to have the requested structure of a list of forms; using mojarra jsf 2.2.5
given Managed Bean:
#ManagedBean
#SessionScoped
public class Debug {
private final List<DebugBean> list;
public Debug() {
super();
this.list = new ArrayList<DebugBean>();
this.list.add(new DebugBean("label 1", "value 1"));
this.list.add(new DebugBean("label 2", "value 2"));
}
public String submit() {
LOGGER.info("list = " + this.list.toString());
return "";
}
public List<DebugBean> getList() {
return this.list;
}
}
The bean DebugBean is simple, just contains two variables label and value, both String, and its setter and getter.
Now the xhtml page:
<h:body style="height: 100%">
<ui:repeat value="#{debug.list}" var="x">
<h:form>
<h:panelGrid columns="2">
<h:outputText value="#{x.label}" />
<h:inputTextarea value="#{x.value}" />
</h:panelGrid>
<h:commandButton action="#{debug.submit}" value="ok" />
</h:form>
</ui:repeat>
</h:body>
The problem is in changing the first value. When I change the second one, the logger gives me the new value as expected. But changing the first one, the logger gives me the old list, as if I would have reloaded the complete form without any changes. What's the problem here, and more important: what can I do to make it work?

The solution is simple, but worse. I don't know why, but ui:repeat does not behave as h:dataTable does. So replacing ui:repeat by h:dataTable solves the problem, but means page layout rework (which is a bigger problem, but can be done by me).

Related

Primefaces AJAX Cannot render nested UIComponent AND actionListener is not working

EDIT 2: Why it is different
#BalusC, this is different from the post you say because I'm asking about an actionListener weird behavior that's not executing a method.
Also, In my test I'm following the Primefaces's documentation, applying fixed id attribute to every NamingContainer and checking the generated HTML (and has the expected id's)
The post you say it's a duplicate from this, was a good help but here we're talking about another problems.
I was working in a webapp's layout and found a problem updating the DOM. I'm working with Primefaces 5.2 and Glassfish 4.1. To isolate the problem I tried to test it in other project but I found another issue in my test. Maybe I'm missing something, so I need to ask in this forum.
I'll go backwards: I'll describe the test and then the real problem so you can see it has the same structure
THE FAILING TEST
index.xhtml
<h:body>
<h:form id="form-render">
<p:commandButton id="btnRender" value="Render" update=":form-rendered"
actionListener="#{renderView.setRender(true)}"/>
</h:form>
<h:form id="form-rendered">
<h:outputText value="Rendered in form-rendered" rendered="#{renderView.render}"/>
<p:panelGrid id="panel-grid" rendered="#{renderView.render}">
<p:outputLabel value="Rendered in form-rendered:panel-grid"/>
</p:panelGrid>
</h:form>
</h:body>
renderView.java
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class RenderView implements Serializable {
private static final long serialVersionUID = -1687524798440117276L;
private boolean render;
public RenderView() {
this.render = false;
System.out.println("Constructor - render=" + this.render);
}
public boolean isRender() {
return render;
}
public void setRender(boolean render) {
this.render = render;
System.out.println("Setter - render=" + this.render);
}
}
But when I run the test only renders the <h:outputText> component. I tried another options:
update=":form-rendered, :form-rendered:panel-grid": same result
update=":form-rendered:panel-grid": doesn't render anything
move rendered=#{...} attribute from <p:panelGrid> to <p:outputLabel> inside it: doesn't change
I don't know how to reference the <p:panelGrid> inside the <h:form>. According to Primefaces's documentation:
Update attribute takes a comma or white-space separated list of JSF component ids to be updated
So the first option should work, but it doesn't. What am I missing here?
THE REAL PROBLEM
The previous test came from a problem I was facing working with a layout. The layout's idea is the following:
In several pages I need to search something and show the results under the search form (with AJAX, of course).
Every page in the layout is a facelet template client (made with <ui:composition>)
This is ONLY the layout, there are not any queries, business logic or anything like that. The #ManagedBean classes I use are just creating dummy objects to populate the Primefaces's components
So I used the same structure of the previous test to solve the problem in every page. The funny thing is that I coded the first one and worked perfectly, but when I coded the second, fails on rendering the second form. Even more, I coded the third and worked perfectly, coded the fourth and fails!!!.
In my first solution attempt I used the following bean:
#ManagedBean // from javax.faces.bean
#ViewScoped // idem
public class SearchView {
private boolean showResults;
public SearchView() {
this.showResults = false;
System.out.println("Constructor-showResults=" + this.showResults);
}
public boolean isShowResults() {
return showResults;
}
public void setShowResults(boolean showResults) {
this.showResults = showResults;
System.out.println("Setter-showResults=" + this.showResults);
}
}
I'll copy-paste from a working example first and then form a failing example, so maybe you can see something I'm missing.
searchPage1.xhtml (working)
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="./../templateMenu.xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<ui:define name="left">
<ui:include src="include/auMenu.xhtml"/>
</ui:define>
<ui:define name="content">
<h1>Search 1</h1>
<h:form id="form-busq-au">
<p:panelGrid columns="2" styleClass="ui-noborder">
<!--form fields -->
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update=":form-res-au"/>
</p:panelGrid>
</h:form>
<h:form id="form-res-au">
<c:if test="#{searchView.showResults}">
<h1>Search Results</h1>
</c:if>
<p:panelGrid id="panelgrid-res-au" columns="2" rendered="#{searchView.showResults}">
<!-- other fields -->
</p:panelGrid>
</h:form>
</ui:define>
</ui:composition>
searchPage2.xhtml (not working)
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="./../templateMenu.xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<ui:define name="left">
<ui:include src="include/menu.xhtml"/>
</ui:define>
<ui:define name="content">
<h:form id="form-busq-mt">
<h1>Search 2</h1>
<p:panelGrid columns="4" styleClass="ui-noborder">
<!-- form fields -->
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update=":form-res-mt"/>
</p:panelGrid>
</h:form>
<h:form id="form-res-mt">
<c:if test="#{searchView.showResults}">
<h1>Search Results</h1>
</c:if>
<p:panelGrid columns="2" rendered="#{searchView.showResults}">
<!-- Other fields -->
</p:panelGrid>
</h:form>
</ui:define>
</ui:composition>
As you can see, it's the same structure in the test and the two pages, but the behavior is different. I don't know if this is a bug in primefaces or what.
Debugging the code I realized that in the second page, the setSearchResult(true) method is never called, don't know why. So I tried the following solution:
Failed solution attempt
Thinking that it was a problem with the SearchView Managed Bean, I made a change in the Controller: I made an abstract class with the attribute and methods and a #ManagedBean for each one of the web pages extending the abstract class. In code:
public abstract class SearchView {
private boolean showResults;
public SearchView() {
this.showResults = false;
System.out.println("Constructor-showResults=" + this.showResults);
System.out.println("Class: " + this.getClass().toString());
System.out.println("ID: " + this.getClass().hashCode());
}
public boolean isShowResults() {
return showResults;
}
public void setShowResults(boolean showResults) {
this.showResults = showResults;
System.out.println("Setter-showResults=" + this.showResults);
}
}
And for every search page:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class ConcreteXXXSearchView extends SearchView implements Serializable {
private static final long serialVersionUID = -4717041016001640528L;
}
but the result is the same (setShowResult(true) is never called in the second page). The println() methods in the abstract class SearchView was to test that I'm instantiating different objects (indeed, there are different).
I tried the test's options but the problem is in the <p:commandButton> because actionListener is not working.
Summarizing
I'm facing two problems here:
In the test I need to render a <h:form> containing a UIComponent that implements NamingContainer(<p:panelGrid>)
In the project I need to know why the setShowResults(true) is not called in the failing page
Any help / guide is appreciated. Thanks in advance
EDIT: more info
In both TEST and REAL PROBLEM I checked the DOM with the browser inspector and the id attribute is correct for both <h:form id="form-res-mt"> and <p:panelGrid id="panelgrid-res-mt">
You have used actionListener="#{searchView.setShowResults()}" in your non-working approach.
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults()}"
update="form-res-mt"/>
But, searchView.setShowResults() method expects a boolean argument, change your code to:
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update="form-res-mt"/>
Also, use <ui:fragment rendered="yourCondition"> instead of <c:if test="condition"> because if component condition returns false ,it'll not attached to the component tree. And thus can't find the component in the UI Tree.

Richfaces Datatable loses submitted value of inputs on validation fail

I have two inputs - one inside a "normal" h:dataTable and one inside a rich:dataTable When I submit a wrong value, i.e. validation fails, the "normal" one keeps the value I submitted while the second one loses it. See the following code snippets (enter any value an press the button):
ManagedBean
#ManagedBean
#ViewScoped
public class TestController implements Serializable {
private static final long serialVersionUID = -484022507596298941L;
private String[] stringArray1 = {"Element 1", "Element 2"}; // + Getter
private String[] stringArray2 = {"Element A", "Element B"}; // + Getter
private Map<String, String> inputValues = new HashMap<String, String>(4); // + Getter
public TestController() {
inputValues.put(stringArray1[0], "");
inputValues.put(stringArray1[1], "");
inputValues.put(stringArray2[0], "");
inputValues.put(stringArray2[1], "");
}
public void doSomething() {
System.out.println("Did something");
}
public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) {
throw new ValidatorException(new FacesMessage("This can never be valid."));
}
}
View
<h:form>
<h1>h:dataTable</h1>
<h:dataTable id="table1" value="#{testController.stringArray1}" var="string" columnClasses="inactive">
<h:column>
<h:outputText value="#{string}:"/>
<h:inputText id="someInput" value="#{testController.inputValues[string]}" validator="#{testController.validate}"/>
<h:message for="someInput" id="msg" style="color: red;"/>
</h:column>
</h:dataTable>
<h1>rich:dataTable</h1>
<rich:dataTable id="table2" value="#{testController.stringArray2}" var="string">
<rich:column>
<h:outputText value="#{string}:"/>
<h:inputText id="someInput" value="#{testController.inputValues[string]}" validator="#{testController.validate}"/>
<h:message for="someInput" id="msg" style="color: red;"/>
</rich:column>
</rich:dataTable>
<h:commandButton id="button" action="#{testController.doSomething}" value="do something"/>
</h:form>
Is this known Richfaces behaviour or a bug of some kind? Is there a way to make it behave the same way the normal JSF-DataTable does? Using h:dataTable instead is not always an option and losing your "I-was-just-about-to-correct-it" input is rather annoying.
ADDITION:
I just checked the behaviour of ui:repeat and a4j:repeat and it's just the same: ui:repeat keeps the submitted value while a4j:repeat does not.
UPDATE: Re-worked example code to rule out some possible problems as mentioned in comments (input fields now point to different values; only one form element).
Tested on Mojarra 2.1.21 with RichFaces 4.3.7 and JBoss AS 7 plus on Mojarra 2.2.7 with RichFaces 4.5.0 Alpha3 and JBoss Wildlfy - same result.
I just tried each form of your page separately with Richefaces 4.3.7 and Mojarra 2.2.6 and it's perfectly working! i didn't noticed any abnormal behavior, when validation fails i didn't lost any values. That means that there is no Validation issues with the Richfaces components.
However, When using the two forms in a single page, i can notice that when i submit the first form the inputText of the second form loses it's value, while if we submit the form2 the inputText of the first form don't lost it's value, in my guess its because JSF stored the state of it's HTML components in the javax.faces.ViewState and doesn't do the same for Richfaces components, using Firebug you can easily verify that the only common request parameter between those two POST requests is the javax.faces.ViewState.

Copying value of a textbox to a nother using ajax in jsf

I am very new to ajax and trying to copy the value of one box to another. Here is my code:
<h:form>
<h:inputText value="#{ajaxBean.name}">
<f:ajax render="otherbox" execute="#this" event="keyup"></f:ajax>
</h:inputText>
<h:inputText id="otherbox" value="#{ajaxBean.name}"></h:inputText>
</h:form>
And the bean
#Named(value = "ajaxBean")
#Dependent
public class AjaxBean {
public AjaxBean() {
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
That code does not work. Can anyone help me?
Thanks
Your question is not about Ajax or JSF. It is a JavaScript question.
You can access and modify items with JavaScript.
Add this JavaScript codes between <h:head></h:head>
<script>
function copyField()
{
document.getElementById("field2").value = document.getElementById("field1").value;
}
</script>
And your page:
<h:form id="myform" prependId="false">
<h:inputText id="field1" value="#{myBean.name}" onkeyup="copyField();" />
<h:inputText id="field2" value="#{myBean.name}"></h:inputText>
</h:form>
Take attention to prependId="false" to avoid mixing ids.
See also:
JSF: Why prependId = false in a form?
I think you are mixing up JSF and CDI. #Dependent says that the bean is in the dependent pseudo-scope (which is anyways the default-scope for CDI-beans), so every time you make a request the bean will be reinstanciated and the bean can not hold any state. Look here for an explanation of the scopes and especially for what the dependent scope is used for.
So first of all you have to use some different scope, #RequestScoped should be enough for your task. And as I do not see any use of CDI here, use #ManagedBean instead of #Named - so the default scope for the bean will be the request scope.
Try this:
#ManagedBean
public class AjaxBean {
...
}

Dynamic ajax navigation with <ui:include>

Suppose I want to navigate in my application, and include different facelet pages dynamically. I have a commandLink like this:
<h:commandLink value="Link" action="#{navigation.goTo('someTest')}">
<f:ajax render=":content" />
</h:commandLink>
And this is where I include the facelet:
<h:form id="content">
<ui:include src="#{navigation.includePath}" />
</h:form>
The Navigation class:
public class Navigation {
private String viewName;
public void goTo(String viewName) {
this.viewName = viewName;
}
public String getIncludePath() {
return resolvePath(viewName);
}
}
I have seen similar examples, but this doesn't work of course. As ui:include is a taghandler, the include happens long before my navigation listener is invoked. The old facelet is included, instead of the new. So far I get it.
Now to the headache part: How can I dynamically include a facelet, based on an actionListener? I tried to include the facelet in a preRender event, and a phaseListener before RENDER_RESPONSE. Both work, but in the event listener I can't include a facelet which contains an other preRender event, and in the phaseListener I get duplicate Id's after some clicks in the included facelet. However, inspecting the component tree tells me, there are no duplicate components at all. Maybe these two ideas were not to good at all..
I need a solution, where the page with the ui:include, or the Java class which includes the facelet, doesn't have to know the pages, which will be included, nor the exact path. Did anybody solve this problem before? How can I do it?
I am using JSF 2.1 and Mojarra 2.1.15
All you need to reproduce the Problem is this bean:
#Named
public class Some implements Serializable {
private static final long serialVersionUID = 1L;
private final List<String> values = new ArrayList<String>();
public Some() {
values.add("test");
}
public void setInclude(String include) {
}
public List<String> getValues() {
return values;
}
}
This in your index file:
<h:head>
<h:outputScript library="javax.faces" name="jsf.js" />
</h:head>
<h:body>
<h:form id="topform">
<h:panelGroup id="container">
<my:include src="/test.xhtml" />
</h:panelGroup>
</h:form>
</h:body>
And this in text.xhtml
<ui:repeat value="#{some.values}" var="val">
<h:commandLink value="#{val}" action="#{some.setInclude(val)}">
<f:ajax render=":topform:container" />
</h:commandLink>
</ui:repeat>
That's enough to produce an error like this:
javax.faces.FacesException: Cannot add the same component twice: topform:j_id-549384541_7e08d92c
For OmniFaces, I've also ever experimented with this by creating an <o:include> as UIComponent instead of a TagHandler which does a FaceletContext#includeFacelet() in the encodeChildren() method. This way the right included facelet is remembered during restore view phase and the included component tree only changes during render response phase, which is exactly what we want to achieve this construct.
Here's a basic kickoff example:
#FacesComponent("com.example.Include")
public class Include extends UIComponentBase {
#Override
public String getFamily() {
return "com.example.Include";
}
#Override
public boolean getRendersChildren() {
return true;
}
#Override
public void encodeChildren(FacesContext context) throws IOException {
getChildren().clear();
((FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY)).includeFacelet(this, getSrc());
super.encodeChildren(context);
}
public String getSrc() {
return (String) getStateHelper().eval("src");
}
public void setSrc(String src) {
getStateHelper().put("src", src);
}
}
Which is registered in .taglib.xml as follows:
<tag>
<tag-name>include</tag-name>
<component>
<component-type>com.example.Include</component-type>
</component>
<attribute>
<name>src</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
</tag>
This works fine with the following view:
<h:outputScript name="fixViewState.js" />
<h:form>
<ui:repeat value="#{includeBean.includes}" var="include">
<h:commandButton value="Include #{include}" action="#{includeBean.setInclude(include)}">
<f:ajax render=":include" />
</h:commandButton>
</ui:repeat>
</h:form>
<h:panelGroup id="include">
<my:include src="#{includeBean.include}.xhtml" />
</h:panelGroup>
And the following backing bean:
#ManagedBean
#ViewScoped
public class IncludeBean implements Serializable {
private List<String> includes = Arrays.asList("include1", "include2", "include3");
private String include = includes.get(0);
private List<String> getIncludes() {
return includes;
}
public void setInclude(String include) {
return this.include = include;
}
public String getInclude() {
return include;
}
}
(this example expects include files include1.xhtml, include2.xhtml and include3.xhtml in the same base folder as the main file)
The fixViewState.js can be found in this answer: h:commandButton/h:commandLink does not work on first click, works only on second click. This script is mandatory in order to fix JSF issue 790 whereby the view state get lost when there are multiple ajax forms which update each other's parent.
Also note that this way each include file can have its own <h:form> when necessary, so you don't necessarily need to put it around the include.
This approach works fine in Mojarra, even with postback requests coming from forms inside the include, however it fails hard in MyFaces with the following exception during initial request already:
java.lang.NullPointerException
at org.apache.myfaces.view.facelets.impl.FaceletCompositionContextImpl.generateUniqueId(FaceletCompositionContextImpl.java:910)
at org.apache.myfaces.view.facelets.impl.DefaultFaceletContext.generateUniqueId(DefaultFaceletContext.java:321)
at org.apache.myfaces.view.facelets.compiler.UIInstructionHandler.apply(UIInstructionHandler.java:87)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:49)
at org.apache.myfaces.view.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:158)
at org.apache.myfaces.view.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:57)
at org.apache.myfaces.view.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:48)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:394)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:448)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:426)
at org.apache.myfaces.view.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:244)
at com.example.Include.encodeChildren(Include.java:54)
MyFaces basically releases the Facelet context during end of view build time, making it unavailable during view render time, resulting in NPEs because the internal state has several nulled-out properties. It's however possible to add individual components instead of a Facelet file during render time. I didn't really have had the time to investigate if this is my fault or MyFaces' fault. That's also why it didn't end up in OmniFaces yet.
If you're using Mojarra anyway, feel free to use it. I however strongly recommend to test it thoroughly with all possible use cases on the very same page. Mojarra has some state saving related quirks which might fail when using this construct.

AJAX onSubmit validation in JSF 2.0

I've started learning JSF2.0, and have come across a problem. Any advice on how to proceed would be welcome.
I have renamed form elements and classes for simplicity sake.
I have a form, for example:
<h:form id="frmSearch">
<h:inputText id="dataPoint1" value="#{bean.dataPoint1}"/>
<div id="dataPoint1Error" class="msgError">Value not found in database.</div>
<h:inputText id="dataPoint2" value="#{bean.dataPoint2}"/>
<div id="dataPoint2Error" class="msgError">Value not found in database.</div>
<h:commandButton action="#{bean.validate}" type="submit" value="Search"/>
</h:form>
The CSS class "msgError" keeps the element hidden by default.
I would like to basically have a method in the "bean" class that validates the input by checking against the database, then if the value isn't found, unhide the error message, or if it is found, then execute another method which performs the actual functionality.
In my head, it would work sort of like this in the Java (forgive any syntax errors, just typing as I think):
#ManagedBean
public class Bean {
private String dataPoint1 = "";
private String dataPoint2 = "";
public boolean validate() {
if(dao.fieldExists(this.dataPoint1) && dao.fieldExists(this.dataPoint2)) { //check the database
performFunctionality();
return true;
}
else {
return false; //and show error div on screen
}
}
public void performFunctionality() {
//do whatever
}
//getters and setters
}
Any advice would be very welcome!
Thanks!
You're not utilizing JSF builtin validation facilities. Make use of it.
Here's how it can look like:
<h:form id="frmSearch">
<h:inputText id="dataPoint1" value="#{bean.dataPoint1}" validator="#{bean.validateDataPoint}" />
<h:message for="dataPoint1" />
<h:inputText id="dataPoint2" value="#{bean.dataPoint2}" validator="#{bean.validateDataPoint}" />
<h:message for="dataPoint2" />
<h:commandButton action="#{bean.performFunctionality}" value="Search">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
</h:form>
with
public void validateDataPoint(FacesContext context, UIComponent component, Object convertedValue) {
if (!dao.fieldExists((String) convertedValue)) {
throw new ValidatorException(new FacesMessage("Value not found in database."));
}
}
That performFunctionality() must be executed by the command button's action method.
When validation fails (i.e. ValidatorException is been thrown), then the message will be displayed in the <h:message> associated with the input component and the action method won't be invoked. The validator attribute can alternatively also point to a fullworthy class which implements javax.faces.validator.Validator. The <f:ajax> is been added to make it an ajax submit.
See also:
How to perform validation in JSF, how to create a custom validator in JSF
Wherever you've learnt JSF, make sure that you've also read the chapters about conversion and validation. Don't think too much the PHP/ASP/JSP/jQuery way. JSF is a full fledged component based MVC framework.

Resources