2 Replies Latest reply on Jul 24, 2009 8:20 PM by Alex Cougarman

    Set controls value to objects in array

    Alex Cougarman Apprentice

      I've got a page that needs to have controls added to it dynamically. An HtmlPanel on the page is bound to the manager class -- CustomCalendarHandler.

      Each time the user clicks the "Add Another Calendar" button, the manager class fires off the addComponent() method on the CustomCalendar, which adds a RichCalendar and HtmlInputText to the panel on the page. It stores that particular CustomCalendar in an ArrayList.

      How do I set the Value of each calendar and textbox to Date and String properties of their respective CustomCalendar object in the ArrayList? When I try this code, it throws an exception:

      javax.faces.convert.ConverterException: value must be a date


      Here's the code for the CustomCalendar class:

      import org.ajax4jsf.component.html.HtmlAjaxSupport;
      import org.richfaces.component.html.HtmlCalendar;
      import javax.el.ValueExpression;
      import javax.faces.component.html.HtmlInputText;
      import javax.faces.component.html.HtmlOutputLabel;
      import javax.faces.component.html.HtmlOutputText;
      import javax.faces.context.FacesContext;
      import org.richfaces.component.html.HtmlPanel;
      import org.richfaces.component.html.HtmlRichMessage;
      import org.ajax4jsf.component.html.HtmlAjaxOutputPanel;
      import java.util.Date;
      
      public class CustomCalendar {
       private HtmlCalendar calendarInput;
       private HtmlInputText hoursInput;
       private HtmlOutputLabel label;
       private HtmlOutputText text1;
       private HtmlOutputText text2;
       private HtmlPanel htmlPanel;
       private Integer componentCount;
       private HtmlAjaxSupport ajaxSupport;
       private HtmlRichMessage message;
       private HtmlAjaxOutputPanel ajaxPanel;
       private Integer componentId;
       private Date date;
       private String hours;
      
       public CustomCalendar(){
       if (this.componentCount == null || this.componentCount == 0){
       this.addComponent();
       }
       }
      
       public void addComponent(){
       if (this.componentCount == null) return;
      
       componentId = componentCount;
       StringBuilder el = new StringBuilder();
      
       String calId = "calOvertimeDate_" + Integer.toString(componentCount);
       String txtId = "txtOvertimeHours_" + Integer.toString(componentCount);
      
       calendarInput = new HtmlCalendar();
       message = new HtmlRichMessage();
       hoursInput = new HtmlInputText();
       label = new HtmlOutputLabel();
       text1 = new HtmlOutputText();
       text2 = new HtmlOutputText();
       ajaxSupport = new HtmlAjaxSupport();
       ajaxPanel = new HtmlAjaxOutputPanel();
      
       ajaxPanel.setId("renderPanel");
       ajaxPanel.setAjaxRendered(true);
      
       calendarInput.setValue("#{CustomCalendarHandler.components.get(" + Integer.toString(componentId) + ").getDate()}");
       calendarInput.setDatePattern("MM-dd-yyyy");
       calendarInput.setId(calId);
      
       message.setFor(txtId);
       message.setStyleClass("errors");
      
       el.append("#{CustomCalendarHandler.components.get(");
       el.append(Integer.toString(componentId));
       el.append(").getDate() == null || ");
       el.append("CustomCalendarHandler.components.get(");
       el.append(Integer.toString(componentId));
       el.append(").getDate().toString() == '' ? false : true}");
      
       ajaxSupport.setEvent("oninputblur");
       ajaxSupport.setReRender("renderPanel");
       ajaxSupport.setAjaxSingle(true);
       calendarInput.getFacets().put("a4jsupport", ajaxSupport);
      
       hoursInput.setId(txtId);
       hoursInput.setMaxlength(5);
       hoursInput.setValue("#{CustomCalendarHandler.components.get(" + Integer.toString(componentId) + ").hours}");
       hoursInput.setRequiredMessage("If you provide a value for Date " + Integer.toString(componentCount) + ", you must provide the OT Hours.");
       FacesContext ctx = FacesContext.getCurrentInstance();
       ValueExpression ve = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(),el.toString(), Boolean.class);
       hoursInput.setValueExpression("required", ve);
      
       label.setValue("Date " + Integer.toString(componentCount));
       text1.setValue("  OT Hours");
       text1.setEscape(false);
       text2.setValue("<br />");
       text2.setEscape(false);
      
       htmlPanel.getChildren().add(label);
       htmlPanel.getChildren().add(calendarInput);
       htmlPanel.getChildren().add(text1);
       ajaxPanel.getChildren().add(message);
       ajaxPanel.getChildren().add(hoursInput);
       htmlPanel.getChildren().add(ajaxPanel);
       htmlPanel.getChildren().add(text2);
       }
      
       public Date getDate(){
       return this.date;
       }
       public void setDate(Date date){
       this.date = date;
       }
      
       public String getHours(){
       return this.hours;
       }
       public void setHours(String hours){
       this.hours = hours;
       }
      
       public Integer getComponentId(){
       return this.componentId;
       }
       public void setComponentId(Integer componentId){
       this.componentId = componentId;
       }
      
       public Integer getComponentCount(){
       return this.componentCount;
       }
       public void setComponentCount(Integer componentCount){
       this.componentCount = componentCount;
       }
      
       public HtmlPanel getPanel(){
       return this.htmlPanel;
       }
       public void setPanel(HtmlPanel htmlPanel){
       this.htmlPanel = htmlPanel;
       }
      
       public HtmlCalendar getCalendarInput(){
       return this.calendarInput;
       }
       public void setCalendarInput(HtmlCalendar calendarInput){
       this.calendarInput = calendarInput;
       }
      
       public HtmlInputText getHoursInput(){
       return this.hoursInput;
       }
       public void setHoursInput(HtmlInputText hoursInput){
       this.hoursInput = hoursInput;
       }
      }


      Here's the code for the manager class:

      import java.util.ArrayList;
      import org.richfaces.component.html.HtmlPanel;
      
      public class CustomCalendarHandler {
       private ArrayList<CustomCalendar> components;
       private HtmlPanel htmlPanel;
       private Integer componentCount;
      
       public CustomCalendarHandler() {
       components = new ArrayList<CustomCalendar>();
       componentCount = 0;
       if (htmlPanel == null){
       htmlPanel = new HtmlPanel();
       }
       this.addCustomCalendar();
       }
      
       public void addCustomCalendar(){
       componentCount = (componentCount == null) ? 0 : componentCount + 1;
       CustomCalendar customCalendar = new CustomCalendar();
       customCalendar.setPanel(htmlPanel);
       customCalendar.setComponentCount(componentCount);
       customCalendar.addComponent();
       components.add(customCalendar);
      
       }
      
       public HtmlPanel getPanel(){
       return this.htmlPanel;
       }
       public void setPanel(HtmlPanel htmlPanel){
       this.htmlPanel = htmlPanel;
       }
      
       public ArrayList<CustomCalendar> getComponents(){
       return this.components;
       }
       public void setComponents(ArrayList<CustomCalendar> components){
       this.components = components;
       }
      }


      Here's the pertinent XHTML:
      <rich:panel id="calPanel" binding="#{CustomCalendarHandler.panel}"/>
      
      <a4j:commandButton action="#{CustomCalendarHandler.addCustomCalendar()}" value="Add Another Calendar" reRender="calPanel" />


        • 1. Re: Set controls value to objects in array
          Alexander Smirnov Master

          You have at least a two errors in your code :
          1) component Java attributes does not parse EL-expressions. You have to compile them in your code using ExpressionFactory methods and assign ValueExpression object to component using setValueExpression method.
          2) Using 'binding' feature is little bit complex. JSF asks 'get..' method for the bindend component only then a new view ( or branch ) is created. For consequent requests these components are saved/restored in the view state. Framework assign restored component to the backed bean by 'set..' method during 'restore view' phase for each postback request. That means what your 'CustomCalendarHandler' can be request-scope only and you have to assign proper references to bean fields not in the constructor but in the setPanel() method and create new components in the getPanel() method only if current value was null.
          P.S. One more advice: do not create component by 'new' but use Application.createComponent method instead.
          P.P.S. Really, you have to implement a significant part of the JSF view library ( ever taglib or facelets ), that requires a deep knowledge of the JSF internals. For your scenario, it would be much easy to use iteration component like dataTable instead.

          • 2. Re: Set controls value to objects in array
            Alex Cougarman Apprentice

            Thanks, Alex. Your points are all very valid. I've been working with JSF for a few months and trying to implement the above code correctly will be impossible.

            Do you have some examples of the dataTable approach that can help me? It could hold one or more the rows of calendars and textboxes.

            Thanks again for your help. It is very much appreciated by this newbie :)