8 Replies Latest reply on Aug 28, 2013 11:30 AM by anilarora

    UICalendar events not propagating in composite component

    anilarora

      I've been working on a composite component for a date range using two Richfaces Calendar components underneath to capture the individual date values, generate a date range (a pair of LocalDate objects). I found [this blog entry] (http://balusc.blogspot.com/2013/01/composite-component-with-multiple-input.html) 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 in the value bindings.  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" value="#{calendarDateRangeTestBean.dateRange}" converter="localDatePairConverter" /><br/>
           <yxp:calendarDateRange id="calendarDateRange" value="#{calendarDateRangeTestBean.dateRange}"
                       dataModel="#{calendarDateRangeTestBean}"
                       valueChangeListener="#{calendarDateRangeTestBean.processValueChange}">
                    <!-- The value change listener is called -->
      
                    <!-- This listener gets called, but the output text is not re-rendered -->
                    <f:ajax render=":mainform:out1" listener="#{calendarDateRangeTestBean.processBehaviorEvent}"/>  
      
           </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));
      
             public LocalDateRange getDateRange() {
                 return dateRange;
             }
      
             public void processBehaviorEvent(final javax.faces.event.AjaxBehaviorEvent event) {
                 logger.info("processing event " + event + ": " + event.getBehavior());
             }
      
             @Override
             public void processValueChange(final ValueChangeEvent event) throws AbortProcessingException {
                 logger.info(this.toString() + ": processing value change event " + event + ": ["
                      + event.getOldValue() + ":" + event.getNewValue() + "]");
             }
      
             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">
      
      
          <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=":${cc.clientId}"/>
                  </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=":${cc.clientId}"/>
                      </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.