Keep non submitted values, when pressing "more-Button" - jsf

First, my scenario:
I have a form including several input fields. Some of them are hidden by default, because they are not really often needed:
<h:form>
<!-- with validators -->
<h:inputText>...</h:inputText>
<h:inputText>...</h:inputText>
<!-- show this fields only when the user wants to -->
<h:panelGroup rendered="#{!bean.hide}">
<h:inputText>...</h:inputText>
<h:inputText>...</h:inputText>
</h:panelGroup>
<h:commandButton value="Show / hide more fields" actionListener="#{bean.toogleHide}" />
<h:commandButton value="Submit" />
</h:form>
This would be the bean:
#SessionScoped
#ManagedBean
public class Bean {
// with getter
private boolean hide = true;
public void toogleHide(ActionEvent event) {
hide = !hide;
}
}
What i want to do:
Assume, a user fills the first to input fields with valid / invalid data. Now he wants to see the other inputfields that are hidden by default. So would press the command button "show / hide more fields".
=> is there a way the keep the non submitted values when pressing the "show / hide more fields" button?

My suggestion would be to always send the "hidden fields" from your JSF page, but make them display: none by default, using CSS. Then hide/unhide them with javascript only, e.g. jQuery or a small JS script on the page. The whole will be much more responsive, plus you don't have the issue you're fighting with.

Related

Primefaces won't call setter on ManagedBean

I'm in the middle of building a web application (Spring 4, Primefaces 5.1 and JPA).
While one of my xhtml-pages has no problems working with its ManagedBean, the others won't really work. To be more specific, the UI input components (like inputText) won't call any setters from the managed Beans.
I've already run my Tomcat (8) on debug mode, which showed me that the page calls the get-methods, but no set-methods at all. If I try to persist something, all values are null (or 0, in case of an int-value). (it even persists an object into the database with all values null, though I have declared some #NotNull constraints which should be taken to the database configuration via JPA)
So, question is: How can I make my inputFields work with the fields of my ManagedBean? (Eclipse also shows me in the editor, that theoretically, the connection to the fields is there, it knows the managed bean, the field and the get-/set-methods)
SoftwareManagedBean.java
#ManagedBean(name = "swmb")
#ViewScoped
public class SoftwareManagedBean extends AssetManagedBean implements
Serializable {
private String bezeichnung;
private Software neueSoftware;
// +some more private fields, every single one with its get-/set-method
#Override
public String getBezeichnung() {
return super.getBezeichnung();
}
#Override
public void setBezeichnung(final String bezeichnung) {
super.setBezeichnung(bezeichnung);
}
//instantiante the field "neueSoftware"
public void createEmptySoftware(){
if(neueSoftware != null)
return;
this.neueSoftware = new Software();
}
//Persist Software with values from inputFields
public void addSoftware() {
createEmptySoftware();
neueSoftware.setBezeichnung(getBezeichnung());
softwareService.addSoftware(neueSoftware);
//...
neueSoftware = null;
}
viewSoftware.xhtml
<h:body>
<p:dialog header="Neue Software anlegen" widgetVar="SwNeuDialog" width="60%"
closeOnEscape="true" draggable="false" resizable="false" position="center">
<h:form id="SwDlgForm">
<h:panelGrid columns="3" border="0" >
<p:outputLabel for="swBezeichnung" value="Bezeichnung: " />
<p:inputText id="swBezeichnung" value="#{swmb.bezeichnung}"
label="Bezeichnung" required="true" />
<f:verbatim/>
<p:outputLabel for="swKategorie" value="Kategorie: " />
<p:selectOneMenu id="swKategorie" value="#{swmb.kategorie}" label="Kategorie" required="true" >
<f:selectItem itemLabel="Kategorie wählen" value="#{null}" noSelectionOption="true"/>
<f:selectItems value="#{swmb.kategorieListe}" var="kat" itemLabel="#{kat.bezeichnung}" itemValue="#{kat}"/>
</p:selectOneMenu>
<p:commandButton value="neue Kategorie hinzufügen" />
<!-- + some more input fields -->
<p:commandButton value="Speichern" action="#{swmb.addSoftware()}" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
<p:commandButton value="Abbrechen" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
</h:panelGrid>
</h:form>
</p:dialog>
<!-- dataTable -->
</h:body>
AssetManagedBean.java
#ManagedBean
public abstract class AssetManagedBean {
//name of the hard-/software
private String bezeichnung;
//+ some more fields with get-/set methods
public String getBezeichnung() {
return bezeichnung;
}
public void setBezeichnung(String bezeichnung) {
this.bezeichnung = bezeichnung;
}
I hope that code is sufficient to see the problem, since the rest of the code follows the same structure. I think, the problem could lie within the xhtml file, but I don't see where or why. I've got the SpringBeanFacesELResolver (or however it is called), I've already looked through the code and compared it to another xhtml page and its Managed Bean, but there are no differences anymore. (though one is working, one not)
My debugger showed, how the working class/page (viewAdministration.xhtml) called the get-/set methods of the managed bean:
opening dialog window: get...(), set...()
clicking the commandButton to submit/persist: get() (old Value), set() (new Value), get() (new Value)
Another get() (called by the add... method)
(+ another get() for the dataTable on the main page)
on viewSoftware.xhtml, it looks like this:
opening dialog window: get()
clicking the commandButton to submit/persist:
another get() called by the addSoftware method
As you can see, when I try to submit, there is no set or get.
So, to summarize:
no setter called by trying to submit
the code on viewSoftware.xhtml and SoftwareManagedBean is similar to another, functioning ManagedBean + xhtml page (I've compared it again and again)
annotations in Managed Beans are the same (#ManagedBean, #ViewScoped)
the inputFields are inside a form (
I'm totally clueless, but I think it is some small mistake from my side that I can't just see.
I've searched through the web and especially stackoverflow, but all the problems and answers I've found couldn't help me finding what's wrong
Even without inheriting from a superclass it won't work (tried that out too)
I hope, you can help me. If this post is lacking some information, I'm sorry about that, I tried my best to not let this post grow too big and still get as much relevant information in it as possible.
So, I have found my mistake (or at least, I think I have). Only took me 2 weeks, but anyway...
I tried to test it out more specifically, wrote test classes and an xhtml page. Nothing worked (from simple Input over Dates to own classes).
The solution to this problem was to disable ajax on my commandButton (ajax="false"). When I tried to understand more of it, I realized that the commandButton opening the dialog window was nested in a facet inside a dataTable, and thus it had problems to properly set the values of the input fields.
So, thank you for your help. Maybe/hopefully this can or will help some other people later as well.
From the first glance at the code, without reading it whole, try putting process="SwDlgForm" on your command buttons, instead of process="#this". If that doesn't solve the problem, I'll read more carefully and try to help.

how to get selected records count in rich data table having multiple pages rich:datascroller

I am facing a problem where i want to test that whether a user has selected any record or not from the multiple page in rich:dataTable. These pages are created through rich:datascroller. Related code is given below.
<rich:datascroller id="dataScroller" selectedStyleClass="myClass" renderIfSinglePage="false"
align="center" for="dataList" maxPages="10"
pageIndexVar="currentPage" pagesVar="totalPages" reRender="pageNum,dataList" ajaxSingle="false">
<f:facet name="first"><h:outputText value="First" /></f:facet>
<f:facet name="last"><h:outputText value="Last" /></f:facet>
</rich:datascroller>
<h:inputHidden id="pageNum" value="#{currentPage}"></h:inputHidden>
<h:inputHidden id="numOfRows" value="10"></h:inputHidden>
The problem is that if user selects a record (say on 3rd page) and after that he moves to page one. Now in this case when i try to iterate through the table in Java Script to know that whether he has selected any record or not than it always allows me to iterate till the current page on which i am and hence i am unable to give user the proper alert message for selecting a record first on pressing of submit button. All i want is to know that user has checked the check box against a record on any page from the available pages , irrespective of the fact that on which page is currently opened. Any ideas?
Try it via the Richfaces Javascript API.
For every component you have in your page, there's a representing Javascript object, named as next:
Richfaces_componentId (small 'f' for faces).
Using that and to understand it live, go to the ScrollableDatatable Live Demo, open your chrome browser console, and execute this:
Richfaces_ScrollableGrid_j_id353_carList.selectionManager.selection.ranges
This will print out an empty javascript array.
Try selecting some rows, and running that line again, you'll have an array containing the indexes of your selected rows.
So to put that in a function:
<script>
function tableHasSelection() {
return Richfaces_ScrollableGrid_j_id353_carList.selectionManager.selection.ranges.length != 0;
}
</script>
Now you call this function via onclick of your submit button, and return false in case of no selection to prevent submission.
Of course you would replace Richfaces_ScrollableGrid_j_id353_carList with your component's javascript representing object.
Hopefully this will help.
[UPDATE]
According to our discussion in the comments, here is an update that might be helpful.
Your data table would be like this:
<rich:extendedDataTable value="..." var="element" id="table" selectionMode="..." selection="...">
<rich:column>
<h:selectBooleanCheckbox value="..." onclick="toggleSelection(this, #{element.id});" />
</rich:column>
</rich:extendedDataTable>
<h:commandButton value="Submit" action="#{...}" onclick="return tableHasSelection();" />
And your script would have these functions:
<script>
var selectedElements = [];
function toggleSelection(checkbox, id) {
if ($(checkbox).is(':checked')) {
//add selected row if checkbox is checked
selectedElements.push(id);
} else {
//or find it in the array and remove it
var index = selectedElements.indexOf(id);
if (index > -1) {
array.splice(index, 1);
}
}
}
function tableHasSelection() {
return selectedElements.length != 0;
}
</script>
(<rich:extendedDataTable> has selectable rows, you may want to look at that)
One thing you can do is to submit the selection when the user clicks it. E.g.
<h:selectBooleanCheckbox onclick="saveSelection(#{rowId})">
…
<a4j:jsFunction name="saveSelection">
<a4j:param name="id" assignTo="#{bean.selectedId}">
</a4j:jsFunction>
But if the user will only be choosing one row then simply say if the selected row is not on the current page the user didn't select anything, I don't think that's a wrong approach.

Programmatically control which components should be ajax-updated

I have a complex form where the user fills a few fields, and has two options: generate a license file or save the changes. If the user clicks on the generate license file button without saving the changes, I render a small component with an error message asking him to save before generating the license.
To display the component with a warning message, I want to use ajax to avoid rendering the whole page just to render the warning component. Of course, if the changes were saved, then the warning message is not required and I redirect the user to another page.
I have a change listener on the changeable fields to detect when a change has been made. What I don't know is the conditional execution. The "render with ajax if unsaved OR redirect if saved" part. Here's the logic
if(saved){
redirect();
}else{
ajax.renderWarning()
}
--EDIT--
I'm going to add more info because I realized I'm leaving things too open ended.
Here's one example of an updateable field.
<h:inputText name="computername3" value="#{agreement.licenseServerBeans[2].computerId}" valueChangeListener="#{agreement.fieldChange}">
<rich:placeholder value="Add Computer ID"/>
</h:inputText>
The fieldChange() bean method
public void fieldChange(ValueChangeEvent event) {
change = true; //change is a boolean, obviously :P
}
Here's the generate license button jsf
<h:commandLink action="#{agreement.generateLicenseFile}">
<span class="pnx-btn-txt">
<h:outputText value="Generate License File" escape="false" />
</span>
</h:commandLink>
Here's the generateLicenseFile() method
public String generateLicenseFile(){
....//lots of logic stuff
return "/licenseGenerated.xhtml?faces-redirect=true";
}
Use PartialViewContext#getRenderIds() to get a mutable collection of client IDs which should be updated on the current ajax request (it's exactly the same as you'd specify in <f:ajax render>, but then in form of absolute client IDs without the : prefix):
if (saved) {
return "/licenseGenerated.xhtml?faces-redirect=true";
}
else {
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add("formId:messageId");
return null;
}
Returning null causes it to redisplay the same view. You can even add it as a global faces message and let the ajax command reference the <h:messages> in the render.
if (saved) {
return "/licenseGenerated.xhtml?faces-redirect=true";
}
else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(...));
return null;
}
with
<h:messages id="messages" globalOnly="true" />
...
<f:ajax render="messages" />

How to evaluate a FacesComponent property inside a custom component

Lately, I've been working on a dynamic form project but is stop with a custom component problem. What I have:
A faces component for formField:
#FacesComponent(value = "formField")
public class FormFieldCompositeComponent {
private boolean email;
}
A custom component jsf:
<o:validator for="email_text" validatorId="emailValidator" disabled="#{not cc.email}" />
OR
<c:if test="#{not cc.email}">
<f:validator for="email_text" validatorId="emailValidator"></f:validator>
</c:if>
OR
<f:validator disabled="#{not cc.email}" for="email_text" validatorId="emailValidator"></f:validator>
And the validator:
#FacesValidator("emailValidator")
public class EmailValidator implements Validator { }
My problems are:
1.) If I use an ordinary f:validator, like the one I use above and then use c:if to enable/disable it, then it will not work. According to some articles I've read it's because f:validator validates on build time, not on render time.
2.) If I use o:validator, it works but the problem is every time you hit submit a new line of invalid email error is added to p:messages. Example I clicked submit button 3 times, then I get 3 times the email error.
Any idea?
More info (anatomy of the project)
Example I have a page user with field email, it will include the following custom components:
+user.xhtml
+formPanel
+formField (this is where the validator is defined)
+formButtons (the action button)
+p:messages is defined
user.xhtml
<formPanel>
<formField field="email" />
<formButtons />
</formPanel>
Command button is like (formButtons):
<p:commandButton id="saveButton" rendered="#{cc.attrs.edit}"
value="#{messages['action.save']}"
action="#{cc.attrs.backingBean.saveOrUpdate()}" icon="ui-icon-check"
ajax="#{cc.attrs.ajaxSubmit}">
<c:if test="#{cc.attrs.backingBean.lcid != null}">
<f:param name="cid" value="#{cc.attrs.backingBean.lcid}" />
</c:if>
</p:commandButton>
The p:messages as defined on formPanel:
<p:messages id="formMessages" showDetail="true" showSummary="false" redisplay="false"></p:messages>
Note:
1.) What I've noticed is that the validator is called n times, where n is the number of submit or click done.
xhtml - https://github.com/czetsuya/crud-faces/blob/master/crud-faces/src/main/webapp/administration/user/user.xhtml
the tags - https://github.com/czetsuya/crud-faces/tree/master/crud-faces/src/main/webapp/resources/tags
bean component - https://github.com/czetsuya/crud-faces/tree/master/crud-faces/src/main/java/org/manaty/view/composite
Seems like there's no chance for the f:validator so I push through o:validator and come up with a workaround. Need to catch if the error is already in the FacesMessages list:
boolean match = false;
for (FacesMessage fm : context.getMessageList()) {
if (fm.getDetail().equals(message)
|| fm.getSummary().equals(message)) {
match = true;
break;
}
}
if (!match) {
throw new ValidatorException(facesMessage);
}

richfaces text and text area on rerender unable to use keyboard up and down arrow keys to move the cursor

I am using RichFaces 3.3.2 and JSF 1.2.
Scenario:
In my application when the user enters some text in a text field or a text area we have to enable the apply button. For this we are using rerender 'onkeyup' event.
Validation error mesage should be shown when user enters invalid date
JSF Page code:
<h:inputText id="input" required="true" requiredMessage="Value cann't be null"
value="#{client.Value}" validatorMessage="Value should be between 0 and 999999999999.99" valueChangeListener="#{myHandler.onChange}" maxlength="30">
<f:validateDoubleRange minimum="0" maximum="999999999999.99"/>
<a4j:support event="onkeyup" eventsQueue="A4JQueue" reRender="outputPanel_btn_save"></a4j:support>
Apply button code:
<a4j:outputPanel id="outputPanel_btn_save">
<a4j:commandButton value="#{msg.btn_Apply}" id="applyButton" reRender="outputPanel_btn_save" eventsQueue="A4JQueue" disabled="#{myHandler.isApplyButtonDisabled}" styleClass="button" action="#{myHandler.update}">
</a4j:commandButton>
</a4j:outputPanel>
Backing bean code for myHandler.onChange()
MyHandler {
public void onChange(ValueChangeEvent event) {
Iterator<FacesMessage> facesMessages = getFacesContext().getMessages();
boolean isMessagePresent=false;
while(facesMessages.hasNext()){
FacesMessage message = facesMessages.next();
isMessagePresent=true;
break;
}
if (event != null
&& event.getNewValue() != event.getOldValue()&& !isMessagePresent) {
this.isApplyButtonDisabled = Boolean.FALSE;
}
if(isMessagePresent){
this.isApplyButtonDisabled = Boolean.TRUE;
}
}
}
Problem:
When the user enters some text onkeyup event we are rerendering the apply button. Due to rerender keyboard arrow keys are not working as the field is loosing it's focus onKeyup event.
I tried using JS to enable the apply button, when user enters some text. But then JSF validations are not being executed and no validation message are being displayed.
Question:
Can you suggest a way to enable the apply button with out loosing focus on the text field / text area and also JSF validations should be executed / displayed?
Try using focuc attribute on a4j:support. It allows to set focus on a component after Ajax request.

Resources