I have internalization implemented on my JSF -page as following:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
xmlns:p="http://primefaces.org/ui">
<p:outputPanel id="menupanel">
</h:form>
<p:commandButton value="FI" pt:aria-label="Suomeksi" actionListener="#{language.countryLocaleCodeChanged}" #{view.locale.language eq 'fi'? 'ws-lang-selected' : ''}" update="menupanel"/>
<p:commandButton value="SV" pt:aria-label="På Svenska" actionListener="#{language.countryLocaleCodeChanged}" #{view.locale.language eq 'sv'? 'ws-lang-selected' : ''}" update="menupanel"/>
<p:commandButton value="EN" pt:aria-label="In English" actionListener="#{language.countryLocaleCodeChanged}" #{view.locale.language eq 'en'? 'ws-lang-selected' : ''}" update="menupanel"/>
</h:form>
</p:outputPanel>
</ui:composition>
On my mainTemplate I have set:
<f:view locale="#{language.localeCode}">
And languageController:
#ManagedBean(name = "language")
#SessionScoped
public class LanguageController implements Serializable {
private SessionVariablesBean sessionVariables;
private String localeCode = "fi_FI";
public String getLocaleCode() {
return localeCode;
}
public void setLocaleCode(String localeCode) {
this.localeCode = localeCode;
}
public String getLang() {
return this.localeCode.split("_")[0];
}
public void countryLocaleCodeChanged(ActionEvent e) {
String newLocaleValue = e.getComponent().getAttributes().get("value").toString();
//System.out.println("Locale: " + newLocaleValue);
String kieli = "FI";
if (newLocaleValue.equals("FI")) {
newLocaleValue = "fi_FI";
kieli = "FI";
localeCode = "fi";
}
if (newLocaleValue.equals("SV")) {
newLocaleValue = "sv_SE";
kieli = "SE";
localeCode = "sv";
}
if (newLocaleValue.equals("EN")) {
newLocaleValue = "en_EN";
kieli = "EN";
localeCode = "en";
}
//loop country map to compare the locale code
for (Map.Entry<String, Object> entry
: countries.entrySet()) {
if (entry.getValue().toString().equals(newLocaleValue)) {
FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale) entry.getValue());
this.localeCode = newLocaleValue;
}
}
}
}
My problem is that the following code does not update the lang attribute when pressing the buttons for changing language. How to achieve this? Strangely when I view page source and press F5, the "lang" attribute is updated on the page. I'm using FireFox as browser.
It seems the only answer to this problem is to add the update="#all" tag to the commandButton. <p:commandButton update="#all"/> That provides the hoped income updating the lang tag, but it also refreshes the whole page, so not optimal solution, but it works.
Related
This question already has answers here:
Creating master-detail pages for entities, how to link them and which bean scope to choose
(2 answers)
Closed 3 years ago.
I'm working on one school project, where i need to use JSF to make CRUD app.
I'm using MySQL database, and managed to make list of all objects, delete button, and i have trouble with edit button.
When i click on edit it redirect me to edit.xhtml page, get id and fill all fields based on that id.
When i click update button on edit page, it always change customer with id=0.
I have one java doc with getter, setter and methods
have two views index.xhtml and edit.xhtml
and one page with method for connection with database.
All other methods work fine, except update.
Customer.java
#ManagedBean
#RequestScoped
public class Customer {
private int id;
private String username;
private String adress;
private int quantity;
private double price;
private Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
public String edit() {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
String primarId = params.get("action");
System.out.println(primarId);
try {
DatabaseConnection dbc = new DatabaseConnection();
Connection connection = dbc.getConnection();
Statement st = connection.createStatement();
ResultSet rs = st.executeQuery("select * from customer where customer_id=" + primarId);
Customer customer = new Customer();
rs.next();
customer.setUsername(rs.getString("username"));
customer.setAdress(rs.getString("adress"));
customer.setQuantity(rs.getInt("quantity"));
customer.setPrice(rs.getDouble("price"));
sessionMap.put("editcustomer", customer);
} catch (SQLException ex) {
System.out.println(ex);
}
return "/edit.xhtml?faces-redirect=true";
}
public String updateCustomer() {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
String fieldId = params.get("action");
System.out.println(fieldId);
try {
DatabaseConnection dbc = new DatabaseConnection();
Connection connection = dbc.getConnection();
PreparedStatement ps = connection.prepareStatement("update customer set username=?,adress=?,quantity=?,price=? where customer_id=?");
ps.setString(1, username);
ps.setString(2, adress);
ps.setInt(3, quantity);
ps.setDouble(4, price);
ps.setInt(5, id);
System.out.println(id);
ps.executeUpdate();
} catch (SQLException ex) {
System.out.println(ex);
}
return "/index.xhtml?faces-redirect=true";
}
edit.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<center>
<h:form>
Username: <h:inputText value="#{editcustomer.username}"></h:inputText> <br/>
Adress: <h:inputText value="#{editcustomer.adress}"></h:inputText> <br/>
Quantity: <h:inputText value="#{editcustomer.quantity}"></h:inputText> <br/>
Price: <h:inputText value="#{editcustomer.price}"></h:inputText> <br/><br/>
<h:commandButton value="Update" action="#{editcustomer.updateCustomer()}">
<f:param name="action" value="#{editcustomer.id}" />
</h:commandButton>
</h:form>
</center>
</h:body>
</html>
when I run this code the ID stays 0
I think the problem is the #RequestScoped annotation in the bean. With this annotation data only "lives" in the current page. As you are redirecting to another URL (altough it is the same one), you lose your changes (more taking into account that you are putting the customer in the session map. Try using #SessionScoped instead.
Hope it works.
I'm using JSF2 and Tomcat server. I programmed a simple example in which:
User Selects a faculty from "h:selectOneMenu"
Upon selection, the value of "h:inputText" is changed to "odd" or "even" based on facultyNo
Also, upon selection, the value of "h:selectBooleanCheckBox" is changed to "checked" if facultyNo is even and "not checked" if facultyNo is odd
Everything works fine for "h:inputText". On the other hand, the value of "h:selectBooleanCheckBox" does not change. Why is this happening?
By the way, the use of boolean value inside a HashMap is intentional because the project I'm working on has lots of boolean values in a HashMap. So, replacing the Hashmap with a simple boolean property and using a getter and a setter for it is definitely not a solution for my case.
The code for the xhtml page is below:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Dummy Title</title>
</head>
<body>
<h:form>
<label for="faculty">Faculty</label>
<h:selectOneMenu id="faculty" value="#{test.selectedFaculty}" converter="faccon" valueChangeListener="#{test.facultyChange}" onchange="submit()">
<f:selectItems value="#{start.app.faculties}"/>
</h:selectOneMenu>
<h:selectBooleanCheckbox id="mycheck" value="#{test.x.get(0)}"></h:selectBooleanCheckbox>
<h:outputText value="#{test.res}"></h:outputText>
<h:commandButton value="Save" action="#{test.saveChoices}" />
</h:form>
</body>
</html>
The code for the backing bean is below
import java.io.Serializable;
import java.util.HashMap;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.ValueChangeEvent;
import com.myWork.Application;
import com.myWork.Faculty;
#ManagedBean(name="test")
#RequestScoped
public class TestBean implements Serializable
{
private HashMap<Integer,Boolean> x;
private String res;
private Faculty selectedFaculty;
#PostConstruct
public void init(){
Application app = Application.getInstance();
selectedFaculty = app.getFaculties()[0];
x = new HashMap<Integer, Boolean>();
if (selectedFaculty.getFacultyNo()%2==0)
{
x.put(0, true);
res = "even";
}
else
{
x.put(0, false);
res = "odd";
}
}
public HashMap<Integer,Boolean> getX() {
return x;
}
public void setX(HashMap<Integer,Boolean> x) {
this.x = x;
}
public Faculty getSelectedFaculty() {
return selectedFaculty;
}
public void setSelectedFaculty(Faculty selectedFaculty) {
this.selectedFaculty = selectedFaculty;
}
public String getRes() {
return res;
}
public void setRes(String res) {
this.res = res;
}
public void facultyChange(ValueChangeEvent e){
Faculty fac = (Faculty) e.getNewValue();
if (fac.getFacultyNo()%2==0)
{
x.put(0, true);
res = "even";
}
else
{
x.put(0, false);
res = "odd";
}
}
public String saveChoices(){
return "test";
}
}
Any help is greatly appreciated.
Actually your problem is not related to JSF, it is related to EL. in your case, EL treat you map keys as long not int, so you should change your map to be:
HashMap<Long, Boolean> map = new HashMap<>();
and set the key values as follows :
map.put(0L, false);
map.put(1L, true);
to force it to be auto-boxed to long.
Note: the problem described in detailed in the following question :
EL access a map value by Integer key
In order to send updates to a different component, you can make use of partial page rendering by making Ajax calls to send parameters to Managed bean.
Within <h:selectBooleanCheckBox>
<h:selectOneMenu id="faculty" value="#{test.selectedFaculty}" converter="faccon" valueChangeListener="#{test.facultyChange}">
<f:selectItems value="#{start.app.faculties}"/>
</h:selectOneMenu>
<h:selectBooleanCheckbox id="mycheck" valueChangeListener="#{test.facultyChange}" value="#{test.x.get(0)}">
<f:ajax event="change" execute="#form" render="faculty"/>
</h:selectBooleanCheckbox>
Edit: Removed call to onSubmit() in selectOneMenu. introduced an ajax call in selectBooleanCheckbox for partial page refresh.
Refer full solution at this post.
I am having a problem with the SelectOneMenu control. I want the the selected item to be be displayed via the valueChange Ajax event listen. But this is not happening.
However, when I change the value in the SelectOneMenu and then click on the Submit button, then selected value is getting displayed via the 'save' bean function
Cannot figure out why this is not working. Would appreciate any help on this.
Thanks.
The relevant xhtml code is as follows:
<h:form>
<h:dataTable value="#{dynamicList.myData}" var="item" >
<h:column>
<h:outputText value="#{item.oracleType}"></h:outputText>
</h:column>
<h:column>
<h:selectOneMenu value="#{item.coffeeFlavour}" rendered="#{item.showLov}" >
<f:selectItems value="#{item.coffeeList}"></f:selectItems>
<f:ajax event="valueChange" listener="#{dynamicList.listen}" ></f:ajax>
</h:selectOneMenu>
<h:inputText value="#{item.coffeeFlavour}" rendered="#{item.showText}">
</h:inputText>
</h:column>
</h:dataTable>
<p:commandButton value="Submit" action="#{dynamicList.save}" ></p:commandButton>
</h:form>
The relevant bean code is as follows:
#ManagedBean
#ViewScoped
public class DynamicList implements Serializable{
private List<OraclePrfl> oracleList=new ArrayList<OraclePrfl>();
private String coffee;
private Map<String,String> coffeeList=new LinkedHashMap<String,String>();
public List<OraclePrfl> getOracleList() {
return oracleList;
}
public List<OraclePrfl> getMyData()
{
oracleList.clear();
oracleList.add(new OraclePrfl("Oracle Lot Number",new HashMap<String,String>(){
{
put("Coffee2 - Cream Latte", "Cream Latte");
put("Coffee2 - Extreme Mocha", "Extreme Mocha");
put("Coffee2 - Buena Vista", "Buena Vista");
}
},true,false));
oracleList.add(new OraclePrfl("Oracle Product Number",new HashMap<String,String>(){
{
put("ABC", "abc");put("PQR", "pqr");put("XYZ", "xyz");
}
},true,false));
oracleList.add(new OraclePrfl("Oracle Specification",new HashMap<String,String>(){
{
put("MNP", "mnp");put("WXY", "wxy");put("XYZ", "xyz");
}
},true,false));
oracleList.add(new OraclePrfl("Address",false,true));
return oracleList;
}
public void setOracleList(List<OraclePrfl> oracleList) {
this.oracleList = oracleList;
}
public String getCoffee() {
return coffee;
}
public void setCoffee(String coffee) {
this.coffee = coffee;
}
public Map<String,String> getCoffeeList() {
coffeeList.clear();
coffeeList.put("Coffee2 - Cream Latte", "Cream Latte"); //label, value
coffeeList.put("Coffee2 - Extreme Mocha", "Extreme Mocha");
coffeeList.put("Coffee2 - Buena Vista", "Buena Vista");
return coffeeList;
}
public void setCoffeeList(Map<String,String> coffeeList) {
this.coffeeList = coffeeList;
}
public void save(){
for(OraclePrfl oracle:oracleList){
System.out.println("oracle type------"+oracle.getOracleType()+"------coffee----
"+oracle.getCoffeeFlavour());
}
}
public void listen(AjaxBehaviorEvent event){
System.out.println("calling listener "+event.getSource().toString());
for(OraclePrfl oracle:oracleList){
System.out.println("type....."+oracle.getOracleType()+"----value-----
"+oracle.getCoffeeFlavour());
}
}
}
Try removing the event:
<f:ajax listener="#{dynamicList.listen}" ></f:ajax>
It should default to event="change".
In JSF & Primefaces web application, I want to pass a value for the complete method of primefaces input text area control. I have tried it as follows.
JSF file
<p:inputTextarea id="txtMicMemoVal"
value="#{patientReportController.memoEnterVal}"
style="min-width: 200px;"
completeMethod="#{investigationItemValueController.completeValues}" >
<f:attribute name="ii" value="#{pv.investigationItem}" />
<f:ajax event="blur" execute="#this"
listener="#{patientReportController.saveMemoVal(pv.id)}" ></f:ajax>
</p:inputTextarea>
Relevant Backing Bean
public List<String> completeValues(String qry) {
System.out.println("completing values");
FacesContext context = FacesContext.getCurrentInstance();
InvestigationItem ii;
try {
ii = (InvestigationItem) UIComponent.getCurrentComponent(context).getAttributes().get("ii");
System.out.println("ii = " + ii);
} catch (Exception e) {
ii = null;
System.out.println("error " + e.getMessage());
}
Map m = new HashMap();
String sql;
sql = "select v.name from InvestigationItemValue v "
+ "where v.investigationItem=:ii and v.retired=false and"
+ " (upper(v.code) like :s or upper(v.name) like :s) order by v.name";
m.put("s","'%"+ qry.toUpperCase()+"%'");
m.put("ii", ii);
List<String> sls = getFacade().findString(sql, m);
System.out.println("sls = " + sls);
return sls;
}
But the backing bean method is not fired when i enter text to input text area. But if I remove the f:attribute, backing bean is fired. But I want that parameter as well for functionality.
Thanks in advance to direct me to over come this issue.
Interesting question. Primefaces bounds you to receive only a String parameter in your completion method, so the only solution I see is evaluating your expression at server side, when completion function gets called.
I suppose you've got an iteration (either ui:repeat or p:dataTable) where each id differs from the previous one. If you don't, you can also use it.
That would be the way to go:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head />
<h:body>
<h:form>
<ui:repeat var="str" value="#{bean.strings}">
<p:inputTextarea value="#{bean.value}" style="min-width: 200px;"
completeMethod="#{bean.complete}" />
</ui:repeat>
</h:form>
</h:body>
</html>
#ManagedBean
#RequestScoped
public class Bean {
public String value;
public List<String> strings = Arrays.asList("param1", "param2", "param3");
public List<String> complete(String query) {
List<String> results = new ArrayList<String>();
//Here we evaluate the current #{str} value and print it out
System.out.println(FacesContext
.getCurrentInstance()
.getApplication()
.evaluateExpressionGet(FacesContext.getCurrentInstance(),
"#{str}", String.class));
if (query.equals("PrimeFaces")) {
results.add("PrimeFaces Rocks!!!");
results.add("PrimeFaces has 100+ components.");
results.add("PrimeFaces is lightweight.");
results.add("PrimeFaces is easy to use.");
results.add("PrimeFaces is developed with passion!");
}
return results;
}
public List<String> getStrings() {
return strings;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Note you're evaluating the current EL result for #{str} when method call performs. You'll get a different evaluation result depending on which p:inputTextArea you write to.
See also:
How to pass parameter to f:ajax in h:inputText? f:param does not work
all
I've been working on a composite component for a date range. Essentially, my composite component uses two Richfaces 4.3 calendar components underneath to capture the individual date values, generate a date range (a pair of LocalDate objects). I found this blog entry which was the basis for my custom component that combines the two submitted values on the calendar into one pair value.
Everything seems to work fine and the values are getting updated. However, I'm trying to figure out how to propagate the change event to the using xhtml page for a partial render of another component, and I've been unsuccessful. I've tried everything I could think of, but I think I'm missing something.
The page:
<rich:panel>
<f:facet name="header">Calendar Date Range Component</f:facet>
<h:outputText id="out1" binding="#{calendarDateRangeTestBean.component1}"
value="#{calendarDateRangeTestBean.dateRange}" converter="localDatePairConverter" /><br/>
<h:outputText id="out2" value="#{calendarDateRangeTestBean.dateRange}" converter="localDatePairConverter" /><b>NOT WORKING</b>
<yxp:calendarDateRange id="calendarDateRange" value="#{calendarDateRangeTestBean.dateRange}"
dataModel="#{calendarDateRangeTestBean}"
valueChangeListener="#{calendarDateRangeTestBean.processValueChange}">
<f:ajax execute="#all" listener="#{calendarDateRangeTestBean.processBehaviorEvent}"/>
<!-- This doesn't seem to work???? -->
<f:ajax execute="#all" render="out2" />
</yxp:calendarDateRange>
</rich:panel>
My test managed bean:
#ViewScoped
#ManagedBean
public class CalendarDateRangeTestBean extends AbstractCalendarDateRangeDataModel implements
ValueChangeListener, Serializable {
private static Logger logger = LoggerFactory.getLogger(CalendarDateRangeTestBean.class);
private Pair<LocalDate> dateRange = Pair.of(LocalDate.now(), LocalDate.now().plusDays(7));
private UIComponent component1;
public UIComponent getComponent1() {
return component1;
}
public LocalDateRange getDateRange() {
return dateRange;
}
public void processBehaviorEvent(final javax.faces.event.AjaxBehaviorEvent event) {
logger.info("processing event " + event + ": " + event.getBehavior());
final FacesContext context = FacesContext.getCurrentInstance();
logger.info("Setting render to " + component1.getClientId(context));
// This seems to cause a rerender of the first component
context.getPartialViewContext().getRenderIds().add(component1.getClientId(context));
}
#Override
public void processValueChange(final ValueChangeEvent event) throws AbortProcessingException {
logger.info(this.toString() + ": processing value change event " + event + ": ["
+ event.getOldValue() + ":" + event.getNewValue() + "]");
}
public void setComponent1(final UIComponent component1) {
this.component1 = component1;
}
public void setDateRange(final Pair<LocalDate> dateRange) {
logger.info("Setting date range to " + dateRange);
this.dateRange = dateRange;
}
}
My composite component:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:composite="http://java.sun.com/jsf/composite">
<!-- Methods exposed on rich:component are available in the __proto__ object. -->
<composite:interface componentType="com.yieldex.platform.ui.CalendarDateRange">
<composite:attribute name="value" required="true" type="demo.Pair"/>
<composite:attribute name="dataModel" required="false" type="demo.Pair" />
<composite:clientBehavior name="change" event="change" targets="startCalendar endCalendar" default="true"/>
</composite:interface>
<composite:implementation>
<h:outputStylesheet library="yieldex/platform" name="css/yieldex-platform.css" target="head" />
<div id="#{cc.clientId}" class="yxp-calendar-date-range">
<rich:calendar id="startCalendar"
binding="#{cc.startCalendar}"
styleClass="yxp-start-date-range"
converter="localDateConverter" mode="ajax"
dataModel="#{not empty cc.attrs.dataModel ? cc.attrs.dataModel.startCalendarDataModel : standardCalendarDateRangeDataModel.startCalendarDataModel}"
monthLabels="#{dateRangeMessages.monthNames}"
weekDayLabelsShort="#{dateRangeMessages.weeksShort}"
monthLabelsShort="#{dateRangeMessages.monthNames}" popup="false"
showInput="false" showFooter="false" showWeeksBar="false"
showWeekDaysBar="true" showApplyButton="false"
buttonIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}"
buttonDisabledIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}">
<f:facet name="weekDays"></f:facet>
<f:ajax immediate="true" execute="#all" render="#this endCalendar"/>
</rich:calendar>
<rich:calendar id="endCalendar"
binding="#{cc.endCalendar}"
styleClass="yxp-end-date-range"
converter="localDateConverter" mode="ajax"
dataModel="#{not empty cc.attrs.dataModel ? cc.attrs.dataModel.endCalendarDataModel : standardCalendarDateRangeDataModel.endCalendarDataModel}"
monthLabels="#{dateRangeMessages.monthNames}"
weekDayLabelsShort="#{dateRangeMessages.weeksShort}"
monthLabelsShort="#{dateRangeMessages.monthNames}" popup="false"
showInput="false" showFooter="false" showWeeksBar="false"
showWeekDaysBar="true" showApplyButton="false"
buttonIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}"
buttonDisabledIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}">
<f:facet name="weekDays"></f:facet>
<f:ajax immediate="true" execute="#all" render="startCalendar #this"/>
</rich:calendar>
</div>
</composite:implementation>
</ui:composition>
My backing component:
#FacesComponent("com.yieldex.platform.ui.CalendarDateRange")
public class YXCalendarDateRange extends UIInput implements NamingContainer {
private UICalendar startCalendarComponent;
private UICalendar endCalendarComponent;
#Override
public void encodeBegin(final FacesContext context) throws IOException {
final Pair<LocalDate> value = (Pair<LocalDate>) this.getValue();
if (value == null) {
startCalendarComponent.setValue(null);
endCalendarComponent.setValue(null);
} else {
startCalendarComponent.setValue(value.getStart());
endCalendarComponent.setValue(value.getEnd());
}
super.encodeBegin(context);
}
#Override
protected Object getConvertedValue(final FacesContext context, final Object submittedValue) {
final LocalDate startDate = (LocalDate) startCalendarComponent.getConverter().getAsObject(context,
startCalendarComponent, (String) this.startCalendarComponent.getSubmittedValue());
final LocalDate endDate = (LocalDate) endCalendarComponent.getConverter().getAsObject(context,
endCalendarComponent, (String) this.endCalendarComponent.getSubmittedValue());
if (startDate == null || endDate == null) {
return null;
} else {
if (startDate.isAfter(endDate)) {
final FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_ERROR);
message.setSummary("start date cannot be after end date");
message.setDetail("start date cannot be after end date");
throw new ConverterException(message);
}
return Pair.of(startDate, endDate);
}
}
public UICalendar getEndCalendar() {
return this.endCalendarComponent;
}
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public UICalendar getStartCalendar() {
return this.startCalendarComponent;
}
#Override
public Object getSubmittedValue() {
return this;
}
public void setEndCalendar(final UICalendar endCalendarComponent) {
this.endCalendarComponent = endCalendarComponent;
}
public void setStartCalendar(final UICalendar startCalendarComponent) {
this.startCalendarComponent = startCalendarComponent;
}
}
What I see is that the valueChangedEvent is coming though. I also see my processBehaviorEvent being called, and the first outputText being rerendered as I'm calling that programmatically. But the second one doesn't seem to get rerendered. I am trying to figure out if this is a bug in Mojarra 2.1.25 or is there something fundamentally wrong with my approach. Any suggestions would be greatly appreciated.
Any client ID in <f:ajax render> is evaluated relative to the parent naming container of the component it has been attached to. In this construct, the <f:ajax> ends up being attached inside the composite component, which is by itself a naming container. However, there's no component with ID out2 inside the composite, which is the problem.
To solve it, specify the absolute client ID. For example, when it's inside a <h:form id="formId"> element:
<f:ajax execute="#all" render=":formId:out2" />
If it's more complicated, binding the component to the view and refer to its client ID dynamically:
<h:outputText id="out2" binding="#{out2}" ... />
...
<f:ajax execute="#all" render=":#{out2.clientId}" />
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"