How can I check if multiple checkboxes are checked with JSF / AjaxBehaviorEvent - ajax

I have multiple checkboxes - they are given the same id, "choosen" in the xhtml view code.
I have added an Ajax event listener for them, like so:
<ui:repeat var="posts"
value="#{postBacking.postsOverview.posts}">
<h:selectBooleanCheckbox id="choosen"
styleClass="check-margin"
value="#{posts.choosen}" rendered="#{posts.declined =='No'}">
<f:ajax listener="#{postsBacking.isPostsChosen}" event="click" render="#form" execute="#form"/>
</h:selectBooleanCheckbox>
</ui:repeat>
In the listener method in my backingBean, I would like to check if any of these checkboxes are checked, and then set a button disabled if no checkboxes are marked - and enabled if one or more is set.
But how do I get hold of the values of all checkboxes - can I use the AjaxBehaviorEvent objet for that or is there some other way?

While processing the ajax event listener, the checkbox values should normally have been applied to your data model where you can look up the state of user choice. Depending on that state, simply toggle a boolean bean property which disables the command button:
#Named
#ViewScoped
public class MyBean implements Serializable {
private List<Post> posts;
private boolean buttonDisabled = true;
public void ajaxEventListener(BehaviorEvent e) {
boolean anythingChosen = false;
for (Post post : posts) {
if (post.isChosen() && "No".equals(post.getDeclined())) {
anythingChosen = true;
break;
}
}
// or alternatively using the stream api:
// anythingChosen = posts.stream().filter(Post::isChosen).map(Post::getDeclined)
// .anyMatch("No"::equals);
setButtonDisabled(!anythingChosen);
}
// getters, setters ...
}
Using the following form, the command button is disabled as long as no checkbox is checked:
<h:form>
<ui:repeat var="post" value="#{myBean.posts}">
<h:selectBooleanCheckbox value="#{post.chosen}">
<f:ajax event="click" listener="#{myBean.ajaxEventListener}" render="#form" execute="#form"/>
</h:selectBooleanCheckbox>
</ui:repeat>
<h:commandButton action="#{myBean.doSomething()}" value="doSomething"
disabled="#{myBean.buttonDisabled}" />
</h:form>

You can use valueChangeListener rather than using listener and event attributes. The valueChangeListener will be triggered every time you check/uncheck the checkbox.
To get hold of the values when thevalueChangeListener is invoked, you can loop through the postBacking.postsOverview.posts to see if any value is checked.
Don't forget to update the button on <f:ajax render="#form btnId"/> to enable/ disable it.

Related

How to postpone a keyup ajax request until h:inputText length is reached

Is there a way to postpone a keyup ajax request until a h:inputText value has reached a defined length?
I would like to reach the following goal:
a textInput field has to be filled with a combined date and time value. The expected format is: ddMMHHmm
Once the value reaches the length of 8 characters a new event object has to be added to an data list and should be displayed for confirmation immediately.
To confirm to add the new event the user simply presses enter inside this textInput field.
I don't know if there are different capabilities than using the ajax keyUp event to validate the input wihtout any further user interaction?
Here you see an very shortened example of my idea:
#Named
#SessionScoped
public class EventController {
private Date selectedDate; // +getter/+setter
private MyEvent event;
private List<MyEvent> events; // ArrayList<MyEvent>(), +getter
#PostConstruct
private void init() {
// load current events from DAO
}
public void validateInput() {
event = new MyEvent(selectedDate);
events.add(event);
}
public void confirmEvent() {
eventDAO.addEvent(event);
}
And the view:
<h:inputText
value="#{eventController.selectedDate}"
converter="#{comfortableDateTimeInputConverter}"
id="inputDateTime">
<f:ajax
<!-- pseudo code on !!! -->
executeCondition="<lengthOfInputField equals 8>"
<!-- pseudo code off !!! -->
execute="inputDateTime"
render="eventData"
event="keyup"
listener="#{eventController.validateInput}"
/>
</h:inputText>
<h:commandButton ... actionListener="#{eventController.confirmEvent}" />
<h:panelGroup id="eventData">
<h:dataTable var="..." value="#{eventController.events}">
// display event properties
</h:dataTable>
</h:panelGroup>
The ComfortableDateTimeInputConverter extracts the date an time parts of the input string and returns an date object.
I am using
primefaces 5.2
mojarra 2.2.8
Edit 1
As suggested by BalusC I modified my h:inputText, but nothing seems to happen. This is my original code exept the controller name. I've added a logging message inside eventController.validateNewEvent, but it seems not to be executed. Did I miss something?
<h:inputText
readonly="#{empty eventController.selectedPerson}"
value="#{eventController.selectedDate}"
id="inputDateTime"
tabindex="3"
converter="#{comfortableDateTimeInputConverter}"
onkeyup="return value.length >= 8"
onfocus="this.select()">
<f:ajax
event="keyup"
execute="inputDateTime"
listener="#{eventController.validateNewEvent}"
render="selectedDate txtDate listEvents" />
</h:inputText>
Also I tried to render="#all" at the ajax element, but still nothing happens. If i use event="blur" and leave the input with TAB it works like a charme ...
Edit 2 (resolved)
Replaced
onkeyup="return value.length >= 8"
with
onkeyup="return this.value.length >= 8"
and it works. See answer of BalusC ...
Just return false from onkeyup as long as value.length hasn't reached the desired value.
E.g.
<h:inputText ... onkeyup="return this.value.length >= 8">
<f:ajax event="keyup" ... />
</h:inputText>

Execute Backing Bean Action on p:selectOneRadio

I'm using a radio component on which, when selecting an item I want to execute an action on backing bean (not navigate to an outcome page, but to perform some action and then update current page via Ajax). Problem is I can't get the backing bean value change listener action to execute
<h:form id="one-radio">
<p:selectOneRadio layout="grid"
valueChangeListener="#{myBean.selectRadioItem}" >
<p:ajax process="#this" update="#form" />
<f:selectItems value="#{myBean.radioOptions}" var="radio"
itemLabel="#{radio.name}" itemValue="#{radio.id}" >
</f:selectItems>
</p:selectOneRadio>
</h:form>
and the backing bean method...
public void selectRadioItem(ValueChangeEvent event) {
String[] selectedItems = (String[]) event.getNewValue();
//more...
}
is there something wrong in the code which I'm missing? I've used this same structure for select many check box which is working...
Rodrigo, there's a difference between valueChangeListener and a simple method call via ajax.
Check this answer by BalusC about the difference between valueChangeListener and <f:ajax>
To solve your problem, you could use the listener property of <p:ajax>.
OneRadio component is used to receive only one value, if you want to select a list of values, use SelectOneMenu
Try to do the following:
<p:selectOneRadio layout="grid" value="#{myBean.radioValue}">
<p:ajax process="#this" update="#form" listener="#{myBean.selectRadioItem}"/>
<f:selectItems value="#{myBean.radioOptions}" var="radio"
itemLabel="#{radio.name}" itemValue="#{radio.id}" >
</f:selectItems>
</p:selectOneRadio>
In the backbean, you can remove the event parameter, because the value of the OneRadio component now is a property, which I named as radioValue
String radioValue;
...
public void selectRadioItem() {
String selectedItem = this.radioValue;
//more...
}

JSF Ajax method does not persist variable in bean

I have two forms: one to define some filter settings (ajax) and with sumbit buttons which reloads the page by action="null" and one form with a single command button which executes the ajax listener "#{bean.activate}":
<h:form id="settingsForm">
<h:selectBooleanCheckbox value="#{bean.boolean1}" id="bool1">
<f:ajax event="click" render="#all" execute="bool1"/>
</h:selectBooleanCheckbox>
<ui:repeat var="step" value="#{bean.availableSteps}">
<h:commandLink action="#{bean.navigate(step)}">
<h:outputText value="#{step.name}" />
</h:commandLink>
</ui:repeat>
</h:form>
<h:form id="activateForm">
<h:commandButton value="set activate bool and rerender" >
<f:ajax event="click" execute="#form" render="#all" listener="#{bean.activate}"/>
</h:commandButton>
</h:form>
The problem is following:
when I change the checkbox (boolean in backing bean) and click the submit button in the same form, only the changes (in the backing bean) are persisted which were set in the same form.
When I execute the second form (bean.activate), it rerenders fine (shows some more information), but when I THEN submit the first form, the changes of the second form are lost (but they are working on the same bean). I want that it remembers also the variables set in the second form...
It seems that the values set by the other form are not persisted, because when i perform a form submit, all the "information" stored by ajax of the second form is lost.
e.g.
boolean boolean1 = false; //set by form 1
boolean show = false; //set by form 2 (ajax)
public void activate(Ajaxbehaviour...){ show = true; }
Should there be by default only ONE form at all? it seems that two forms (one big and one just so set a variable by ajax) are too much...

selectOneMenu ajax events

I am using an editable primefaces selectOneMenu to display some values. If the user selects an item from the List a textarea should be updated. However, if the user types something in the selectOneMenu, the textarea should not be updated.
I thought I could work this with ajax event out. However, I don't know which event I can use here. I only know the valueChange event. Are there any other events, like onSelect or onKeyUp?
Here is my code:
<p:selectOneMenu id="betreff" style="width: 470px !important;"
editable="true" value="#{post.aktNachricht.subject}">
<p:ajax event="valueChange" update="msgtext"
listener="#{post.subjectSelectionChanged}" />
<f:selectItems value="#{post.subjectList}" />
</p:selectOneMenu>
<p:inputTextarea style="width:550px;" rows="15" id="msgtext"
value="#{post.aktNachricht.text}" />
The PrimeFaces ajax events sometimes are very poorly documented, so in most cases you must go to the source code and check yourself.
p:selectOneMenu supports change event:
<p:selectOneMenu ..>
<p:ajax event="change" update="msgtext"
listener="#{post.subjectSelectionChanged}" />
<!--...-->
</p:selectOneMenu>
which triggers listener with AjaxBehaviorEvent as argument in signature:
public void subjectSelectionChanged(final AjaxBehaviorEvent event) {...}
I'd rather use more convenient itemSelect event. With this event you can use org.primefaces.event.SelectEvent objects in your listener.
<p:selectOneMenu ...>
<p:ajax event="itemSelect"
update="messages"
listener="#{beanMB.onItemSelectedListener}"/>
</p:selectOneMenu>
With such listener:
public void onItemSelectedListener(SelectEvent event){
MyItem selectedItem = (MyItem) event.getObject();
//do something with selected value
}
Be carefull that the page does not contain any empty component which has "required" attribute as "true" before your selectOneMenu component running.
If you use a component such as
<p:inputText label="Nm:" id="id_name" value="#{ myHelper.name}" required="true"/>
then,
<p:selectOneMenu .....></p:selectOneMenu>
and forget to fill the required component, ajax listener of selectoneMenu cannot be executed.
You could check whether the value of your selectOneMenu component belongs to the list of subjects.
Namely:
public void subjectSelectionChanged() {
// Cancel if subject is manually written
if (!subjectList.contains(aktNachricht.subject)) { return; }
// Write your code here in case the user selected (or wrote) an item of the list
// ....
}
Supposedly subjectList is a collection type, like ArrayList. Of course here your code will run in case the user writes an item of your selectOneMenu list.

When to use valueChangeListener or f:ajax listener?

What's the difference between the following two pieces of code - with regards to listener placement?
<h:selectOneMenu ...>
<f:selectItems ... />
<f:ajax listener="#{bean.listener}" />
</h:selectOneMenu>
and
<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
<f:selectItems ... />
</h:selectOneMenu>
The valueChangeListener will only be invoked when the form is submitted and the submitted value is different from the initial value. It's thus not invoked when only the HTML DOM change event is fired. If you would like to submit the form during the HTML DOM change event, then you'd need to add another <f:ajax/> without a listener(!) to the input component. It will cause a form submit which processes only the current component (as in execute="#this").
<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
<f:selectItems ... />
<f:ajax />
</h:selectOneMenu>
When using <f:ajax listener> instead of valueChangeListener, it would by default executed during the HTML DOM change event already. Inside UICommand components and input components representing a checkbox or radiobutton, it would be by default executed during the HTML DOM click event only.
<h:selectOneMenu value="#{bean.value}">
<f:selectItems ... />
<f:ajax listener="#{bean.ajaxListener}" />
</h:selectOneMenu>
Another major difference is that the valueChangeListener method is invoked during the end of the PROCESS_VALIDATIONS phase. At that moment, the submitted value is not been updated in the model yet. So you cannot get it by just accessing the bean property which is bound to the input component's value. You need to get it by ValueChangeEvent#getNewValue(). The old value is by the way also available by ValueChangeEvent#getOldValue().
public void changeListener(ValueChangeEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
// ...
}
The <f:ajax listener> method is invoked during INVOKE_APPLICATION phase. At that moment, the submitted value is already been updated in the model. You can just get it by directly accessing the bean property which is bound to the input component's value.
private Object value; // +getter+setter.
public void ajaxListener(AjaxBehaviorEvent event) {
System.out.println(value); // Look, (new) value is already set.
}
Also, if you would need to update another property based on the submitted value, then it would fail when you're using valueChangeListener as the updated property can be overridden by the submitted value during the subsequent UPDATE_MODEL_VALUES phase. That's exactly why you see in old JSF 1.x applications/tutorials/resources that a valueChangeListener is in such construct been used in combination with immediate="true" and FacesContext#renderResponse() to prevent that from happening. After all, using the valueChangeListener to execute business actions has actually always been a hack/workaround.
Summarized: Use the valueChangeListener only if you need to intercept on the actual value change itself. I.e. you're actually interested in both the old and the new value (e.g. to log them).
public void changeListener(ValueChangeEvent event) {
changeLogger.log(event.getOldValue(), event.getNewValue());
}
Use the <f:ajax listener> only if you need to execute a business action on the newly changed value. I.e. you're actually interested in only the new value (e.g. to populate a second dropdown).
public void ajaxListener(AjaxBehaviorEvent event) {
selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}
If you're actually also interested in the old value while executing a business action, then fall back to valueChangeListener, but queue it to the INVOKE_APPLICATION phase.
public void changeListener(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
System.out.println(newValue.equals(value)); // true
// ...
}
for the first fragment (ajax listener attribute):
The "listener" attribute of an ajax tag is a method that is called on the server side every time the ajax function happens on the client side. For instance, you could use this attribute to specify a server side function to call every time the user pressed a key
but the second fragment (valueChangeListener) :
The ValueChangeListener will only be called when the form is submitted, not when the value of the input is changed
*you might like to view this handy answer

Resources