Why does PrimeFaces 5.3 not call my listener function when I catch the "blur" and "focus" events? - ajax

My intention was to show information on filling a text area component. When it gets the focus I show the info, when it loses the focus I clear the info. I do not want to proccess any input value just catch the focus and blur events and call the listener and show/clear the message.
I have read BalusC's comment on conditional rendering so I put my conditional text message to an "always rendered" JSF component.
My "always rendered" f:outPutPanel component is shown in the generated XHTML code but my listener function is never called. As far as I know the method signature of primefaces listener is just void somefunction() not like in jsf ajax listener.
I also read BalusC's comment about including #this for the proccess attribute but in my case it seems not to be reasonable as I do not want to catch any input values.
I am newbie to JSF and primefaces so any help is appreciated.
My relevant view snippet:
<h:form>
<h:panelGroup layout="block" id="info">
<h:outputText rendered="#{ebookController.infoMessage != null}" value="#{ebookController.infoMessage}"/>
</h:panelGroup>
....
<p:inputTextarea id="description" styleClass="form-control vspace" required="true" requiredMessage="Kérlek, add meg az e-könyv rövid leírását" value="#{ebookController.newEbook.description}" rows="20" cols="30" counter="display" maxlength="500" counterTemplate="{0} karakter lehet még" autoResize="false" validatorMessage="Az e-könyv leírása maximum 500 karakterből állhat">
<f:validateLength maximum="500"/>
<p:ajax event="focus" process="#none" update="info" listener="#{ebookController.setTextAreaInfoMessage()}"/>
<p:ajax event="blur" process="#none" update="info" listener="#{ebookController.clearInfoMessage()}"/>
</p:inputTextarea>
<h:outputText id="display" />
</h:form>
My relevant java code:
#Named(value = "ebookController")
#SessionScoped
public class EbookController implements Serializable {
private String infoMessage;
public String getInfoMessage() {
return infoMessage;
}
public void setInfoMessage(String infoMessage) {
this.infoMessage = infoMessage;
}
public void setTextAreaInfoMessage(){
setInfoMessage("Tipp: Az e-könyved bemutatása során a fontos részeket emeld ki, és tagold a szöveget bekezdésekkel. Ha egy bekezdés végére értél, csak egy ENTER-t üss!");
}
public void clearInfoMessage(AjaxBehaviorEvent e){
setInfoMessage("");
}
....
}

Solution 1 : User f:ajax
<f:ajax immediate="true" event="focus" process="#none" render="info"
listener="#{ebookController.setTextAreaInfoMessage()}" />
<f:ajax immediate="true" event="blur" process="#none" render="info"
listener="#{ebookController.clearInfoMessage()}" />
Managed Bean
public void setTextAreaInfoMessage() {
System.out.println("setTextAreaInfoMessage");
setInfoMessage(
"Tipp: Az e-könyved bemutatása során a fontos részeket emeld ki, és tagold a szöveget bekezdésekkel. Ha egy bekezdés végére értél, csak egy ENTER-t üss!");
}
public void clearInfoMessage() {
System.out.println("clearInfoMessage");
setInfoMessage("");
}
Reference Problem with h:form and p:ajax

Related

PrimeFaces input components are not highlighted on validation error

I am using Seam 2.3.1 Final. And I have added the Custom EmailValidation on my form.
#Name("emailValidator")
#BypassInterceptors
#org.jboss.seam.annotations.faces.Validator
public class EmailValidator implements Validator {
private static final String EMAIL_REGEX = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
/**
* <a href="http://www.mkyong.com/regular-expressions/how-to-validate-email
* -address-with-regular-expression/">Source</a> <br/>
* Modification : autorisation des "-" dans le nom de domaine <br/>
* Exemple valide : jean-michel-75440.exemple42#email-pro.mon-entreprise.com
*/
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
/* Create the correct mask */
Pattern mask = Pattern.compile(EMAIL_REGEX);
/* Get the string value of the current field */
String emailField = (String) value;
/* Check to see if the value is a valid email */
Matcher matcher = mask.matcher(emailField);
if (!matcher.matches()) {
FacesMessage message = new FacesMessage();
message.setDetail("E-posta adresi geçerli değil!");
message.setSummary("E-posta Hatasi");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
public String getValidatorId() {
return "emailValidator";
}
}
And the JSF
<h:form id="editPersonelForm">
<p:messages showDetail="true" autoUpdate="true" closable="true"/>
<p:outputLabel for="personName" value="Ad"/>
<p:inputText id="personName" placeholder="Ad" value="#{personelBean.personel.name}" required="true"
requiredMessage="Ad alanını doldurmak zorunludur." validatorMessage="Ad alanı zorunludur.">
<p:outputLabel for="personEmail" value="E-Posta"/>
<p:inputText id="personEmail" value="#{personelBean.personel.email}" placeholder="E-Posta" >
<f:validator validatorId="emailValidator" />
</p:inputText>
<p:outputLabel for="personelSaveBtn" value=""/>
<p:commandButton id="personelSaveBtn" value="Kaydet"
action="#{personelBean.saveOrPersist}"
oncomplete="if (args && !args.validationFailed) PF('personEdit').hide();"
update=":tableForm" ajax="true">
</p:commandButton>
</p:panelGrid>
</h:form>
It works, when I type invalid email, it gives error message text. However, input fields does not switch error-state mode. There is no redline border of input anymore.
You need to explicitly cover the inputs in ajax-update as well. One way is adding #form which represents the current form.
<p:commandButton ... update=":tableForm #form" />
Or by its explicit ID.
<p:commandButton ... update=":tableForm :editPersonelForm" />
Another way is using a PFS/jQuery selector to reference only the inputs.
<p:commandButton ... update=":tableForm #(#editPersonelForm :input)" />
A completely different way is using OmniFaces <o:highlight> to put PrimeFaces own style sheet on the associated inputs (and labels) so that you never need to worry about explicitly ajax-updating them.
<o:highlight styleClass="ui-state-error" />

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>

How to use f:selectItem properly with JSF2?

guys I need to update another selectOneMenu when the User selects one option of combo_pedido_tipoplm selectOneMenu. If the user selects the "Chose One" option it needs to clear the second one.
I've tried everything but I cant call my actionListener after I've selected the Option "Chose one" (Selecione) from my h:selectOneMenu.
XHTML
<h:selectOneMenu id="combo_pedido_tipoplm" value="#mBeanManterPedido.tipoPlacaMaeFiltro}" required="true" disabled="#{!mBeanManterPedido.pedidoValido or mBeanManterPedido.clonado}">
<f:selectItem itemLabel="#{msgTemplate.lblSelecione}" />
<f:selectItems value="#{mBeanManterPedido.selectItemsTipoPlacaMae}" />
<p:ajax event="change" listener="#{mBeanManterPedido.tipoPlacaMaeChange}" process="#this"/>
</h:selectOneMenu>
MBean
private TipoPlacaMae tipoPlacaMaeFiltro;
public void popularTipoPlacaMae() {
this.selectItemsTipoPlacaMae = new ArrayList<SelectItem>();
for (TipoPlacaMae tipoplaca : TipoPlacaMae.values()) {
this.selectItemsTipoPlacaMae.add(new SelectItem(tipoplaca, tipoplaca.getNome()));
}
UtilsCommon.orderByLabel(selectItemsTipoPlacaMae);
}
public void tipoPlacaMaeChange(AjaxBehaviorEvent e) {
// deseleciona a PlacaMae atualmente selecionada.
pedido.setPlacaMae(null);
limparDadosPedido();
popularPlacaMae();
}
if I select the option "Chose one" I got a validation error because this selectOneMenu is requeried and my setter for tipoPlacaMaeFiltro is not called.
<?xml version='1.0' encoding='UTF-8'?>
{"validationFailed":true}
What can I do about it ?
For those that have the same doubt.
You need to put the immediate="true" in your ajax request.
<h:selectOneMenu id="id"
value="#{mbean.value}" required="#{mbean.required}"
converter="#{mbean.converter}">
<f:selectItem itemLabel="Chose one" />
<f:selectItems value="#{mbean.selectItems}" />
<f:ajax event="valueChange" listener="#{mbean.onchange}"
render="second_combo"
immediate="true">
</f:ajax>
</h:selectOneMenu>
After that in the ManagedBean you need to implements but handle differently the listener
public void onchange(AjaxBehaviorEvent e) {
YourClass o = getSelectedValue(e);
}
private YourClass getSelectedValue(AjaxBehaviorEvent e)
{
if (e != null)
{
UISelectOne select = (UISelectOne) e.getSource();
if (select.getSubmittedValue() == null ||
select.getSubmittedValue().toString().isEmpty()) {
return null;
}
else {
String id = select
.getSubmittedValue().toString();
return ObjectThatImplementsConverterInterface.getAsObject(FacesContext.getCurrentInstance(), select, id);
}
}
return null;
}
Unfortunally you must handle that way because if you don't do the user can selects "Chose One" and the mbean.value will be OK (null) after that the second selection in the same SelectOneMenu will trigger with the previous selected value (null). I don't know if it happens with everybody or just me.
Bye.

Updating a checkbox from a radio button

I am trying to add ajax behavior to selectoneradio with this code:
xhtml:
<h:selectOneRadio id="metalA" converter="metalConverter" value="#{backingBean.metal.metalCode">
<f:selectItems value="#{backingBean.metalCodeRadio}" />
<f:ajax listener="#{backingBean.updateMenu}" event="click" execute="metalA" render="metalTypeMenuA"/>
</h:selectOneRadio>
<p:outputPanel id="panelA">
<h:selectOneMenu id="metalTypeMenuA" converter="metalConverter" value="#{backingBean.order.metal}" rendered="#{teklifIslemleriBean.selectedITip == 1}">
<f:selectItems value="#{backingBean.metalDetailsMenu}" />
</h:selectOneMenu>
</p:outputPanel>
backing bean:
MetalCode selectedMK = null;
public void updateMenu(AjaxBehaviorEvent event) {
System.out.println("Entered to updateMenu method");
if (metal.getMetalKod()!= null) {
electedMK = aMetal.getMetalCode();
}
if (selectedMK != null) {
// metalTypeMenuA Combobox
List<Metal> metalList = aService.getAccToMetalCode(null, selectedMK);
System.out.println("MetalList:" + metalList.size());
metalTypeMenuA.clear();
for (Metal m : metalList) {
metalTypeMenuA.add(new SelectItem(m, "No:" + m.getMetalNo() + " ,Weight: " + m.getWeight();
}
}
}
However it does not even enter to the updateMenu method. instead of click I tried select, change, etc. I also tried to put a wrapper panel and update it instead of checkbox, still no good. What is wrong with above code? Is updating a checkbox with a change in radiobutton doable? Thanks in advance.
JSF 2.0 Primefaces 2.2.1
EDIT:
I added following
<h:message for="metalA" id="messaged"/>
<f:ajax listener="#{backingBean.updateMenu}" event="click" execute="metalKoduA" execute="metalA" render="messaged orderPG2"/>
orderPG2 is a wrapper around checkbox. But still I can get any error message in h:message or any ajax behavior is happening.
The render attribute of <f:ajax> should not point to a component which is by itself conditionally server-side rendered by rendered attribtue. Let it point to the closest parent which is always rendered instead.
<f:ajax listener="#{backingBean.updateMenu}" render="panelA" />
(note that I removed event="click" and execute="metalA" as those are the defaults already)
If that still doesn't work, then you'd need to read the server logs for any missing faces messages. Big change that you'll see a Validation Error: "Value is not valid" or perhaps a conversion error. To prevent those messages from being missed during ajax rendering, ensure that you're using <h:message> and/or <h:messages> the right way and that you also include them in the render of the <f:ajax>.
<h:selectOneRadio id="metalA" ...>
...
<f:ajax ... render="metalAmessage panelA" />
</h:selectOneRadio>
<h:message id="metalAmessage" for="metalA" />

JSF 2 f:ajax lifecycle problem

The problem is, that if a property is changed during an f:ajax request and a binded panelGroup should be newly created depending on that changed value, the old value is used.
This code will explain the problem.
Here is the backingbean TestBean:
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
public String getName(){
return first+" "+last;
}
public void setDynamicPanel(HtmlPanelGroup panel){ }
public HtmlPanelGroup getDynamicPanel(){
Application app = FacesContext.getCurrentInstance().getApplication();
HtmlPanelGroup component = (HtmlPanelGroup)app.createComponent(HtmlPanelGroup.COMPONENT_TYPE);
HtmlOutputLabel label1 = (HtmlOutputLabel)app.createComponent(HtmlOutputLabel.COMPONENT_TYPE);
label1.setValue(" --> "+getFirst()+" "+getLast());
component.getChildren().add(label1);
return component;
}
and now the jsf/facelet code:
<h:form id="form">
<h:panelGrid columns="1">
<h:inputText id="first" value="#{testBean.first}" />
<h:inputText id="last" value="#{testBean.last}" />
<h:commandButton value="Show">
<f:ajax execute="first last" render="name dyn" />
</h:commandButton>
</h:panelGrid>
<h:outputText id="name" value="#{testBean.name}" />
<h:panelGroup id="dyn" binding="#{testBean.dynamicPanel}" />
</h:form>
After the page was initially loaded the outputText and panelGroup shows both "null" as first and last. But after the button is pressed, the outputText is updated well, but the the panelgroup shows again only "null". This is due to the problem, that the "binded method" dynamicPanel is executed before the update of the first and last properties.
how can workaround this behaviour or what is wrong with my code?
If you add the attribute immediate="true" to your input elements, the values will be applied during the "Apply Request Values" phase, and hence be present before your action executes. You may or may not need the immediate attribute set to true on the commandButton as well.

Resources