Getting a null value passed to a Java function via BootsFaces datatable - jsf

I am trying to create a modal to send user-selected requests to an email address; however, I am having trouble getting the user-selected requests. I keep getting the null value passed through fooBean.setSelected(id).
Versions:
BootsFaces: 1.3.0
Java: 1.8.0
JSF: 2.0
Browser: Internet Explorer 11.2x
MCVE of thisThing.xhtml:
<b:dataTable value="#{fooBean.newRequests}"
var="foo"
onselect="ajax:fooBean.setSelected(id)"
ondeselect="ajax:fooBean.setSelected(id)"
selectedItems="row"
selection-mode="multiple"
selectedRows="#{foo.selected}"
saveState="false">
<b:dataTableColumn label="Select">
<b:selectBooleanCheckbox value="#{foo.selected}" />
</b:dataTableColumn>
<b:dataTableColumn label="Status" value="#{foo.status}" />
<b:dataTableColumn label="Request Number"
value="#{foo.requestNumber}"
data-type="string" />
<b:dataTableColumn label="ID" value="#{foo.id}" />
<b:dataTableColumn value="#{foo.storeName}"
label="Store Name" />
</b:dataTable>
MCVE of fooBean.java:
#ManagedBean(name="fooBean")
#ViewScoped
public class fooBean extends BeanBase implements Serializable {
private List<FooRecord> fooRecords = new ArrayList<FooRecord>();
private List<FooRecord> selectedFooRecords = new ArrayList<FooRecord>();
// ...
public void setSelected(String requestId) {
// This is not how I really do it, but it gives an idea
// with what I intend to do.
this.fooRecords.stream().filter(...).toggleSelection();
this.selectedFooRecords.stream().filter(...).toggleSelection();
}
}
Update:
I found out that I had the method called as getSelect instead of getSelected, so I fixed it and that part is done. I just remembered the real issue which is why a null parameter is being passed instead of the requestId. When I debug through the fooBean.getSelected(String requestId), it shows null being passed through as parameter. I have even tried:
<!-- Using varName.property -->
onselect="ajax:fooBean.setSelected(foo.id)"
<!-- Using just the property name -->
onselect="ajax:fooBean.setSelected(id)"
<!-- Using the loop variable -->
onselect="ajax:fooBean.setSelected(foo)"
Update 2:
How do I pass foo.id to the function?

There are only three parameters you can pass to the bean method:
The loop variable. In your example, that's foo. This parameter only works if the attribute selected-items is set to rows. Caveat: if you activate column or cell select mode, this parameter is still passed to the server, and it seems to be valid. Only that every attribute of this object is null.
typeOfSelection. This is either "row", "column" or "item". It determines what the user has selected. That, in turn, is determined by the attribute selected-items, so you hardly ever have to use this parameter. Just omit it from the parameter list.
indexes tells the JSF bean which rows, columns or cells have been selected. Note that in the first two cases this is either an individual number or - if multiple items have been selected - a comma separated list. If you've set selected-items="cell", indexes is a comma-separated list of objects containing the row and column index.
I suppose indexes or foo is the most useful parameter in your case.
Final remark: we'd like to add more flexibility, but this requires much more understanding of the internal API of JSF than even we have. Truth to tell, I'm not even sure we can pass arbitrary bean values without modifying the JSF engine itself. If anybody knows how to do that, please leave a message at our bug tracker.
Update:
Maybe the problem is caused by the modal. The modal is rendered at load time, but shown later, when the user has selected a row. Thing is, the modal isn't re-rendered automatically unless you add the content of the modal to the update attribute.

Related

Problem with Primefaces Autocomplete Component

I have a local Web Application developed with JDK 8, JSF 2.2 (implementation provided by JBoss), Spring 4.2 and Primefaces 6.2
In the application, I have one XHTML page for edit some fields of an object that is instance of a class named SiepRoEncabezado. One of those fields is an instance of SiepRpaPescador.
Until yesterday, I had one <p:selectOneMenu> contained in a form that is contained in a modal dialog in order to select one SiepRpaPescador object. The modal itself has 2 submit <p:commandButton> (one for save the changes and one for clean the form) and some aditional input fields.
The modal is developed in such manner that it closes automatiquely when the data is successfully edited after I push the save button.
The modal with the <p:selectOneMenu> worked fine, but due to the fact that there are too many select items to be handled in terms of memory usage, I was forced to replace the <p:selectOneMenu> by the following component:
<p:autoComplete dropdown="true"
id="rpaAutoComplete"
value="#{correccionROBean.tmpPescador}"
var="itemRpa"
itemLabel="#{itemRpa.nmPescador.concat(' ').concat(itemRpa.nmPaterno).concat(' ').concat(itemRpa.nmMaterno)}"
itemValue="#{itemRpa}"
completeMethod="#{correccionROBean.filtrarRpa}"
minQueryLength="4"
maxResults="10"
forceSelection="true" />
There, tmpPescador is a bean in the Managed Bean CorreccionROBean.
The complete method there works fine and the selection items are displayed as desired.
However, after I select one item in the <p:autoComplete> component and push the button to save the changes, it does nothing at all. Also, it does not display an error message and it does not throw any exception. What is worst, if I try to debug the action listener method in the button to save changes, it does nothing, it's like the action listener method is not called at all.
I put an <p:ajax> tag hoping to solve the problem with no avail:
<p:autoComplete dropdown="true"
id="rpaAutoComplete"
value="#{correccionROBean.tmpPescador}"
var="itemRpa"
itemLabel="#{itemRpa.nmPescador.concat(' ').concat(itemRpa.nmPaterno).concat(' ').concat(itemRpa.nmMaterno)}"
itemValue="#{itemRpa}"
completeMethod="#{correccionROBean.filtrarRpa}"
minQueryLength="4"
maxResults="10"
forceSelection="true">
<p:ajax event="itemSelect" listener="#{correccionROBean.onRpaSelect}" update="rpaAutoComplete"/>
</p:autoComplete>
Here, when I select one item, the listener method is not triggered. When I try to debug the method, it's like the method is not called at all.
Finally, when I push the save button without selecting an item in the <p:autoComplete>, then and only then, the action listener method in the save <p:commandButton> is triggered.
What may cause this behaviour?
Thanks in advance.
EDIT
I added the field immediate="true" to the autocomplete component and that triggered the ajax submit method, but still cannot execute the save button action listener method
SOLVED.
Refeer to Melloware's answer and my reply to that answer for more details.
For those that are unfamiliar with Converters:
The interface Converter, defined in JSF's API, allows to convert the data inputted and outputted to a Auto Complete component (and some other JSF UI components as well and their sub-classes). This interface has two methods:
public Object getAsObject(FacesContext context, UIComponent component, String value): This method must return an Object that is instance of the same class as the value defined in the value field of the Auto Complete component (in my case, it returns a SiepRpaPescador object). You must handle exceptions in this method as it is called automatiquely in two situations:
When you input characters in the Auto Complete text field and the number of inputted characters are equal or greater than the value defined in the minQueryLength field of the Auto Complete component or it's default value if not specified (in my case, when I input 4 characters or more). In this case, the value parameter in this method will be the String you inputted. Notice that if you enabled dropdown (dropdown="true")as in my case and you push the dropdown button, this method is NOT called.
When you submit the form. In this case, the value will be the value of the labelValue field in the Auto Complete component (in my case, #{itemRpa.nrFolio}) converted to String using the toString method defined in the class that value is instance of (#{itemRpa.nrFolio} is an Integer, so the value will be converted using the Integer class' own implementation of toString()) or in the Object class if no implementation of toString() is defined.
public String getAsString(FacesContext context, UIComponent component, Object value): This method must return a String representation of the label value of each item to be displayed in the Auto Complete component. The parameter value is an instance of the same class as the value defined in the labelValue field of the Auto Complete component. This method is called automatiquely when the items are displayed (it doesn't matter if you inputted characters in the Auto Complete's text field or pressed the dropdown button if present) and it's called as many times as defined in the field maxResults (in my case, 10) in the Auto Complete, using the objects obtained from the returned list of the completeMethod (in my case, from each object in the list obtained from #{correccionROBean.filtrarRpa}, this method obtains it's nrFolio, as I stablished in the labelValue field of the AutoComplete).
I hope this helps to you all

How to enable/disable SelectManyCheck based on method returned value?

Can anyone help in getting the returned value from a method in a bean to enable/disable components through ajax ex(SelectManyCheckbox, textbox ..etc) in a jsf page based on the selected radio button. First the SelectManyCheckbox component should disabled and based on the selection it should be enabled. The method is checking whether the value is "A" or "B" and it will return the value. Here is the method code:
private String firstToggle="True";
public String firstToggle(AjaxBehaviorEvent e){
firstToggle=requestType;
System.out.print("Value "+firstToggle);
return firstToggle;
}
Here it will return A or B.
However, When I am trying to disable/enable the SelectManyCheckbox component, the SelectManyCheckbox component won't pickup the value in the disabled attribute when the Value is B.
<p:selectManyCheckbox id="Cars" value="#{user.xyz}" layout="pageDirection" disabled="#{user.firstToggle}=='B')">
<f:selectItems value="#{user.xyz}" var="xyz" itemLabel="#{xyz}" itemValue="#{xyz}"/>
</p:selectManyCheckbox>
So please help
The reason why you don't want to use firstToggle as a Boolean/boolean as suggested per Tiny's comment is really not clear! However, if you have your reasons to do that or you are handling the firstToggle in another place and you want to use it's value 'A' or 'B' in two different p:selectManyCheckbox ("where one should be enabled and the other should be disabled" as you said), then you could implement the first one with disabled="#{user.firstToggle eq 'B'}" and the second one with disabled="#{user.firstToggle eq 'A'}" or disabled="#{user.firstToggle ne 'B'}".
In case, you want to work with Boolean/boolean following Tiny's suggestion, then just use the first one with disabled="#{user.firstToggle}" and the second one with disabled="#{not user.firstToggle}"
NB: Also private String firstToggle="True"; even if it's correct, it doesn't make any sense, either use Boolean/boolean and assigne true to it, or assign 'A' which seems to mean false regarding your context.

PostValidate datatable list values

I have a list of items that are being displayed using a h:datatable like so:
<p:dataTable value="#{myBean.instructorsList}" var="ins">
<p:column headerText="Name">
<h:inputText value="#{ins.name}"/>
</p:column>
</p:dataTable>
My spec is that I cannot allow a instructor to have the same name as another insturctor. So I need to have access to all the entire instructorList when it is submitted. I have attempted to validate using a postValidate f:event however due to the JSF lifecycle it does not update the model values till after the postValidation phase.
My attempt
<f:event listener="#{myBean.myMethod}" type="postValidate" />
Backing code
private List<instructors> instructorsList;
public void myMethod(ComponentSystemEvent event) {
// Attempting to use the instructorsList with new values. However, this
// is at the wrong stage
}
How would I write a validator to accomplish checking for duplicate instructor names?
Within a listener, use the getSubmittedValue method on HtmlInputText to pull the value straight off the component. Also, postValidate is semantically late to call problems on the validation process. Use the preValidate method instead. Altogether, you should have
public void myMethod(ComponentSystemEvent event) {
HtmlInputText txtBox = (HtmlInputText)event.getComponent();
String theValue = txtBox.getSubmittedValue().toString(); //the value in the textbox
//based on the outcome of your validation, you also want to do the following
FacesContext.getCurrentInstance().setValidationFailed(); //flag the entire request as failing validation
txtBox.setValid(false); //mark the component as failing validation
}
EDIT: This approach is heavily hinged on the premise that your users will be submitting only one row at a time. In the case where the entire table/column is being submitted in one request, you'll find that evaluating each input field on by one doesn't do much to prevent race conditions; you should be looking toward cross-field validation instead.
EDIT 2: I was mistaken, you cannot have race conditions when the event listener is being called. The listener is executed for each row, sequentially. This allows you to safely check each row (perhaps against a Map, for duplicates), without worrying about race conditions.

EL function not found in validator attribute

El function cannot be found using within validator attribute, which is dependent on dynamic or repeated values?
Function 'el:min' not found
#{el:min(a + b, c)}
<f:validateLongRange maximum="#{el:min(foo.bar, 10)}"/>
Just printing out the value is working where it is not working in the validator.
The error message Function 'el:min' not found was so misleading.
The problem was never the construct but it was an underlying NullPointerException on the nested property.
Since in one case the value was depending on a different component selection it was updated via ajax and the default value was null. Since the default value was null this misleading exception was thrown.
The value was a nested property, so it was not catched within the el function
Solution: disable the validator on default
<o:validator validatorId="javax.faces.LongRange" maximum="#{el:min(foo.bar, 10)}"
disabled="#{foo eq null}"/>
This construct should work just fine. The problem is most likely the scope of the variables which you've there and the timing (i.e. when do you need them? when are they "behind the scenes" changed?).
You need to understand that taghandlers like <f:xxx> run during view build time (like JSTL <c:xxx>). So their attribtues are resolved during view build time and would be filled with bean's default values. Perhaps you're performing some business logic on them while submitting the form and expecting that they would be reflected into the taghandler attribute. But this is not true. They were already evaluated during view build time and won't re-evaluate the values during processing the form submit.
If this is indeed the case, then you've basically the same problem which is already outlined and answered with various possible solutions in this answer: How to set converter properties for each row of a datatable? Apart form homegrowing a Validator for this, you could use OmniFaces <o:validator> for this:
<o:validator validatorId="javax.faces.LongRange" maximum="#{el:min(a + b, c)}" />

h:outputtext issue in passing attributes

I have a search screen where I search for a customer id and it consumes a webservice a returns list of objects. I display the results in a datatable.For a specific field , I have a method which provides the value based on a key value in each row of the list being iterated. The key value is productID. I set that in a bean named output. In the getCustomerValue method I call the method which provides the relevant value by passing the value of "productID". I use the below listed code to do the same.
<h:outputText id="customerID" binding="#{myBean.output}" value="#{customerBean.customervalue}">
<f:attribute name="myID" value="#{item2.customerService.productID}"/>
</h:outputText>
The value gets displayed properly when the page is loaded. I have hyperlink in the same page which basically calls the same webservice and renders the same page.But this time around all the values except the value listed above is being displayed. When I print the value of attribute "item2.customerService.productID" in the method "customervalue" , it is displayed as null.I am not sure why this value isn't being passed.
You're displaying this in a <h:dataTable>. The <f:attribute> is specific to the component itself, not to its generated HTML output. The <f:attribute> is evaluated during view build time, not during the view render time. At the moment JSF builds the view, the #{item2} is not present in the scope. It's only present in the scope when JSF renders the view.
You need to look for the solution by alternate means. It's unclear what JSF version you're using, but based on your question you're using JSF 1.2 (in the future questions, please explicitly mention the JSF impl/version you're using; in JSF 2.0 a lot of things can be done differently and much more elegantly).
My answer on your previous question of Passing parameters to a method in h:outputtext tag should be the best answer to your current problem. This is apparently not an option somehow. In that case, there are at least 3 alternative ways:
Move the property to the class behind #{item2}:
<h:outputText value="#{item2.customervalue}">
You've in there instant access to the customerservice property.
Get the current item inside the getter by evaluating EL programmatically:
public String getCustomervalue() {
FacesContext context = FacesContext.getCurrentInstance();
Long productID = context.getApplication().evaluateExpressionGet(context, "#{item2.customerService.productID}", Long.class);
// ...
}
(I assume that productID is a Long)
Bind the datatable's value to a DataModel:
private DataModel<Item2> items;
with
<h:dataTable value="#{customerBean.items}" var="item2">
and
public String getCustomervalue() {
Item2 item2 = items.getRowData();
// ...
}

Resources