How to freeze or disable an update input form - spring

I wonder if it is possible for me to freeze or disable the entire update form? I have an input h:form with a check box in it. when users check the box, I would like to freeze or disable the entire form so that disallow users from changing inputs.
Thanks, and I am using JSF, Spring Web Flow, Facelets, and Trinidad.

You would want to use javascript to set all the form inputs to disabled when the user checks the checkbox. Something like:
document.getElementById('id').disabled = true;
You would do this for each input element where 'id' is the ID of that element.

If you want to disable only certain inputs, It is a good idea to enumerate them:
function OptCheckBox(chkd) {
if (chkd == 'y') {
document.frm.input1.disabled = true;
document.frm.input2.disabled = true;
}
}

You cannot disable an entire form at once. You really need to disable each of the input elements. There are basically two ways to achieve this.
First way is to use Javascript to submit the form to the server when the checkbox is clicked, so that you can use JSF component's disabled attribute to disable the elements. Here's a basic example:
<h:form>
<h:selectBooleanCheckbox value="#{bean.freeze}" onclick="submit()" />
<h:inputText value="#{bean.value1}" disabled="#{bean.freeze}" />
<h:inputText value="#{bean.value2}" disabled="#{bean.freeze}" />
<h:inputText value="#{bean.value3}" disabled="#{bean.freeze}" />
</h:form>
Here #{bean.freeze} should point to a boolean property.
Second way is to write a Javascript function for this. This does not require a form submit and saves you from one HTTP request-response cycle, which is better for user experience.
<h:form>
<h:selectBooleanCheckbox onclick="disableForm(this)" />
<h:inputText value="#{bean.value1}" />
<h:inputText value="#{bean.value2}" />
<h:inputText value="#{bean.value3}" />
</h:form>
The JS function disableForm() is basically simple. Just pass the checkbox in as function argument by this so that you can get the parent form by checkboxElement.form and then get all form elements by form.elements. You only need to make sure that you don't disable the checkbox itself, so that you could re-enable the form again :)
function disableForm(checkboxElement) {
var elements = checkboxElement.form.elements;
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element != checkboxElement) {
element.disabled = checkbox.checked;
}
}
}
No need to know the ID's beforehand and this makes the JS code generic and reuseable.

Related

Interdependent components validation [duplicate]

In a basic registration screen (with button register records the screen) there are two panels:
Data panel:
Address panel:
I can register by completing only the Data panel. It is not necessary to fill the Address panel. However, if at least one field of the Address panel is filled, then all other fields in the same panel should be required.
How can I achieve this?
You need to check in the required attribute if the other inputs have submitted a non-empty value. Since this can result in quite some boilerplate, here's a kickoff example with only 3 input components.
<h:form id="form">
<h:inputText id="input1" value="#{bean.input1}" required="#{empty param['form:input2'] and empty param['form:input3']}" />
<h:inputText id="input2" value="#{bean.input2}" required="#{empty param['form:input1'] and empty param['form:input3']}" />
<h:inputText id="input3" value="#{bean.input3}" required="#{empty param['form:input1'] and empty param['form:input2']}" />
</h:form>
An alternative is to bind the components to the view and use UIInput#getValue() to check the value of the previous components and UIInput#getSubmittedValue() to check them for next components (components are namely processed in the order as they appear in the component tree). This way you don't need to hardcode client ID's. You only need to ensure that binding names doesn't conflict with existing managed bean names.
<h:inputText binding="#{input1}" value="#{bean.input1}" required="#{empty input2.submittedValue and empty input3.submittedValue}" />
<h:inputText binding="#{input2}" value="#{bean.input2}" required="#{empty input1.value and empty input3.submittedValue}" />
<h:inputText binding="#{input3}" value="#{bean.input3}" required="#{empty input1.value and empty input2.value}" />
You'll understand that this produces ugly boilerplate when you have more and more components. The JSF utility library OmniFaces has a <o:validateAllOrNone> validator for the exact purpose. See also the live demo. Based on your quesiton tags, you're using OmniFaces, so you should already be set with just this:
<o:validateAllOrNone components="input1 input2 input3" />
<h:inputText id="input1" value="#{bean.input1}" />
<h:inputText id="input2" value="#{bean.input2}" />
<h:inputText id="input3" value="#{bean.input3}" />
First you should add a method to backing bean something like this:
public boolean isAddressPanelRequired() {
// Check if at least one field is entered
// and return true if it is and false othervise
}
Each input element on address panel should have required="#{backingBean.addressPanelRequired}"
Then add onblur ajax listener on each input component on address panel which process that component, and updates address panel.

PrimeFaces disable submit on pressing enter key

PrimeFaces disable submit on pressing enter key.
I’m, running PrimeFaces 5.1 running on WildFly 8.2 Final.
I have dialog, with two inputNumbers and two buttons. And the first inputNumber does some calculation on ajax blur event. Next to it is button which does some calculation in bean. And the problem is that when users press enter while focus is in inputNumber the button’s action gets fired and it’s really annoying. Is there a way to disable submitting with enter key on dialog?
Here is small xhtml dialog which can simulate my behavior:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions" >
<p:dialog id="id_example" header="Test dialog"
widgetVar="exampleDialog" modal="true" closable="true" >
<h:form id="id_example_form">
<p:panelGrid columns="3" styleClass="noBorders">
<h:outputText value="Input 1:" />
<pe:inputNumber id="Input1" value="#{exampleBean.number1}">
<p:ajax event="blur" update="valueInput1" />
</pe:inputNumber>
<p:commandButton value="Check something else" action="#{exampleBean.checkForUsername()}"
update=":growl_form" />
<h:outputText value="Input 1:" />
<p:inputText id="valueInput1" value="#{exampleBean.number1}" />
<p:commandButton value="Save" action="#{exampleBean.save()}" oncomplete="PF('exampleDialog').hide();"
update=":growl_form" />
</p:panelGrid>
</h:form>
</p:dialog>
</ui:composition>
And the bean:
package si.pucko.beans;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import si.pucko.util.Util;
#Named(value = "exampleBean")
#ViewScoped
public class ExampleBean implements Serializable {
private static final long serialVersionUID = 1L;
private BigDecimal number1;
public ExampleBean() {
number1 = new BigDecimal(BigInteger.ONE);
}
public BigDecimal getNumber1() {
return number1;
}
public void setNumber1(BigDecimal number1) {
this.number1 = number1;
}
public void checkForUsername() {
Util.ShowWarning("Just testing");
}
public void save() {
Util.ShowWarning("Saved");
}
}
The catch is i can't disable enter key with:
<h:form onkeypress="if (event.keyCode == 13) { return false; }">
Because client asked for hotkeys support and enter is used for submiting forms, recalculation some other values in some cases etc...
I think you use JavaScript to capture the enter key press and do nothing.
<h:form onkeypress="if (event.keyCode == 13) { return false; }">
Ref: https://stackoverflow.com/a/5486046/201891
return false; cancels an event across browsers if called at the end of an event handler attribute in the HTML. This behaviour is not formally specified anywhere as far as I know.
Ref: https://stackoverflow.com/a/1648854/201891
Update
It sounds like you want to disable the Enter key only when focus is in a particular field. You can write a Javascript method for that too and bind it to onkeypress. Write a Javascript method something like "if the enter key was pressed and the focus is in this field, return false; otherwise, return true".
As the answer referenced by Nimnio says, this is specific to HTML and browsers.
I consider this behavior to be inappropriate when using PrimeFaces.
I prefer to disable it globally, for all forms like this:
$('form').off('keypress.disableAutoSubmitOnEnter').on('keypress.disableAutoSubmitOnEnter', function(event) {
if (event.which === $.ui.keyCode.ENTER && $(event.target).is(':input:not(textarea,:button,:submit,:reset)')) {
event.preventDefault();
}
});
The target check allows the other default behaviors to work, like adding a line break in a textarea by pressing Enter.
To take into account new ajaxically added forms you'll need to call the above script after every AJAX request. There are multiple ways to do that, such as a <script> in a p:outputPanel autoUpdate="true", or calling a function in a p:ajaxStatus's oncomplete callback.
If this solution is not appropriate for some reason then consider the more localized one:
<h:form onsubmit="return false;">
Returning false here disables the non-AJAX default submit.
Putting a dummy/hidden button before the one you want to stop works best for me
<p:commandButton style="visibility: hidden;"/> <!-- dummy hidden button to stop keyPress from firirng submit -->
I used following solution:
<h:form onsubmit="return false;">
This prevents form submit. It works well only in case if you have ajax only behavior on this form.
It is default browser behavior to hunt a form for jQuery(":submit") elements and trigger the first listed on the form, when the enter key is pressed.
this will look strange on debugger, because if you have a function such as onclick="handle(event);".
You go to the text field, hit enter, and you will see an "artifical" onclick event with being passed to your primary submit action for that form.
The surest way to be in-control of what happens, I would say, is not by means of onkeypress as explained above. I found that to not work in all cases. On soame cases the form onkeypress simply does not get triggered, and you do not have then the opportunity to return flase; / event.preventDefault();. I am not 100% sure of all cases that justfied the onkeypress not getting triggered, but I suspect framework code preventing event bubbling in some instances.
Ultimately, what is really happening is your form is being submitted by your browser default behavior on ENTER withing input text field. It is not the bubling of the event to the form that submits the form. That is the flaw of trying to handle the event on the form and preventing default behavior there. That might just be too late.
And this is very easy to verify. If you tune youe inpu text and onkeypress always to event.preventDefault(). (A) you kill the textbox no text gets written on the texbox (B) The event still bubles up, but the browser does not submit the form anyway.
So the most important element of all is: where is the browser aiming at when it acts with the default behavior?
So what you can decide instead is that what you will take control of where the browser unleashes its default behavior. You can direct it to a dummy button.
This is the one and only one mechanism I have found to be really reliable to be in control of what happens when you hit enter on a text field withing a form is to have on that form a well known artifical button, e.g.:
<input type="submit" onclick"return false;" style="border:none; background-color: #myAppBrackgroundColor;" />
You get the idea, to make sure that the submit event is being routed by the browser to a button under your control.
You have to be careful, IE in particular is quirky.
If not for IE, you could simply style your button as display: none.
Because of IE, the browser will route the default submit action to a visible submit button, if one exists.
Therefore, your button cannot be in display none. You have to make it logically visible, but phsysically invisible. For that you supress the border and give it an appropriate background for your application.
This method is 100% reliable.
onkeydown and onchange()
<h:form id="inputform">
<p:inputText onkeydown="if (event.keyCode === 13) {onchange();return false;}" >
<p:ajax update="updatearea" />
</p:inputText>
</h:form>

Is there a way to prevent JSF from displaying previous/outdated content in h:selectManyMenu after validation error?

I have a form with, say a text field and a multi-select field.
<h:form id="form2">
<h:messages for="text3" />
<h:inputText id="text3" value="#{danielBean.text3}" required="true"/>
<br/>
<h:messages for="select1" />
<h:selectManyMenu id="select1" value="#{danielBean.selStrings}"
style="height:8em;" required="true"/>
<f:selectItems value="#{danielBean.allStrings}" var="_item" itemLabel="#{_item} (length #{_item.length()})" />
<br/>
<p:commandButton
value="Submit"
action="#{danielBean.save()}"
update=":form2"
>
</h:form>
On submit, both get validated, and if validation is successful, the relevant variables in the backing bean are updated. Fair enough.
The problem happens when the validation (of the multi-select) is NOT successful. In my Bean, I have (particularly for the List<String>s allStrings and selStrings)...
#PostConstruct
public void init() {
text3 = "";
allStrings.clear();
allStrings.add("This");
allStrings.add("is");
allStrings.add("a");
allStrings.add("test");
selStrings.clear();
selStrings.add("a");
}
...so that the multi-select has one pre-selected option. If the user unselects that option (i.e. no options chosen), the validation will -of course- fail, an error message will be displayed...
...but the multiselect will not be empty. It will show the content from the bean, i.e. "a" selected. This is confusing to the user - getting an error message "input required", and being shown a filled-out field.
This appears to be a feature of JSF's lifecycle, see this article by BalusC:
"When JSF renders input components, then it will first test if the submitted value is not null and then display it, else if the local value is not null and then display it, else it will display the model value."
This works fine for the text field text3, because it submits as an empty string, not null.
The problem is that zero selected options from a multi-select means that the submitted value is null, that the local copy (I guess, since it's the first submit) is null, so that the model value ("a" selected) is displayed.
I do not want that.
How can I force JSF to use the null value it got submitted when rendering the validation response?
Thanks in advance
Antares42
There is no solution for this (other than reporting an issue to JSF guys and/or hacking in JSF source code). There's however a workaround: update only the components which really need to be updated on submit. Currently, you've set ajax to update the entire form. How about updating just the messages?
<h:messages id="text3_m" for="text3" />
...
<h:messages id="select1_m" for="select1" />
...
<p:commandButton ... update="text3_m select1_m" />
You can if necessary make use of PrimeFaces Selectors to minimize the boilerplate if you have rather a lot of fields:
<h:messages for="text3" styleClass="messages" />
...
<h:messages for="select1" styleClass="messages" />
...
<p:commandButton ... update="#(.messages)" />

Multiple onClick events

I have a JSF page, a drop down where values are getting fetched from service.
Default is "Select from Drop Down" and a datePicker and a submit button.
I need to apply JS/AJAX validation here.
If a user clicks on Submit button without chosing any value from the drop down and the date. It should first diplay a message,
1) If none chosen, first show a message - Please select a value from drop down.
2) if the value is selected from the drop down and date has not been selected It should display a message " select a date".
3) if the date is selected and value is not selected from the drop down It should display a message " select a value from the drop down".
Both validation should be done on a single click on submit button.
Currently it's just checking if date is not selected. onclick event code is mentioned below.
Drop Down
<h:selectOneMenu id="valueList" value="#bean.values">
<f:selectItem itemValue="Select Action" itemLabel="Select Action" />
<f:selectItems value="#{sampleService.sampleMethod}"
var="SampleVar" itemValue="#{SampleVar}"
itemLabel="#{SampleVar}">
</f:selectItems>
</h:selectOneMenu>
Submit button
<ui:define name="actions">
----h:inputHidden id="getAllDates"
value="#{serviceMethod.getAllDates}"-----
<h:commandButton styleClass="inputbutton" valuGenerate" id="export"
action="#{generate.someReport}" onclick="saveDate(); />
</ui:define>
Passing all selected dates in hidden.
OnClick event this js function is written in another file.
onclick="saveDate();"
function saveDate() {
var dates = $('#selectDates').datepick('getDate');
var datesValue = '';
if(dates==null || dates=="undefined"){
alert("Select Dates");
return false;
}
if(dates!=null){
// store in an array and return true
}
Currently page is getting refreshed on every click on Submit button and alert message gets displayed only for date.
Can anybody help me in applying ajax call on submit button and displaying validation messages?
You're thinking too much in the JavaScript direction. Use JSF provided facilities. Use the required attribute to specify required inputs. Use requiredMessage attribute to specify the validation message. Use <h:message> to display the validation messages. Use <f:ajax> to submit (and validate) data by ajax.
So, this should do:
<h:selectOneMenu id="action" binding="#{action}" value="#{bean.action}" required="true" requiredMessage="Please select action">
<f:selectItem itemValue="#{null}" itemLabel="Select Action" />
<f:selectItems value="#{bean.actions}" />
<f:ajax execute="#this" render="actionMessage date" />
</h:selectOneMenu>
<h:message id="actionMessage" for="action" />
<x:yourDatePickerComponent id="date" value="#{bean.date}" required="#{not empty action.value}" requiredMessage="Please select date">
<f:ajax execute="#this" render="dateMessage" />
</x:yourDatePickerComponent>
<h:message id="dateMessage" for="date" />
(I have no idea what component you're using as date picker, just replace x:yourDatePickerComponent accordingly)
you don't need ajax :
function save() {
var validated = true;
var dates = $('#selectDates').datepick('getDate');
var datesValue = '';
var message;
if(dates==null || dates=="undefined"){
alert("Select Dates");
message = "message1";
validated = false;
return false;
}
if( $('#idOfDropDown').val() != ''){
message = "message2";
validated = false;
return false;
}
if(!validated){
// preveent thr form from submitting
return false;
}
}

Ajax Listener event valueChange seems to be firing onClick instead of onChange

I have a nested list of Questions that I'd like to display. Initially, I'm displaying the Level 1 questions and then subquestions are displayed based on the users answers to the their parent question. All questions have a radio button and some questions have an input box for additional information that is shown when user selects "Yes"
Here is my JSF code with nested dataTables. Please note that I have pulled out formatting of these questions in order to simply the question on the forum, so these may look "unpretty" if you copy this code into your own environment and run the code:
<h:dataTable id="questionTable" var="q" value="#{generalQuestionBean2.questions.questions}">
<h:column><h:panelGroup id="questionGrp">
#{q.question} <h:selectOneRadio value="#{q.answer}">
<f:selectItem itemValue="1" itemLabel="Yes"/>
<f:selectItem itemValue="0" itemLabel="No"/>
<f:ajax event="valueChange" execute="#form"
render="questionGrp"
listener="#{generalQuestionBean2.reset}"/>
</h:selectOneRadio> <h:inputText value="#{q.addnInfo}"
rendered="#{q.answer eq '1' and q.field ne 'otherCov'}"></h:inputText>
<h:panelGroup id="questionGrpSubs" rendered="#{q.addnQuestions ne null and q.answer eq '1'}">
<h:dataTable id="subQuestionTable" var="subq" value="#{q.addnQuestions}">
<h:column><h:panelGroup id="subQuestionGrp">
->#{subq.question} <h:selectOneRadio id="answer" value="#{subq.answer}">
<f:selectItem itemValue="1" itemLabel="Yes"/>
<f:selectItem itemValue="0" itemLabel="No"/>
<f:ajax event="valueChange" execute="#form"
render="subQuestionGrp"
listener="#{generalQuestionBean2.reset}"/>
</h:selectOneRadio><h:inputText value="#{subq.addnInfo}"
rendered="#{subq.answer eq '1' and subq.field ne 'voluntaryComp' and subq.field ne 'uslh'}"></h:inputText>
<h:panelGroup id="questionGrpSubs2" rendered="#{subq.addnQuestions ne null and subq.answer eq '1'}">
<h:dataTable id="sub2QuestionTable" var="sub2q" value="#{subq.addnQuestions}">
<h:column><h:panelGroup id="sub2QuestionGrp">
-->#{sub2q.question} <h:selectOneRadio id="answer" value="#{sub2q.answer}">
<f:selectItem itemValue="1" itemLabel="Yes"/>
<f:selectItem itemValue="0" itemLabel="No"/>
<f:ajax event="valueChange" execute="#form"
render="sub2QuestionGrp"
listener="#{generalQuestionBean2.reset}"/>
</h:selectOneRadio><h:inputText value="#{sub2q.addnInfo}"
rendered="#{sub2q.answer eq '1'}"></h:inputText>
</h:panelGroup></h:column>
</h:dataTable></h:panelGroup>
</h:panelGroup></h:column>
</h:dataTable></h:panelGroup>
</h:panelGroup></h:column>
</h:dataTable>
Here is the code for the reset function on the backing bean:
private void reset(AjaxBehaviorEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
String id = event.getComponent().getClientId(context);
String[] tokens = id.split("[:]+");
int qId = -1;
int subqId = -1;
int sub2qId = -1;
for (int i = 0; i < tokens.length; i++) {
if(tokens[i].equals("questionTable"))
qId = Integer.parseInt(tokens[i+1]);
if(tokens[i].equals("subQuestionTable"))
subqId = Integer.parseInt(tokens[i+1]);
if(tokens[i].equals("sub2QuestionTable"))
sub2qId = Integer.parseInt(tokens[i+1]);
}
Question q = questions.getQuestion(qId);
Question processQ = q;
String defaultSubAnswer = getDefaultSubAnswer(q.getField());
Question subq;
Question subq2;
if(subqId > -1) {
subq = q.getAddnQuestions().get(subqId);
processQ = subq;
if(sub2qId > -1) {
subq2 = subq.getAddnQuestions().get(sub2qId);
processQ = subq2;
}
}
resetValue(processQ, defaultSubAnswer);
}
private void resetValue(Question q, String defaultSubAnswer) {
q.setAddnInfo("");
if(q.getAddnQuestions() != null) {
for (int i = 0; i < q.getAddnQuestions().size(); i++) {
Question subq = q.getAddnQuestions().get(i);
subq.setAnswer(defaultSubAnswer);
resetValue(subq, defaultSubAnswer);
}
}
}
Here is the problem:
The ajax event should default to "valueChange". If I click on "Yes" and then "Yes" again, the ajax call should not happen, correct? But it is, as the Additional Info box is clearing out based on the reset function.
I had originally tried adding a condition to the reset function to check the value of the button that was clicked and only reset the addnInfo value and subquestions if the answer is "0" (No). But this was causing issues with the rendering as the ajax call would render the input box and subquestions to hide and the value would be held onto on the front end, even though they're reset on the backing bean. When they re-rendered to the front in, the value that was held onto shows up instead of the value in the backing bean.
Another attempt was using a ValueChangeListener for the reset of the values. But this still has the same issue with the value being held onto when re-rendering.
I have tried 3 different approaches (listed above) and all have failed. I'm open to hearing a solution to any of these or possibly another solution. Keep in mind that formatting limitations by the users leaves me with less options to work with.
Ajax Listener event valueChange seems to be firing onClick instead of onChange
That's indeed the default valueChange event which is been used by the radio buttons and checkboxes which are genreated by the respective JSF components. Check the generated HTML source and you'll see that it's hooked to onclick. The reason why JSF does that by default is clear for checkboxes, but at first sight not entirely clear for radio buttons as they cannot be unticked anyway. The real reason is that it's done in order to ensure IE6/7 compatibility as onchange wouldn't be fired on 1st click in that browser.
If you don't care about IE6/7 users (whose distribution is however stongly decreasing lately), then change it to event="change" instead.
<f:ajax event="change" ... />
This way JSF will generate the event handler on onchange instead.
Update: you could use jQuery's .on('change') function binder which will fix the IE6/7 misbehaviour on the change event. Include the following in <h:head> if you're not already using jQuery:
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
and execute this function on load:
$(function() {
$(':radio').each(function() {
var handler = this.onclick;
this.onclick = null;
$(this).on('change', handler);
});
});
This will basically for every radio button move the JSF-generated onclick attribute to change event handler which is managed by jQuery so that it works consitent in all browsers, including IE6/7.
We can of course also do it with plain JS, but that would require lot of boilerplate and cross browser sensitive code.

Resources