JSF事件不从具有支持组件的复合组件传播
全部 我一直在为一个日期范围内的复合组件工作。基本上,我的复合组件使用下面的两个Richfaces 4.3日历组件来捕获各个日期值,生成一个日期范围(一对LocalDate对象)。我发现这是我的自定义组件的基础,该组件将日历上提交的两个值合并为一对值 一切似乎都很顺利,价值观也在不断更新。但是,我正在尝试找出如何将更改事件传播到使用xhtml页面以部分呈现另一个组件,但我没有成功。我已经尝试了我能想到的一切,但我想我错过了一些东西 页面:JSF事件不从具有支持组件的复合组件传播,jsf,composite-component,Jsf,Composite Component,全部 我一直在为一个日期范围内的复合组件工作。基本上,我的复合组件使用下面的两个Richfaces 4.3日历组件来捕获各个日期值,生成一个日期范围(一对LocalDate对象)。我发现这是我的自定义组件的基础,该组件将日历上提交的两个值合并为一对值 一切似乎都很顺利,价值观也在不断更新。但是,我正在尝试找出如何将更改事件传播到使用xhtml页面以部分呈现另一个组件,但我没有成功。我已经尝试了我能想到的一切,但我想我错过了一些东西 页面: <rich:panel> <f
<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>
日历日期范围组件
不起作用
我的测试管理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;
}
@ViewScoped
@ManagedBean
公共类CalendarDateRangeTestBean扩展了AbstractCalendarDateRangeDataModel实现
ValueChangeListener,可序列化{
私有静态记录器Logger=LoggerFactory.getLogger(CalendarDateRangeTestBean.class);
private Pair dateRange=Pair.of(LocalDate.now(),LocalDate.now().plusDays(7));
专用UIComponent组件1;
公共UIComponent getComponent1(){
返回组件1;
}
公共LocalDateRange getDateRange(){
返回日期范围;
}
public void processBehaviorEvent(最终javax.faces.event.AjaxBehaviorEvent事件){
info(“处理事件”+event+:“+event.getBehavior());
final FacesContext context=FacesContext.getCurrentInstance();
info(“将渲染设置为”+component1.getClientId(上下文));
//这似乎会导致第一个组件的重新加载
context.getPartialViewContext().getRenderIds().add(component1.getClientId(context));
}
@凌驾
public void processValueChange(final ValueChangeEvent事件)引发AbortProcessingException{
logger.info(this.toString()+”:处理值更改事件“+事件+”:[“
+event.getOldValue()+“:“+event.getNewValue()+”]”;
}
公共无效集合组件1(最终UIComponent1){
this.component1=component1;
}
公共void setDateRange(最后一对日期范围){
logger.info(“将日期范围设置为“+日期范围”);
this.dateRange=日期范围;
}
}
我的复合组件:
<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>
@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;
}
}
我的支持组件:
<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>
@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;
}
}
@FacesComponent(“com.yieldex.platform.ui.CalendarDateRange”)
公共类YXCalendarDateRange扩展UIInput实现NamingContainer{
专用UICalendar StartCalendar组件;
专用UICalendar EndCalendar组件;
@凌驾
public void encodeBegin(最终FacesContext上下文)引发IOException{
最终对值=(对)this.getValue();
如果(值==null){
startCalendarComponent.setValue(null);
endCalendarComponent.setValue(null);
}否则{
startCalendarComponent.setValue(value.getStart());
endCalendarComponent.setValue(value.getEnd());
}
super.encodeBegin(上下文);
}
@凌驾
受保护对象getConvertedValue(最终FacesContext上下文,最终对象submittedValue){
最终LocalDate startDate=(LocalDate)startCalendarComponent.getConverter().getAsObject(上下文,
startCalendarComponent,(字符串)this.startCalendarComponent.getSubmittedValue();
最终LocalDate endDate=(LocalDate)endCalendarComponent.getConverter().getAsObject(上下文,
endCalendarComponent,(字符串)this.endCalendarComponent.getSubmittedValue();
if(startDate==null | | endDate==null){
返回null;
}否则{
if(开始日期:isAfter(结束日期)){
最终FacesMessage消息=新FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_错误);
message.setSummary(“开始日期不能晚于结束日期”);
message.setDetail(“开始日期不能晚于结束日期”);
抛出新的ConverterException(消息);
}
返回对(开始日期、结束日期);
}
}
公共UICalendar getEndCalendar(){
返回此.endCalendar组件;
}
@凌驾
公共字符串getFamily(){
返回UINamingContainer.COMPONENT_族;
}
公共UICalendar getStartCalendar(){
返回此.startCalendarComponent;
}
@凌驾
公共对象getSubmittedValue(){
归还这个;
}
public void setEndCalendar(最终UICalendar endCalendar组件){
this.endCalendarComponent=endCalendarComponent;
}
公共作废设置开始日历(最终UICalendar开始日历组件){
this.startCalendarComponent=startCalendarComponent;
}
}
但我看到的是,价值观的改变正在到来。我还看到我的processBehaviorEvent被调用,并且在以编程方式调用时,第一个outputText被重新命名。但第二个似乎没有被重新招标。我试图弄清楚这是Mojarra 2.1.25中的一个bug,还是我的方法存在根本性的错误。如果您有任何建议,我们将不胜感激。将根据所连接组件的父级对
中的任何客户端ID进行评估。在这个构造中,
最终被附加到复合组件中,复合组件本身就是一个命名容器。但是,组合中没有ID为out2
的组件,这就是问题所在
要解决此问题,请指定绝对客户端ID。例如,当它位于
元素内时:
<f:ajax execute="@all" render=":formId:out2" />
如果是更合拍的话