rerender component based on selectOneMenu onchange - jsf

Maybe someone could clear this up for me.
I have an h:selectOneMenu that should do something as soon as a user selects something from the list and then rerender a link based on the results.
<h:selectOneMenu value="#{backingBean.itemSelected}" valueChangeListener="#{backingBean.methodThatDoesStuff}" onchange="submit()" >
<s:selectItems var="_items" value="#{backingBean.itemList}"
label="#{_items.Name}" />
<s:convertEntity />
</h:selectOneMenu>
My assumption was that when the user selected an item it would populate a field in the backing bean and then run the methodThatDoesStuff.
But that isn't happening. backingBean.itemSelected isn't set until AFTER the method is called. I can get the event and use it directly in the method:
public void methodThatDoesStuff(ValueChangeEvent event){
Item item = (Item)event.getNewValue();
.... do stuff, set a flag to display link later
}
This works.
But this leaves my aComponent set to the previous value, so the 1st time it will be null, then will change the settings based on the last itemSelected(so I'm always one behind:
public void methodThatDoesStuff(){
Item item = this.itemSelected;
.... do stuff, set a flag to display link later
}
I've tried:
<h:selectOneMenu value="#{backingBean.itemSelected}" >
<s:selectItems var="_items" value="#{backingBean.itemList}" label="#{_items.Name}" />
<a:support event="onchange" ajaxSingle="true" reRender="aComponent" action="#{backingBean.methodThatDoesStuff}" />
<s:convertEntity />
</h:selectOneMenu>
But that doesn't work whether I use ValueChangeEvent or not.
Is using the event object the only way to get the currently selected item?
This is JSF 1.2 and Seam 2

Set attribute immediate="true" on your selectOneMenu.

Related

Updating form component in ui:repeat

I have an ajax call that updates a value on my dto and should update the form to stop rendering a certain component. When mode of trial is changed, it should update a java value (which is working correctly and also stop the rendering on the time limit field.
If update="#form" is set, the rendering works correctly however all values are cleared from the form as well which is not acceptable. If I try to target the update at the id of the field, it doesn't work, I assume this has something to do with the repeat. The id of the component when inspecting the web page is 'offencesForm:offences:0:statutoryTimeLimit' however updating that does not work either.
Any help updating the rendering of the component without clearing the values of the rest of the form, would be great.
The dropdown that is change to trigger the method which is hit.
<ui:repeat id="offences" value="#{casefileDto.offences}" var="_offence" varStatus="n">
<!-- Update Time Limit value in dto & update component to render statutoryTimeLimit by modeOfTrial value -->
<ecis:field label="Mode of trial" starred="true" help="#{label['casefile.help.modeOfTrial']}" helpLayout="hover">
<p:selectOneMenu value="#{_offence.modeOfTrial}"
converter="omnifaces.SelectItemsIndexConverter"
required="${param['skipValidation'] == null}" requiredMessage="Please select the mode of trial">
<f:selectItem noSelectionOption="true" itemLabel="" itemValue="#{null}"/>
<f:selectItems value="#{conditionCache.modeOfTrialConditions}" />
<p:ajax event="change" update="statutoryTimeLimit" listener="#{_offence.onTrialModeSelect()}"/>
</p:selectOneMenu>
</ecis:field>
<!-- Render based off modeOfTrial values -->
<ecis:field id="statutoryTimeLimit" label="Statutory time limit expires on" rendered="#{supplementary and _offence.timeLimitRequired()}"
help="Calculated from 'Date of offence' plus six months." helpLayout="hover">
<h:outputText value="#{_offence.statutoryTimeLimit}" >
<f:convertDateTime pattern="#{cvCache.dateFormat}" />
</h:outputText>
</ecis:field>
</ui:repeat>
Java code
public boolean timeLimitRequired() {
return modeOfTrial != null && modeOfTrial.getValue().contains("(SST)");
}
public void onTrialModeSelect() {
if (getModeOfTrial().toString().equals("Not suitable for summary trial (NSST)")) {
setStatutoryTimeLimit(null);
} else {
onOffenceDateSelect();
}
}
Any help would be much appreciated.

Primefaces calendar does not set the date

I have yet another misunderstanding of Primefaces work logic.
For now <p:calendar> component cannot set selected date when I press submit button.
I see that it successfully enters into actionListener method, but date value is null there.
First of all I tried create my calendar using standard PF example. It looked too simple and I thought according this that the component should call value setter when the user select the date or submit the form. But it did it neither in the first nor in the second case.
Well, I opened Google and found a few posts:
Primefaces Calendar Setting Date Value in Backing Bean
p:calendar value not set in backing bean
I ensured that my calendar is located between <h:form></h:form> tags. Also I tried to add process="#this" and process=":beginDateForm :endDateForm #this", where beginDateForm and endDateForm are forms contained <p:calendar> components.
Also I found the post and tried to create SelectEvent listener method:
private void changeDate(SelectEvent event) {
beginDate = (Date) event.getObject();
}
But unsuccessfully.
I also tried to change Date using valueChangeListener:
<h:form id="beginDateForm">
<p:calendar id="passBeginDate" valueChangeListener="#{territoryFormBean.changeDate}" mode="popup" readonly="true" pattern="dd.MM.yyyy" showOn="button" value="#{territoryFormBean.beginDate}" />
</h:form>
Of course I changed event to ValueChangeEvent.
After that I moved <p:calendar> and <p:commandButton> components into the same <h:form> and tried two different process values process="passTerrForm:passBeginDate passTerrForm:passEndDate #this" and process="#form #this" and process="#form"
In the last case button does not trigger even the listener method.
My current components are:
<p:commandButton value="Search" id="searchPassButton" actionListener="#{territoryFormBean.search}" update=":passTerrForm:territoryTable" process="passTerrForm:passBeginDate passTerrForm:passEndDate #this" partialSubmit="true" />
<p:column>
<p:calendar id="passBeginDate" mode="popup" readonly="true" pattern="dd.MM.yyyy" showOn="button" value="#{territoryFormBean.beginDate}" />
</p:column>
<p:column>
<p:calendar id="passEndDate" mode="popup" readonly="true" pattern="dd.MM.yyyy" showOn="button" value="#{territoryFormBean.endDate}" />
</p:column>
Guys, could you please suggest anything else. Or probably you can see what is wrong in my code.
I cannot understand why the component does not call setter.
Well, guys, I found my mistake. Stupid mistake. Yet another re-read the PF documentation showed that using readonly parameter is incorrect for my goals. I wanted to prevent the manual date input directly into <p:calendar> text field. But according the documentation:
readonly -
Flag indicating that this input element will prevent changes by the
user
But I need readonlyInput parameter which
Makes input text of a popup calendar readonly.
So the second parameter prevents input, while the first one prevents changes totally.
Thank you for your help.
Trying to make an analogy with a code that I have, I think that putting the button into the same form as the calendar, and omitting the 'process' and 'partialSubmit' should work:
<h:form id="beginDateForm">
<p:calendar id="passBeginDate" valueChangeListener="#{territoryFormBean.changeDate}" mode="popup" readonly="true" pattern="dd.MM.yyyy" showOn="button" value="#{territoryFormBean.beginDate}" />
<p:commandButton value="Search" id="searchPassButton" actionListener="#{territoryFormBean.search}" update=":passTerrForm:territoryTable" />
</h:form>
I hope it helps!
JSF Code:
<p:calendar id="from" value="#{pageBean.event.startDate}" pattern="MM/dd/yyyy hh:mm:ss a" timeZone="GMT-4"/>
<p:commandButton id="addButton" value="Save" actionListener="#{pageBean.addEvent}" update=":#form"/>
You can write an addEvent method in the Bean where you need to add these calendar event to an eventModel and save it. This helps you to set the date and you can retrieve it as and when you need.
Java Code:
private ScheduleModel eventModel;
private ScheduleEvent event = new DefaultScheduleEvent();
public String addEvent(ActionEvent actionEvent) {
if(event.getId() == null){
eventModel.addEvent(event);
}
}
Hope this helps!!

How to pass the selected values from selectcheckBoxMenu to my bean?

I'm using JSF 2.2.8 and primefaces 6.0, and i have a selectCheckBoxMenu i want to retrieve the selected values in my bean.
The selectCheckboxMenu is filled from the database but when i select the attributes and I save nothing happens it does not call the save function
Here is my selectCheckBoxMenu
<p:outputLabel for="ressource" value="Ressource"/>
<h:panelGroup >
<p:selectCheckboxMenu id="ressource" label="Ressource" value="#{affectationBean.selectedRessource}" multiple="true">
<f:selectItems value="#{affectationBean.ressources}" var="r" itemLabel="#{r.nom}" itemValue="r.idt_ressource" />
</p:selectCheckboxMenu>
</h:panelGroup>
<p:commandButton icon="ui-icon-save" actionListener="#{affectationBean.save}" value="Save" update="#affectation" ajax="false" style="display:inline-block;margin-top:5px"/>
Here is the the declaration of the selectedRessource and the actionListener save
private Long [] selectedRessource;
// Getters setters and Construct
public void save(){
for(int i=0 ;i<selectedRessource.length;i++){
system.out.println("id ===> " + selectedRessource[i]);
}
My suggestion would be:
First make sure everything is inside the h:form tag.
don't need to multiple = true as this tag does not take this attribute
i tested with below modification and got the selected multiple value in my bean. The only difference is i am using same value for itemLabel and itemValue but in your case it is object. i am using primefaces 6 also and dont even need to change actionListner to action. It is working as it is.sample xhtml
sample ResourceBean.java
<p:outputLabel for="ressource" value="Ressource"/>
<h:panelGroup >
<p:selectCheckboxMenu id="ressource" label="Ressource" value="#{resourceBean.selectedRessource}">
<f:selectItems value="#{resourceBean.ressources}" var="r" itemLabel="#{r}" itemValue="#{r}" />
</p:selectCheckboxMenu>
</h:panelGroup>
<p:commandButton icon="ui-icon-save" actionListener="#{resourceBean.save}" value="Save" ajax="false" style="display:inline-block;margin-top:5px"/>
The problem is in your p:commandButton, you have 3 options
change your method:
public void save(ActionEvent actionEvent){...}
change your action listener value:
actionListener="#{affectationBean.save()}"
or change your button to use action
action="#{affectationBean.save}"
DISCLAIMER: This is a workaround. It is not intended to be a permanent solution but will allow you to use selectCheckboxMenu and keep working.
There is an issue with this component that prevents it from passing values to the backing bean upon submit.
For some reason the array that should contain the selected values gets cleared out upon submit. Therefore I made an extra array that I did not declare in the tag, and updated in on every change event. Then on submit the values were still there. See below:
BackingBean.java
private String[] sCodes;
private String[] sCodes2; //extra array, not in form.xhtml
public void updateCodes()
{
sCodes2 = sCodes; //keeps the values in the other array
}
public void doSend() throws IOException
{
log.trace("selected codes: {} selected codes2 length: {}", sCodes.length, sCodes2.length);
}
form.xhtml
<p:selectCheckboxMenu id="codeCtl" value="#{bean.SCodes}" label="Codes" filter="true" filterMatchMode="startsWith" panelStyle="width:250px">
<f:selectItems value="#{bean.menuCodes}" />
<p:ajax event="change" listener="#{bean.updateCodes()}" />
</p:selectCheckboxMenu>
<p:commandButton value="submit" actionListener="#{bean.doSend}" id="ctlSubmit" update="appform"/>

Auto calculated fields

I've the following form:
<h:form>
<h:inputText size="2" value="#{orderMB.quantity}" />
<h:outputLabel value=" #{orderMB.totalPriceOneItem} €" />
<h:commandButton value="submit" />
</h:form>
And I've the following method in a session scoped managed bean:
public void setQuantity(int quantity) {
this.quantity = quantity;
setTotalPriceOneItem(quantity* item.getItem().getPrice().doubleValue());
}
I would like to auto-update the total price result on every key press of the input field. How can I achieve this without pressing the submit button?
Your code isn't doing that anywhere. It's missing a <f:ajax>.
<h:inputText size="2" value="#{orderMB.quantity}">
<f:ajax event="keyup" render="total" />
</h:inputText>
<h:outputText id="total" value="#{orderMB.totalPriceOneItem} €" />
The event attribute can be set to any HTML DOM event on which JSF must submit the form by ajax, such as click, keyup, blur, focus, etc. The render attribute can be set to any JSF client ID which needs to be updated when the ajax submit finishes. In this case it's referring the ID of the component showing total price.
Note that I replaced the wrong <h:outputLabel> by <h:outputText>. Also noted should be that a setter isn't exactly the right place to perform business logic (a getter also not!). Better revert that setter method to a true setter and add an ajax listener method:
<f:ajax event="keyup" listener="#{orderMB.updateTotalPriceOneItem}" render="total" />
public void updateTotalPriceOneItem() {
totalPriceOneItem = quantity * item.getItem().getPrice().doubleValue();
}
In case when it still doesn't work, then verify if you have a <h:head> in the template instead of a <head>. If still in vain, work through commandButton/commandLink/ajax action/listener method not invoked or input value not updated.
That said, I strongly recommend to take a pause and work through a sane JSF 2.x book. The above is usually already covered in the 1st chapter.

Dynamic reload of items in select many component

I want to achieve sth similar to http://www.primefaces.org/showcase/ui/pprSelect.jsf but i need a collection of double-combos, so i wrapped it in ui:repeat
I need on the backend check which element from collection of double-combos was changed and what I need to reload. For communication is used p:ajax as in the example, but AjaxBehaviorEvent not bring me any idea of index of element ( i mean index of double-combos element generated by ui:repeat)
My client code, the idea is to update bean:selectedIndex everytime when a ajax event will be raised ( on change value of selectOneMenu ), and value of bean:selectedIndex will be set as index of changed selectOneMenu
private List<State> productStates
private int selectedIndex;
private List<Group> groups;
private Map<Integer, Collection<Device>> availableDevicesMap;
<ui:repeat var="state" value="#{bean.productStates}" varStatus="iter">
<p:selectOneMenu id="devGroup" value="#{state.group}">
<f:selectItems value="#{bean.groups}" />
<p:ajax update="refreshable" process="devGroup, #this" listener="#{bean.refreshDevicesForState}" >
<f:setPropertyActionListener target="#{bean.selectedIndex}" value="#{iter.index}"/>
</p:ajax>
</p:selectOneMenu>
<!-- THIS WILL BE UPDATED -->
<h:panelGroup id="refreshable">
<p:selectManyButton id="devices" value="#{state.devices}" >
<f:selectItems value="#{bean.availableDevicesMap[status.index]}" />
</p:selectManyButton>
</h:panelGroup>
</ui:repeat>
Backend which doesn't work as expected. setPropertyActionListener is not invoked and selectOneMenu component hasn't got selected group as value
public refreshDevicesForState(AjaxBehaviorEvent e) {
SelectOneMenu menu = (SelectOneMenu)e.getComponent();
// this value is not as selected on frontend
Group group = (Group)menu.getValue();
// selectedIndex will not be set, so I assume that setPropertyActionListener didn't invoked
availableDevicesMap.put(selectedIndex, group.getDevices());
}
I tried also with code below which works but in my opinion it is ugly
// id will be grandpaId:parentId:index:myId
String selectedIndex = IdHelper.getIdPart(e.getComponent().getClientId(), -2);
State state = productStates.get(Integer.parseInt(selectedIndex));
I am using latest primefaces on glassfish and Mojarra as jsf reference implementation
Thank you for any help
In more general sense:
I have list of objects on backed bean, lets say Cars
List<Car> cars
on frontent I iterate over them and create select brand and select model combos for every car. When user select brand for i.e 4th car i want to get to know on backend that 4th car will be changed and i will reload list of available model for this one car
<ui:repeat var="state" value="#{bean.cars}" >
<p:selectOneMenu id="brands"/>// select brand
<p:selectOneMenu "models"/>// show available models depends on selected brand
</ui:repeat>
How to handle it correct in the JSF world ?
My first suggestion is to use converter for Group.
SelectOneMenu cannot set custom class, only with the help of a converter. (an example is at autocomplete: http://www.primefaces.org/showcase/ui/autoCompletePojo.jsf)
Second, in your bean handler, productStates variable contains already the selected values (of selectOneMenus). You can use it easier, than access it from the event.
If the values of selectOneMenus depend on State, you have to modify this:
<f:selectItems value="#{bean.groups}" />
to be able to express which group values should be displayed.
If you want debug it (without eclipse debugging), you can use messages, for example:
add this to xhtml:
<p:growl id="msgs" showDetail="true"/>
and in bean:
public refreshDevicesForState(AjaxBehaviorEvent e) {
...
FacesMessage msg = new FacesMessage("Selected", "any debug info" + productStates.get(0).getGroup());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
I modified my answer, according to your mods. I would do it this way:
xhtml:
<ui:repeat var="state" value="#{bean.productStates}" varStatus="iter">
<p:selectOneMenu id="devGroup#{iter.index}" value="#{state.group}"
valueChangeListener="#{bean.updateSubProperty}" immediate="true">
<f:selectItems value="#{bean.groups}" />
<f:attribute name="index" value="#{iter.index}" />
</p:selectOneMenu>
<p:selectOneMenu id="subDevGroup#{iter.index}">
...
</p:selectOneMenu>
</ui:repeat>
bean:
public void updateSubProperty(ValueChangeEvent vce) {
String index = vce.getComponent().getAttributes().get("index").toString();
int i = Integer.parseInt(index); //this is the index of the selected selectOneMenu
///...
//update sub selectOneMenu
RequestContext.getCurrentInstance().update("subDevGroup" + index);
}

Resources