I'm trying to add dynamically an item in a SelectOneMenu('Raison') within an editable dataTable using primeFaces.
My code for XHTML :
<p:column>
<!-- ... -->
<!-- inside p:cellEditor -->
<p:selectOneMenu id="selectmenu" value="#{o.rais.raison}"effect="fold" filter="true" filterMatchMode="startsWith" style="width:100%">
<f:selectItems value="#{vueRecupSurveil.raisonsBlocakge}" />
<f:selectItem ItemValue="#{vueRecupSurveil.newRaisonBlocakge}" itemLabel="Ajouter une raison"/>
<p:ajax listener="#{vueRecupSurveil.ajoutNewRaison}"/>
</p:selectOneMenu>
<!-- ... -->
</p:column>
My code for bean:
#ManagedBean(name="vueRecupSurveil")
#ViewScoped
public class VueRecupSurveil implements Serializable {
private String newRaisonBlocakge="";
private List<String> raisonsBlocakge = new ArrayList<String>();
#PostConstruct
public void init() {
// initialize list
}
public void ajoutNewRaison(AjaxBehaviorEvent event) {
if (newRaisonBlocakge.equals(((UIInput) event.getComponent()).getValue())) {
RequestContext ajax = RequestContext.getCurrentInstance();
ajax.update("addNewRaisonDialog");
ajax.execute("PF('widget_addNewRaisonDialog').show()");
}
}
}
My problem is when i click in the icon edit, in options in datatable, and change the raison, then click back in cancel icon, i can't get the initial value displayed; when i remove the ajax code for my xhtml it works.
The dialog should display the new data via ajax.
Related
I am trying to make 4 dependent / cascading selection components. In this question, the selection component happens to be a <h:selectOneMenu>, but this is of course applicable on any other kind of selection component extending from UISelectOne/UISelectMany superclass, such as <h:selectManyCheckbox> or PrimeFaces <p:selectCheckboxMenu>, <p:selectManyMenu>, etc.
When the user chooses an item from the first menu, the second menu will show dependent data and when the user chooses item from the second one , the third one will show dependent data and so on.
The user will see items on the first menu only and the other ones will be blank. If he chooses an item on the first menu the second one will show data but the third and the fourth will remain blank, and so on. The user must eventually choose entries from all the 4 menus.
<h:selectOneMenu id="first" value="#{nodes.selectState}">
<f:selectItems value="#{nodes.stateList}"/>
<f:ajax render="second">
</h:selectOneMenu>
<h:selectOneMenu id="second" value="#{nodes.selectCity}">
<f:selectItems value="#{nodes.cityList}"/>
<f:ajax render="third">
</h:selectOneMenu>
<h:selectOneMenu id="third" value="#{nodes.selectRegion}">
<f:selectItems value="#{nodes.regionList}"/>
<f:ajax render="fourth">
</h:selectOneMenu>
<h:selectOneMenu id="fourth" value="#{nodes.selectStation}">
<f:selectItems value="#{nodes.stationList}"/>
</h:selectOneMenu>
Nodes bean
private String selectState; //+setters, getters
private String selectCity; //+setters, getters
private String selectRegion; //+setters, getters
private String selectStation; //+setters, getters
private List<SelectItem> stateList; //+setters, getters
private List<SelectItem> cityList; //+setters, getters
private List<SelectItem> regionList; //+setters, getters
private List<SelectItem> stationList; //+setters, getters
public getStateList(){
stateList= new ArrayList<SelectItem>();
stateList.add(new SelectItem("A"));
}
public getCityList(){
CityList= new ArrayList<SelectItem>();
if(selectState.equals("A")){
CityList.add(new SelectItem("B"));
}
}
public getRegionList(){
RegionList= new ArrayList<SelectItem>();
if(selectCity.equals("B")){
RegionList.add(new SelectItem("C"));
}
}
public getStationList(){
StationList= new ArrayList<SelectItem>();
if(selectRegion.equals("C")){
StationList.add(new SelectItem("D"));
}
}
It's only working for the first 2 menus. The other 2 menus get null values.
Put the bean in the view scope and get rid of any business logic in getter methods.
The bean must be placed in the view scope so that all previous selections and new available items are remembered for subsequent postbacks, otherwise things will fail when JSF needs to validate the selected item against the list of available items which was prepopulated in a previous selection, or when e.g. rendered attribute depends on a condition which was only set in a previous request.
The getter methods may not contain any business logic as they will also be invoked during among others the validations phase. You should use <f:ajax listener>/<p:ajax listener> to perform business logic based on a change. You should in the listener method also explicitly clear out selected values of child selection components. You can use <f:ajax render>/<p:ajax update> to update the contents of child selection components.
Thus, so:
<h:selectOneMenu id="state" value="#{nodes.selectedState}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableStates}" />
<f:ajax listener="#{nodes.changeState}" render="city region station" />
</h:selectOneMenu>
<h:selectOneMenu id="city" value="#{nodes.selectedCity}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableCities}" />
<f:ajax listener="#{nodes.changeCity}" render="region station" />
</h:selectOneMenu>
<h:selectOneMenu id="region" value="#{nodes.selectedRegion}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableRegions}" />
<f:ajax listener="#{nodes.changeRegion}" render="station" />
</h:selectOneMenu>
<h:selectOneMenu id="station" value="#{nodes.selectedStation}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableStations}" />
</h:selectOneMenu>
with
#Named
#ViewScoped
public class Nodes {
private String selectedState; // getter+setter
private String selectedCity; // getter+setter
private String selectedRegion; // getter+setter
private String selectedStation; // getter+setter
private List<SelectItem> availableStates; // getter (no setter necessary!)
private List<SelectItem> availableCities; // getter (no setter necessary!)
private List<SelectItem> availableRegions; // getter (no setter necessary!)
private List<SelectItem> availableStations; // getter (no setter necessary!)
#EJB
private SomeService someService;
#PostConstruct
public void init() {
availableStates = someService.listStates();
}
public void changeState(AjaxBehaviorEvent event) {
availableCities = someService.listCities(selectedState);
selectedCity = selectedRegion = selectedStation = null;
availableRegions = availableStations = null;
}
public void changeCity(AjaxBehaviorEvent event) {
availableRegions = someService.listRegions(selectedCity);
selectedRegion = selectedStation = null;
availableStations = null;
}
public void changeRegion(AjaxBehaviorEvent event) {
availableStations = someService.listStations(selectedRegion);
selectedStation = null;
}
// Generate necessary getters+setters here. You should not change them.
}
See also:
How to choose the right bean scope?
Best way to add a "nothing selected" option to a selectOneMenu in JSF
How to populate options of h:selectOneMenu from database?
When to use valueChangeListener or f:ajax listener?
Why JSF calls getters multiple times
Try this, it may help you
By using the --Select City-- , --Select Region--, --Select Station-- to avoid the null pointer Exception.
public getStateList(){
stateList= new ArrayList<SelectItem>();
stateList.add(new SelectItem("A"));
}
public getCityList(){
CityList= new ArrayList<SelectItem>();
if(selectState.equals("A"))
{
CityList.add(new SelectItem("B"));
}
else
{
CityList.add(new SelectItem("--Select City--"));
selectCity = "--Select City--";
}
public getRegionList(){
RegionList= new ArrayList<SelectItem>();
if(selectCity.equals("B"))
{
RegionList.add(new SelectItem("C"));
}
else
{
RegionList.add(new SelectItem("--Select Region--"));
selectRegion = "--Select Region--";
}
}
public getStationList(){
StationList= new ArrayList<SelectItem>();
if(selectRegion.equals("C"))
{
StationList.add(new SelectItem("D"));
}
else
{
StationList.add(new SelectItem("Select Station"));
selectStation = "--Select Station--";
}
}
There is a typo error in your code. For third menu you have given id name as "first" instead of "third". May be its because of that problem.
You are facing this issue because you have twice id="first".
Fix this and it should work.
EDIT
If have found the solution, but I need help understanding it. Questions are listed below
Whenever I try to reset the value of a conditionally rendered field, it reverts to a previously entered value instead of a null value.
The following is an explanation of the unexpected behavior:
checkbox is true --> textfield is rendered
enter 'test' into textfield
uncheck checkbox --> texfield is not rendered
click reset button --> checkbox value = true && textfield value = null
expected --> inputtextfield with null value
actual --> inputtextfield with 'test' value
Simplified example
Form
<h:form>
<p:selectBooleanCheckbox value="#{testController.inputTextRendered}">
<p:ajax process="#form" update="#form"/>
</p:selectBooleanCheckbox>
<h:inputText value="#{testController.inputText}" rendered="#{testController.inputTextRendered}"/>
<p:commandButton process="#this" update="#form" action="#{testController.reset}"/>
</h:form>
Controller
#Named
#ViewScoped
public class TestController implements Serializable {
private boolean inputTextRendered = true;
private String inputText;
public void reset() {
setInputTextRendered(true);
setInputText(null);
}
public boolean isInputTextRendered() {
return inputTextRendered;
}
public void setInputTextRendered(boolean inputTextRendered) {
this.inputTextRendered = inputTextRendered;
}
public String getInputText() {
return inputText;
}
public void setInputText(String inputText) {
this.inputText = inputText;
}
}
side notes
it's the omnifaces #ViewScoped annotation
the process #form on the checkbox is necessary, because deselecting the checkbox should not reset the value of the textfield (only the button should reset it)
Solution
This code works
<h:form id="testForm">
<p:selectBooleanCheckbox value="#{testController.inputTextRendered}">
<p:ajax process="#form" update="#form"/>
</p:selectBooleanCheckbox>
<h:inputText value="#{testController.inputText}" rendered="#{testController.inputTextRendered}"/>
<p:commandButton process="#this" update="#form" actionListener="#{testController.reset}">
<p:resetInput target="#form"/>
</p:commandButton>
</h:form>
But this raises some questions:
Why do I need to add a p:resetInput when no validation errors even occur?
Why do I need to use the actionListener attribute instead of the action attribute?
In JSF 2.X, can I render a component only when the validation success?
In my application I have many fields that must be filled. These data can be imported from a WebService through a search key.
When the user enter a valid search key the system searches the other fields and render them with the new values. But when the user enter a nonexistent key (or any other validation error) the server generates a validation error but still renders the fields, thus losing any data that there were filled.
What I need is that the user can perform the query and that if the query does not return results, this does not affect any data that he has already entered.
Below is a code example. Thus, if the user has filled in the fields inside updateThisOnSuccess and just after making an attempt to query without success, the value that is filled in is not lost.
<h:inputText value="#{controller.searchWebService}" >
<f:ajax execute="#this" render="updateThisOnSuccess messages" />
</h:inputText>
<h:panelGroup id="updateThisOnSuccess">
<h:inputText value="#{controller.field}" />
<!-- other fields -->
</h:panelGroup>
Submit the field values to run the search also does not seem an option as this will cause need to validate the fields inside updateThisOnSuccess.
Note: I saw the answer given by #BalusC to a similar question, but this is different from what I'm wondering why, in that case, foo-holder is always rendered and foo is conditioning. It's not my case, since this approach would make the controls do not appear when the validation fails.
Try this
<h:panelGroup id="updateThisOnSuccess">
<ui:fragment rendered="#{not facesContext.validationFailed}">
<h:inputText value="#{controller.field}" />
<!-- other fields -->
</ui:fragment>
</h:panelGroup>
Plaase try this. The requirements are that you must implement model validations with Bean Validation and the search field must implement JSF validation if required.
If you write "123456" then data is returned, else nothing is returned and a message is printed.
The backing bean:
#Named
#ViewScoped
public class yourBean implements Serializable{
private static final long serialVersionUID = 1L;
#Size(min=2)
private String field01;
private String searchWebService;
public void saveF(){
System.out.println("save");
}
public void searchWebServiceF(){
Boolean successWS = ("123456").equals(this.searchWebService);
if(successWS){
this.setField01("WS data");
}else{
FacesContext.getCurrentInstance().
addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "WS fails", ""));
}
}
public String getSearchWebService() {
return searchWebService;
}
public void setSearchWebService(String searchWebService) {
this.searchWebService = searchWebService;
}
public String getField01() {
return field01;
}
public void setField01(String field01) {
this.field01 = field01;
}
}
In your page:
<h:form id="form01">
<h:messages id="message"/>
<h:inputText id="wsid" value="#{pruebasBorradorBean.searchWebService}">
<f:validateLength maximum="6"/>
<f:ajax execute="#form" render="#form" listener="#{pruebasBorradorBean.searchWebServiceF()}" />
</h:inputText>
<h:panelGroup id="thedata">
<h:inputText value="#{pruebasBorradorBean.field01}">
<f:validateBean disabled="#{param['javax.faces.source']!='form01:save'}"/>
</h:inputText>
<!-- other fields -->
</h:panelGroup>
<h:commandButton id="save" value="submit">
<f:ajax render="thedata message" execute="#this thedata" listener="#{pruebasBorradorBean.saveF()}"/>
</h:commandButton>
</h:form>
You can change the components that will be processed in render phase changing the Collection at getRenderIds() of PartialViewContext. According to documentation this Collection is mutable.
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().remove("formName:updateThisOnSuccess");
To test this solution, I used this controller:
#Named
#ViewScoped
public class Controller implements Serializable {
private static final long serialVersionUID = 1L;
private final static List<String> LIST_VALID_WEB_SERVICE_SEARCHS =
Arrays.asList(new String[] {"foo", "bar"});
private String webServiceParameter;
private Integer field01;
public void searchWebService() {
if (LIST_VALID_WEB_SERVICE_SEARCHS.contains(getWebServiceParameter())) {
setField01(123);
} else {
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.getPartialViewContext().getRenderIds().remove("formFields");
FacesMessage facesMessage = new FacesMessage("Search not found in WebService.");
facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
facesContext.addMessage("formName:searchWebService", facesMessage);
}
}
public void submit() {
System.out.println("submitted");
}
// Getters and Setters
}
And used this view:
<h:form id="formSearch">
<h:inputText id="webServiceParameter" value="#{controller.webServiceParameter}">
<f:ajax execute="#this" render="formFields messages" listener="#{controller.searchWebService}" />
</h:inputText><br />
</h:form>
<h:form id="formFields">
<h:inputText id="field01" value="#{controller.field01}" required="true">
<f:validateLongRange minimum="2" maximum="345" />
</h:inputText><br />
<!-- other fields -->
<h:commandButton value="submit" action="#{controller.submit}">
<f:ajax render="#form messages" execute="#form" />
</h:commandButton>
</h:form>
<h:messages id="messages" />
You can do something like that:
<f:ajax execute="#this" render="#{controller.success} message"/>
where success is a String attribute that will be empty if the WS fails and will be "updateThisOnSuccess" if not .
Or you could get rid of the JSF validation mechanism for informing the user the WS has failed. Think of it, it is not really a validation of the Model. You could draw an icon beside the WS Id field in red color or something similar using a boolean flag attribute in the backing bean.
I have the following xhtml, validator, and managedBean:
<h:form id="form">
<ui:repeat var="item" value="#{myBean.usersEmail}" varStatus="status">
<p:inputText id="userEmail" value="#{item.email}">
<f:validator validatorId="MyValidator"/>
</p:inputText>
<p:commandButton value="++++" update=":form" action="#{myBean.addEmail()}" />
</ui:repeat>
</h:form>
#FacesValidator("MyValidator")
public class ValidationClass extends Validator {
#Override
public void validate(FacesContext ctx, UIComponent component, Object value) throws ValidatorException {
String email = value.toString();
EmailValidator validator = EmailValidator.getInstance();
if(StringUtils.isNotBlank(email) && !validator.isValid(email)) {
FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_ERROR);
message.setSummary("Email is not valid.");
message.setDetail("Email is not valid.");
ctx.addMessage("userEmail", message);
throw new ValidatorException(message);
}
}
}
#ManagedBean
public class MyBean{
#Getter
#Setter
List<UserEmail> usersEmail = new ArrayList<UserEmail>();
public void addEmail(){
usersEmail.add(new UserEmail());
}
}
public class UserEmail{
#Getter
#Setter
String email = "";
}
The email addition works fines until the first validation fail.
When this happens, all inputText components show the same values.
For example, first I add "user1#gmail.com", this works ok.
Then I add "user2#gmail.com", this also works ok.
Then I change "user1#gmail.com" to "", this throws a validation exception, which is shown on the screen, and everything is still ok.
But then I correct the "" with "user3#gmail.com" and submit, this time all inputText start showing "user2#gmail.com", even when I add a new InputText, which also shows "user2#gmail.com".
It seems that when the validation fail, all components inside ui:repeat get bound to the value of the last item. Any thoughts?
I changed my implementation to use the c:forEach tag from JSTL and now it's working fine, even on Mojarra 2.2.6, here it's what I did:
<c:forEach var="item" items="#{myBean.usersEmail}" varStatus="status">
<p:inputText id="id${status.index}" value="${item.email}" validator="MyValidator" />
<p:message for="id${status.index}" />
<p:commandButton value="+" update=":form" action="#{myBean.addEmail()}" />
</c:forEach>
So I have a bunch of InputText-Boxes, which express some kind of customer data (customer-id, firstname, lastname, etc.)
Now i want to auto-fill (with the corresponding mysql data) all InputText-Boxes, when I enter a valid customer-id.
<h:inputText id = "customer_id" value="#{reservationHandler.customer.customer_id}"/>
I guess I'll have to use Ajax to pass the current customer-id to the managed bean, but how do i do this? And how do i fill the other InputText-Boxes?
Thanks in advance.
This task can be done using the <f:ajax> tag that comes with JSF 2 and is good illustrated in this tutorial. The first example shows you how to accomplish the task at hand. Another advice would be to use #ViewScoped annotation for your managed bean as explained by BalusC blog post: Communication in JSF 2.0 - Managed bean scopes.
With all this info and the managed bean you're using, a demo could be:
Customer class
public class Customer {
private int customer_id;
private String name;
//constructor, getters and setters...
}
ReservationHandler managed bean
#ManagedBean
#ViewScoped
public class ReservationHandler {
private Customer customer;
//this EJB will retrieve the Customer data
//if you don't have it like this, then use your own
//custom CustomerService class/implementation to retrieve the data from dabatase
#EJB
private CustomerService customerService;
//constructor, getters and setters...
#PostConstruct
public void init() {
customer = new Customer();
}
public void showCustomerDataListener(AjaxBehaviorEvent event) {
Customer customerFromDB =
customerService.getCustomer(customer.getCustomer_id());
if (customerFromDB != null) {
customer = customerFromDB;
}
}
}
Customer Facelets view (just the relevant code)
<h:form>
<h:outputText value="Customer ID:" />
<h:inputText id="customer_id"
value="#{reservationHandler.customer.customer_id}">
<f:ajax event="blur"
listener="#{reservationHandler.showCustomerDataListener}"
render="customer_name" />
</h:inputText>
<h:outputText value="Customer name:" />
<h:inputText id="customer_name"
value="#{reservationHandler.customer.name}" />
</h:form>
In case your Customer class posses more than 1 attribute, you have two options:
Set the ID of every input in the render attribute of the <f:ajax> component, but this is very naive.
Group all the components with an UIContainer and render this UIContainer. Example given assuming that the Customer class has an additional private String address attribute:
<h:form>
<h:outputText value="Customer ID:" />
<h:inputText id="customer_id"
value="#{reservationHandler.customer.customer_id}">
<f:ajax event="blur"
listener="#{reservationHandler.showCustomerDataListener}"
render="customerData" />
</h:inputText>
<h:panelGrid id="customerData" columns="2">
<h:outputText value="Customer name:" />
<h:inputText id="customer_name"
value="#{reservationHandler.customer.name}" />
<h:outputText value="Address:" />
<h:inputText id="customer_address"
value="#{reservationHandler.customer.address}" />
</h:panelGrid>
</h:form>
To know the supported events that cna be applied in <f:ajax event="what_to_write_here">, refer to f:ajax JSF Core Tag Reference, event tag attribute description:
The event that will invoke Ajax requests, for example "click", "change", "blur", "keypress", etc. The event must be supported by the component(s) that have Ajax behavior enabled. Note: the event name is the same as the DOM event name without the "on" prefix, so for the "onclick" event the correct event name is "click". The "action" event is supported for command components such as h:commandButton and h:commandLink, and the "valueChange" event is supported for input components such as h:inputText and h:selectOneMenu.