how to disable multiple h:inputtext fields in JSF - jsf

In my home page(.xhtml),I'm having 4 h:inputText box and one serach button(h:commandButton)in JSF page
<h:inputText id="stlID" value="#{searchSettlementTransRequest.stlmtTransId}" name='stlmt'>
<h:inputText id="pmtID" value="#{searchSettlementTransRequest.pmtTransId}" name='pmt'>
<h:inputText id="AgentID" value="#{searchSettlementTransRequest.AgentId}" name='agnt'>
<h:inputText id="AgencyID" value="#{searchSettlementTransRequest.AgencyId}" name='agncy'>
<h:commandButton id="tranSearchBtn" styleClass="valign first button-search sSearch" action="stlmtSubmit"/>
My requirement is:
on entered data in 1st input field the other input fields should be disabled(ie. 2,3,4).
on entered data in 2nd input field the other input fields should be disabled(ie. 1,3,4). and so on..
Note: at the sametime if user entered data in 1st or 2nd or 3rd or 4th (user dynamic)field and delete the same before leaving out the input field in such case all field should be enabled.
When i get response back(empty or non-empty data response) after clicking "search" button now all the input fields should be enabled for another search(now user may input data in any of the 4 fields)

If you want the inputs to be enabled after each page load you can have a flag representing the page is loaded and set that flag to false after each ajax request through an listener. And to ensure that the flag is set to true after complete page loads you can use preRenderView event. You just need a method to be executed for preRenderView event which set the flag to true if the page is loaded through complete request rather than an ajax request. If the page is loaded with ajax request, faces-request parameter will be set to "partial/ajax".
public static final String INITIAL_VALUE = ""; // I assume your fields are integer and they are initialized to INITIAL_VALUE in the first place
public Boolean pageIsLoadedWithFullRequest = true;
public void preRenderView()
{
//check whether the request is an ajax request or not
Map<String, String> requestHeaderMap = FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderMap();
if(!"partial/ajax".equals(requestHeaderMap.get("faces-request")))
{
pageIsLoadedWithFullRequest = true;
}
And if you want to disable other inputs whenever the user writes into one of the fields, you can make an ajax call after a blur event to change the state of other input fields and set the pageIsLoadedWithFullRequest flag to false in a listener.
public void madeAjaxRequest()
{
pageIsLoadedWithFullRequest = false;
}
public Boolean getStlIDEnabled()
{
return pageIsLoadedWithFullRequest || !stlmtTransId.equals(INITIAL_VALUE) && pmtTransId.equals(INITIAL_VALUE) && AgentId.equals(INITIAL_VALUE) && AgencyId.equals(INITIAL_VALUE);
}
public Boolean getPmtTransIdEnabled()
{
return pageIsLoadedWithFullRequest || stlmtTransId.equals(INITIAL_VALUE) && !pmtTransId.equals(INITIAL_VALUE) && AgentId.equals(INITIAL_VALUE) && AgencyId.equals(INITIAL_VALUE);
}
public Boolean getAgentIdEnabled()
{
return pageIsLoadedWithFullRequest || stlmtTransId.equals(INITIAL_VALUE) && pmtTransId.equals(INITIAL_VALUE) && !AgentId.equals(INITIAL_VALUE) && AgencyId.equals(INITIAL_VALUE);
}
public Boolean getAgencyIdEnabled()
{
return pageIsLoadedWithFullRequest || stlmtTransId.equals(INITIAL_VALUE) && pmtTransId.equals(INITIAL_VALUE) && AgentId.equals(INITIAL_VALUE) && !AgencyId.equals(INITIAL_VALUE);
}
<f:event type="preRenderView" listener="#{searchSettlementTransRequest.preRenderView()}"/> <!--This will ensure that preRenderView method will be called right before rendering of the view ends -->
<h:inputText id="stlID" value="#{searchSettlementTransRequest.stlmtTransId}" name='stlmt' disabled="#{not searchSettlementTransRequest.stlIDEnabled}"/>
<h:inputText id="pmtID" value="#{searchSettlementTransRequest.pmtTransId}" name='pmt' disabled="#{not searchSettlementTransRequest.pmtTransIdEnabled}"/>
<h:inputText id="AgentID" value="#{searchSettlementTransRequest.AgentId}" name='agnt' disabled="#{not searchSettlementTransRequest.AgentIdEnabled}"/>
<h:inputText id="AgencyID" value="#{searchSettlementTransRequest.AgencyId}" name='agncy' disabled="#{not searchSettlementTransRequest.AgencyIdEnabled}"/>
If you want to disable them through ajax you should add the f:ajax command and render all four of these inputTexts. Example:
<h:inputText id="stlID" value="#{searchSettlementTransRequest.stlmtTransId}" name='stlmt' disabled="#{not searchSettlementTransRequest.stlIDEnabled}">
<f:ajax event="blur"
listener=#{searchSettlementTransRequest.madeAjaxRequest()}
execute="stlID pmtID AgentID AgencyID"
execute="stlID pmtID AgentID AgencyID"/>
</h:inputText>
Unrelated It is not a good practice to capitalize the first letters of variables such as AgentId, AgencyID, it should be better if you change them to agentId and agencyID.
Cheers

If you want to do this only in jsf just have to add some AJAX calls. Some suggestions below:
Add to all your <h:inputText attribute disabled with value of #{searchSettlementTransRequest.getDisabled('this_component_id')". So the whole thing will look like this:
<h:inputText id="someId1" value="#{searchSettlementTransRequest.someValue}" name="stlmt" disabled="#{searchSettlementTransRequest.getDisabled('someId1')}"/>
Next in your bean processing this request change settter for `stlmTransId, pmtTransId, AgentId, AgencyId' that their will mark that this value is being set:
public void setXXX(XXX xxx) {
// your actual code comes here and at the end set that this component was changed
actuallyChanged = id_of_input_text_component_corresponding_to_this_value_as_string;
}
Of course you have to add field String actuallyChanged to your bean.
Method getDisabled(String compId) will know looks:
public String getDisabled(String compId) {
if (actuallyChanged.equals(compId)) {
return "";
else {
return "disabled";
}
}
For all your components you have also add AJAX call like this:
<f:ajax event="change" execute="#this" render="stlID pmtID AgentID AgencyID"/>
So know when value of one component will change all other will be disabled. But there is immposible to make them enabled after call is make because without cliend side scripting you cannot easly make them available on select or focus gained or anything.
If you like to disable components temporary, for example when user type in one component other all disabled, but when he select some of them then this one selected is enabled and all other, included this in which he firstly write text, will be disable. In that case better aproach is to use client side scripting for this, JavaScript for example.
Update to exactly match your problem in JavaScript (jQuery)
add to all components an event like this:
onkeyup="$('#thisComponentId').attr('disabled', false); $('#otherComponentId').attr('disabled', true); $('#otherComponentId').attr('disabled', true); "
After your comment you can change it to:
onkeyup="if ($('#thisComponentId').val() && $('#thisComponentId').val() == '') { // here set enable for all components } else { // here set disable for all other components }
This isn't very sophisticated way but it should work. Know when you start typing in one component other will be disabled but when you send request all will be enabled again.

Related

How to make h:inputText readonly all time and value should post back [duplicate]

I am creating a calculator where a user can do the various operations by clicking on virtual numpad. Now I want to restrict the user to only select the various characters by clicking on the buttons and s/he should not be able to enter the values using keyboard. I tried using readonly="true" but it is neither validating the input nor setting the values in the bean when we click on any button.
Do we have any such restrictions available in jsf?
That's the effect of JSF's safeguard against tampered/attacked requests wherein the hacker attempts to circumvent the readonly (and disabled) attribute by manipulating the HTML DOM and/or the HTTP request.
Instead of
<x:inputXxx ... readonly="true">
use
<x:inputXxx ... readonly="#{facesContext.currentPhaseId.ordinal eq 6}">
or
<x:inputXxx ... readonly="#{not facesContext.postback or facesContext.renderResponse}">
This makes sure that readonly is only effective during render response phase and not during all other JSF phases. So, when JSF is about to decode the input component during the apply request values phase, it will consider readonly="false" this way.
See also:
other uses of #{facesContext.currentPhaseId.ordinal}
The key codes for the top row of numbers are 48-57 so you could write a function to disable keypress on those characters.
Something like this
function disableEnter(event) {
var charCode = (event.which) ? event.which : event.keyCode;
if (charCode > 31 && (charCode > 47 || charCode < 58)) {
return false;}
} else return true;
}
<h:inputText onkeypress="return disableEnter(event);" />
I found another workaround:
function stopEvent(event){
event.stopPropagation();
event.preventDefault();
}
onkeyup="stopEvent(event)" onkeydown="stopEvent(event)" onkeypress="stopEvent(event)"

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 apply Primefaces effect when component is rendered

i have drag panel and drop panel, and when component is dropped in the drop panel i show a new panel and hide the old panel based on render boolean attribute as follows:
1- xhtml:
<p:outputPanel id="oldPanel" rendered=#{myBean.old_panel_rendered}> .... </p:outputPanel>
<p:outputPanel id="newPanel" rendered=#{myBean.new_panel_rendered}> .... </p:outputPanel>
2- bean:
old_panel_rendered=true;
new_panel_rendered=false;
public void onComponentDrop(DragDropEvent ddEvent) {
old_panel_rendered=false;
new_panel_rendered=true;
}
how to execute an effect for newPanel when it gets rendered and execute an effect for oldPanel when it gets unrendered.
please advise, thanks.
Call js function which will apply the effects when a new item droppped:
<p:ajax listener="#{bean.onDrop}" onstart="applyEffects();" update="newPanel" />
Function is:
function applyEffects() {
var oldPanel = $(document.getElementById('oldPanel'));
var newPanel = $(document.getElementById('newPanel'));
oldPanel.css({"display":"none"});//or oldPanel.fadeOut(500) which looks fancy
newPanel.css({"display":"inline"});
newPanel.effect("highlight",
{color:"#87FF7A"}, 1500);
}
Don't forget to give exact client id of components when calling document.getElementById. You can detect it via your browser's developer settings. If there will be a problem, you can drop update="newPanel" or maybe you can try update="oldpanel newpanel".
To be able to apply it for specific panel:
public void onComponentDrop(DragDropEvent ddEvent) {
int id = event.getData();//or sth.similar to getId
RequestContext.getCurrentInstance().addCallbackParam("index", id);
}
Upper code adds a parameter to ajax response it can be retrieved by:
function applyEffects(xhr,status,args) {
var oldPanel = $(document.getElementById('oldPanel'));
var newPanel = $(document.getElementById('newPanel'));
if(args.id=='oldPanel') {//oldPanel or whatever which equals to eventID
oldPanel.css({"display":"none"});//or oldPanel.fadeOut(500) which looks fancy
}
newPanel.css({"display":"inline"});
newPanel.effect("highlight",
{color:"#87FF7A"}, 1500);
}
You should call this from p:ajax oncomplete="applyEffects(xhr,status,args);". I am coding directly here, therefore can be few mistakes which can be seen on IDE easily.

JSF: why is empty test in rendered invoked during apply request values phase during form submission under request scoped POST REDIRECT GET

This question is spawned from the partial answer to JSF2: why does empty test in rendered of panelGroup in composite prevent action from being called?
In the following an Element is an #Entity with a name and id. A view.xhtml JSF page takes the id as a viewParam and uses setID(Long id) of the #ManagedBean #RequestScoped ElementController to trigger loading of the corresponding Element by id from database (that plays no further role in the question) and this found Element is set as the 'current' Element available (for historical reasons by a slightly different name) as Element getSelected().
The view.xhtml page performs a rendered attribute test #{not empty elementController.selected}, and has a h:commandButton with action that performs a faces-redirect, along with the id as query parameter, back to the view.xhtml page.
For some reason I do not fully understand, on form submission the test (and thus getSelected) is invoked in both the apply request phase and the process validations phase, before the viewParam id can be set (and thus before the current/selected Element can be found and set) in the update model values phase.
The greatly abbreviated view.xhtml page is:
<f:view>
<f:metadata>
<f:viewParam name="id" value="#{elementController.id}"/>
</f:metadata>
</f:view>
<h:body>
<h:form>
<h:panelGroup rendered="#{not empty elementController.selected}">
<h:outputText value="#{elementController.selected.name}"/>
</h:panelGroup>
<h:commandButton value="Apply" action="#{elementController.action}" />
</h:form>
</h:body>
(The sense of the form submission is lost above, but it does not matter for the this question.)
ElementController extends RequestController:
public void setId(Long id) {
log_debug("setId","id",id);
if (id != null) {
this.id = id;
T found = (T) getAbstractFacade().find(id);
if (found == null) {
String $error = "No object with id(" + id + ") found for class " + getManagedClass().getSimpleName();
log_error($error);
}
setCurrent(found);
}
}
public T getSelected() {
log_debug("getSelected","current",current);
if (current == null) {
log_warn("getSelected","null current Element");
}
return current;
}
public Object action() {
String $i = "action";
log_debug($i);
if (current==null) {
log_warn($i, "can't generate action outcome for null current element");
return null;
}
return "/view?faces-redirect=true&id="+current.getId();
}
Now on form submission, getSelected() happens to get called twice, and when current==null, once during the apply request values phases and once during the process validations phase, due to the test #{not empty elementController.selected} before the setting of the id (and thus loading of the Element entity) can occur thanks to the viewParam in the view.xhtml.
The question is, why is the rendered=#{not empty elementController.selected} invoked at all during the apply request phase and process validations phase ?
It is not invoked during those phases when I perform an initial GET load of the view.xhtml with id parameter, only during a form submission POST and subsequent redirect and GET.
The reason that the rendered attribute is consulted twice or more after a post back is because JSF traverses the component tree in each phase.
The name 'rendered' is perhaps not the best possible name, as it doesn't just make rendering of the component to which it applies conditional, but actually processing it in general.
It's consulted in the first place for 'apply request values' to see if that component and its children should be processed to have those request values applied to them. It's consulted again in 'process validations', since its value might have changed between phases.
It's not invoked 'during those phases when I perform an initial GET load', because when you perform a GET the component tree isn't traversed in those phases (only the metadata is processed, which is the reason view params are put in a special metadata section).
In order to make the id that you received from the GET request available in the action method after the post back, you'd best use the view scope (#ViewScoped) for your backing bean.

Reset value to null in primefaces autocomplete event

I have an autocomplete event that fires correctly once a value is selected. I want another event to fire once I erase the value in the textbox and reset the value to null. I was thinking of using the onChange attribute but I was having issues so I reverted back to my original code.
<p:autoComplete id="deviceAuto" dropdown="true" scrollHeight="250"
value="#{summaryReportController.device.nickname}"
forceSelection="true"
completeMethod="#{summaryReportController.deviceComplete}">
<p:ajax event="itemSelect"
listener="#{summaryReportController.handleDeviceSelect}"
update="printThis" />
</p:autoComplete>
public void handleDeviceSelect(SelectEvent event) {
String deviceSelect = event.getComponent().getId();
if (deviceSelect.equalsIgnoreCase("deviceAuto")) {
Device selectedDevice = deviceMgr.getDevicebyNickname(device.getNickname());
setDevice(selectedDevice);
}
updateInterface();
}
When you modify the text content of the AutoComplete textfield, the search method (aka. completeMethod) will be called on the backing bean. You can reset the value to null there if you get an empty string.
Backing Bean
// insert getter and setter for the device property ...
/** Search for devices by name */
public List<String> deviceComplete(String search) {
if (StringUtils.isBlank(search)) {
setDevice(null); // textfield was cleared, reset device value!
return Collections.emptyList();
} else {
// search for devices ...
return deviceNames;
}
}
Note that I used Apache Commons StringUtils.isBlank(String) to check if the string was null or did only contain whitespace characters.
JSF View
In your XHTML file you probably want to listen to any Ajax event to update your view -- or you figure out the event you need (blur, change, whatever) yourself:
<p:autoComplete ...>
<p:ajax event="itemSelect" listener="..." update="..." />
<p:ajax process="#this" update="..." />
</p:autocomplete>
I hope this helps.
An alternative could be something like a "clear" or "reset" button next to the search textfield to make it clear to the user that the value will be cleared.
The default autoComplete minQueryLength attribute equals 1 and your search string will be updated when you deleting it until it has lenght of 1 character.
E.g.:
You entering 'foo' - and this string is provided to search method (updating after entering first character - minQueryLength = 1)
But when you delete search string - it is also updated until it will have length of 1.
Solution:
set attribute minQueryLength="0"
Or:
if you need bigger value add to your autoCompleteMethod(String search) condition:
if (search.length()<={your minQueryLength attribute} ) field = null;
Old question, but I think it worths another view.
The problem with minQueryLenth = 0 or minQueryLenth = 1 is that it can return hundreds of options (and for sure the user won't read all of them to choose one). My solution was as follows.
First of all I need the input to be sent to the server as soon as the user select one of its values (in my use case the user is not allowed to go to next step in a wizard if this value is null or empty). So I put an ajax function triggered in the event of a selected value.
xhtml:
<p:autoComplete
id="someId"
value="#{myViewScopedBean.selectedValue}"
...
...
minQueryLenth="5"
onblur="autoCompleteLostFocus('someId', 'someCommand()')">
<p:ajax
event="itemSelect"
listener="#{myViewScopedBean.newValueSelected}"
process="#this"
update="commandButtonGoToNextStep" />
</p:autoComplete>
<p:remoteCommand
name="someCommand"
actionListener="#{myViewScopedBean.setValueNull}"
update="commandButtonGoToNextStep" />
<p:commandButton
id="commandButtonGoToNextStep"
...
...
disabled="#{myViewScopedBean.selectedValue == null}" />
If the user clean the text, I need to send that value to "myViewScopedBean" and update the component that allows the user to go to the next step. I solved that putting a javascript function that is called when the autocomplete lose focus.
javascript:
function autoCompleteLostFocus(autocompleteId, comand) {
if ($("[id='" + autocompleteId + "_input']").val().trim() == "") {
eval(comando);
}
}
in myViewScopedBean:
public void setValueNull() {
selectedValue = null;
}
I hope it helps. A lot of work, but the behaviour is exactly what I wanted. The reason for the javascript function is that it just send information to the servlet if the value is equals to "", otherwise it does nothing.
From a completely different angle...
Why do you have summaryReportController.device.nickname as a value in autoComplete?
I'd suggest you to use device as a value and specify
var
itemLabel
itemValue
converter
in your autocomplete, while your completeMethod will return list of devices filtered by nickname. Converter is implementation of javax.faces.convert.Converter.
See the POJO case in PF Showcase.

Resources