1 2 3 Previous Next 31 Replies Latest reply on May 27, 2010 4:52 AM by igorg Go to original post
      • 15. Re: CDK - creating a new component with action
        igorg
        Hi Nick. I think it is the part I've looked for. Is it true?

        <script type="text/javascript"> new Richfaces.DaySelector('#{clientId}', #{this:getSubmitFunction(context,component)}); </script>

         

        Sorry, markup that looks fine in edit screen isn't showed correctly in reality. I mean to script at the end of jspx template of datascroller.

        • 16. Re: CDK - creating a new component with action
          nbelaevski

          Renderer just passes created function as argument into initialize(...) method.

          • 17. Re: CDK - creating a new component with action
            igorg

            It seems me like the thing I wrote in last time. Correct me if I make mistake.

            1. getSubmitFunction() creates ajax function

            2. Small script at the end of jspx template creates new datascroller javascript object and transmits function created by getSubmitFunction() to it as constructor argument.

            3. If one of the components clicked javascript objects creates ajax request that causes JSF to fire DataScrollEvent on server side.

            4. Event listener calls to broadcast() in UIDatascroller

            5. UIDataScroller checks if it DataScrollerEvent and checks what page is selected, if true.

             

            Igor

            • 18. Re: CDK - creating a new component with action
              nbelaevski

              Yes.

              • 19. Re: CDK - creating a new component with action
                igorg

                Thanks Nick. It is great when I right. The problem that my implementation of it doesn't works. Debugging shows that  getAttributes() of component returns empty map, so getDayIndex() in component always returns 1. In datascroller there is a table inside component and they do getDataTable().getAttributes() and I don't has it, so I simple write getAttributes(). What wrong with it and where I can find dayIndex parameter from javascript event.

                I submit parts of my implementation here. May be you can see the problem.

                 

                JSPX template:

                {code:xml}

                <?xml version="1.0" encoding="UTF-8"?>
                class="com.sintecmedia.components.renderkit.html.WeekDaysButtonsRenderer"
                baseclass="com.sintecmedia.components.renderkit.WeekDaysButtonsRendererBase"
                component="com.sintecmedia.components.component.UIWeekDaysButtons"
                xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
                <jsp:directive.page
                    import="javax.faces.context.FacesContext,javax.faces.component.UIComponent" />
                <h:scripts>
                    new org.ajax4jsf.javascript.PrototypeScript(),
                    new org.ajax4jsf.javascript.AjaxScript(),
                    /com/sintecmedia/components/renderkit/html/scripts/weekDaysButtons.js
                </h:scripts>
                <f:clientid var="clientId"/>
                <div id="#{clientId}" name="#{clientId}">
                <table>
                <tr>
                    <th><fmt:message key="Days Of The Week" /></th>
                </tr>
                <colgroup>
                    <c:forEach var="day" items="#{this:getDaysOfWeek( component )}">
                        <tr>
                            <c:object var="dayName" type="java.lang.Object" value="#{day}"/>
                            <c:object var="dayIndex" type="java.lang.Integer"
                                   value="#{this:getIndexByDay( component, day )}"/>
                            <input id="#{clientId}_#{day}" name="#{clientId}_#{day}" type="button" value="#{day}"
                                   onclick="#{this:getOnClick(context,component,dayIndex)}"
                                   class="#{component.attributes['styleClass']}"
                                   x:passThruWithExclusions="id,name,type,value,onclick,class">
                            </input>          
                       </tr>
                    </c:forEach>
                </colgroup>
                </table>
                </div>
                <script>
                     new Richfaces.DaySelector( '#clientId}', #this:getSubmitFunction(context,component)}):
                </script>
                </f:root>
                {code:xml}

                 

                 

                javascript:

                 

                {code}

                if (!window.Richfaces) {      window.Richfaces = {}; } Richfaces.DaySelectedEvent = "rich:weekdaysbuttons:ondayselect"; Richfaces.DaySelector = Class.create({      initialize: function(clientId, submitFunction) {           this.element = $(clientId);           this.element.component = this;               this["rich:destructor"] = "destroy";                    Event.observe(this.element, Richfaces.DaySelectedEvent, submitFunction);      },          destroy: function() {           this.element.component = undefined;           this.element = undefined;      },      switchToDay: function(page) {           if (typeof page != 'undefined' && page != null) {                Event.fire(this.element, Richfaces.DaySelectedEvent, {'dayIndex': page});           }      },          next: function() { this.switchToDay("next"); },          previous: function() { this.switchToDay("previous"); },          first: function() { this.switchToDay("first"); },          last: function() { this.switchToDay("last"); },          fastForward: function() { this.switchToDay("fastforward"); },          fastRewind: function() { this.switchToDay("fastrewind"); } });

                {code}

                 

                Methods relevant from renderer:

                 

                {code}

                protected void doDecode(FacesContext context, UIComponent component) {      Map paramMap = getParamMap(context);      String clientId = component.getClientId(context);      String param = (String) paramMap.get(clientId);             if (param == null) {             return;      }             UIWeekDaysButtons dayButtons = (UIWeekDaysButtons) component;      int newDay = dayButtons.getDayIndex();      int day = dayButtons.getCurrentDayIndex();      if( newDay != 0 && newDay != day ) {      DaySelectedEvent event =           new DaySelectedEvent( dayButtons, String.valueOf(day), param, newDay );                event.queue();      }        } private Map getParamMap(FacesContext context) {         return context.getExternalContext().getRequestParameterMap(); } public String getOnClick(      FacesContext context, UIComponent component, Integer dayIndex ) {      return      "Event.fire(this, 'rich:weekdaysbuttons:ondayselect', {'dayIndex': '" + dayIndex + "'});";    

                } public Integer getIndexByDay( UIComponent component, Object dayName ) {      return      Calendar.getInstance().getDisplayNames(                Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH           ).get( dayName.toString() ); }      public String getSubmitFunction(      FacesContext context, UIComponent component ) {      JSFunctionDefinition definition = new JSFunctionDefinition("event");                   JSFunction function =      AjaxRendererUtils.buildAjaxFunction( component, context );      Map eventOptions =             AjaxRendererUtils.buildEventOptions( context, component, true );      Map parameters = (Map) eventOptions.get( "parameters" );      Map params = getParameters( context,component );      if(!params.isEmpty()){             parameters.putAll(params);      }              parameters.put(      component.getClientId(context),      new JSLiteral("event.memo.page") );              function.addParameter(eventOptions);      StringBuffer buffer = new StringBuffer();      function.appendScript(buffer);      buffer.append("; return false;");      String onDayChange = (String) component.getAttributes().get("ondayselect");      if (onDayChange != null && onDayChange.length() != 0) {          JSFunctionDefinition onDayChangeDef = new JSFunctionDefinition("event");          onDayChangeDef.addToBody(onDayChange);          onDayChangeDef.addToBody("; return true;");          definition.addToBody("if (");          definition.addToBody(onDayChangeDef.toScript());          definition.addToBody("(event)) {");          definition.addToBody(buffer.toString());          definition.addToBody("}");      } else {          definition.addToBody(buffer.toString());      }                   return definition.toScript();

                }      //get UIParameter's Map protected Map getParameters(FacesContext context, UIComponent component){      Map parameters = new HashMap();               if( component instanceof UIWeekDaysButtons ){           UIWeekDaysButtons weekDaysButtons = (UIWeekDaysButtons)component;            List children = weekDaysButtons.getChildren();            for (Iterator iterator = children.iterator(); iterator.hasNext();) {           UIComponent child = (UIComponent) iterator.next();           if(child instanceof UIParameter) {           UIParameter param = (UIParameter)child;           String name = param.getName();           if (name != null) {                parameters.put(name, param.getValue());           }           }        }     }                      return parameters; }     public void encodeChildren(FacesContext context, UIComponent component)     throws IOException {      renderChildren(context, component); } public Object getValue(UIComponent uiComponent) {      if (uiComponent instanceof ValueHolder) {           return ((ValueHolder) uiComponent).getValue();      }      return uiComponent.getAttributes().get("value"); } public String getType(UIComponent uiComponent) {      String type;      if (uiComponent instanceof HtmlCommandButton) {           type = ((HtmlCommandButton) uiComponent).getType();      } else {           type = (String) uiComponent.getAttributes().get("type");      }      if (type == null) {           type = "button";      }      return type; } protected boolean isSubmitted(FacesContext facesContext,      UIComponent uiComponent) {      // Componet accept only ajax requests.      if (!AjaxContext.getCurrentInstance(facesContext).isAjaxRequest()) {      return false;      }      if (getUtils().isBooleanAttribute(uiComponent, "disabled")) {      return false;      }     

                     String clientId = uiComponent.getClientId(facesContext);      Map<String, String> paramMap = facesContext.getExternalContext().getRequestParameterMap();      Object value = paramMap.get(clientId);      boolean submitted = null != value;      if (submitted && _log.isDebugEnabled()) {      _log.debug("Decode submit of the Ajax component " + clientId);      }      return submitted; }         

                /** * * @param component UIWeeklyCalendar component used. * @return list of days from component data model as it called in default java.util.Calendar intance */ public List<String> getDaysOfWeek( UIComponent component ) {      //Days map returned from calendar relays on day's name as key, so it      //needs refinement by day's index.      Map<String, Integer> daysMap =           Calendar.getInstance().getDisplayNames(                     Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH );      String[] names = new String[ daysMap.size() ];      for( String name : daysMap.keySet() ) {           names[ daysMap.get( name ) - 1 ] = name;      }                         List<String> result = new ArrayList<String>( 7 );      for( int i = 0; i < 7; i++ ) {                         result.add( names[i] );      }      return result; }

                {code}

                 

                component methods:

                 

                {code}

                public abstract class UIWeekDaysButtons extends AjaxInputComponent implements ActionSource2{    

                public static final String COMPONENT_TYPE =      "com.sintecmedia.components.component.CommandButton"; public static final String DAY_INDEX_ATTRIBUTE =      COMPONENT_TYPE + ":dayIndex"; private Integer currDayIndex = null;           public void processAction(ActionEvent actionEvent)           throws AbortProcessingException {      System.out.println("processAction entry"); }      public DaySelectedListener[] getDaySelectedListeners() {      return (DaySelectedListener[]) getFacesListeners(                DaySelectedListener.class); } public void broadcast( FacesEvent event ) throws AbortProcessingException {      if( event instanceof DaySelectedEvent ) {           System.out.println(

                               "dayselectedevent.getComponent().getId():    " + event.getComponent().getId());           DaySelectedEvent dataScrollerEvent = (DaySelectedEvent) event;        int index = dataScrollerEvent.getDayIndex();        System.out.println( "dayIndex=" + index );        FacesContext context = getFacesContext();        MethodExpression scrollerListener = getDaySelectedListener();        if( scrollerListener != null ) {             scrollerListener.invoke( context.getELContext(), new Object[]{ event } );        }      } else if( event instanceof ActionEvent ) {           System.out.println("event.getComponent().getId():    " + event.getComponent().getId());        System.out.println("this.getChildCount()   " +this.getChildCount());               } else {        System.out.println("Is not implements ActionEvent" );        super.broadcast(event);      } } public ActionListener[] getActionListeners() {      ActionListener al[] = (ActionListener [])          getFacesListeners(ActionListener.class);      return (al); }      public void addActionListener(ActionListener listener) {      addFacesListener(listener); }      public void removeActionListener(ActionListener listener) {         removeFacesListener(listener); }      public abstract MethodExpression getDaySelectedListener();

                public abstract void setDaySelectedListener(           MethodExpression scrollerListener );      public int getCurrentDayIndex() {           return currDayIndex != null ? currDayIndex : 0; }

                public int getDayIndex() {       Map<String, Object> attributes = getAttributes();       Integer indexAttr = (Integer) attributes.get( DAY_INDEX_ATTRIBUTE );       if( indexAttr != null ) {            return indexAttr;       }             if( currDayIndex != null ) {            return currDayIndex;       }       ValueExpression ve = getValueExpression("dayIndex");       if (ve != null) {            try {            Integer pageObject =

                                    (Integer) ve.getValue(getFacesContext().getELContext());

                           if (pageObject != null) {                 return pageObject;            } else {                 return 1;            }            } catch (ELException e) {            throw new FacesException(e);            }      } else {            return 1;      }      }

                }
                {code}
                • 20. Re: CDK - creating a new component with action
                  nbelaevski

                  Igor,

                   

                  The code has lost formatting, so it's very hard to read it. Please update it using syntax highlighting available in editor menu. Event better, attach zipped projects for component code & demo code.

                  • 21. Re: CDK - creating a new component with action
                    igorg

                    No problem, Nick. I'll attach the files at Sunday when I'll be back at work.

                    By the way what syntax hilighting you wrote about. The only option I see is to add code or code:xml. I added it, but it doesn't improve anything.

                    Igor.

                    • 22. Re: CDK - creating a new component with action
                      nbelaevski

                      Igor,

                       

                      There are two things that caught my eye:

                       

                      1) Why does input component queue ActionEvent. That's responsibility of command component.

                      2) In this code:

                       

                      public int getDayIndex() {
                            Map<String, Object> attributes = getAttributes();
                            Integer indexAttr = (Integer) attributes.get( DAY_INDEX_ATTRIBUTE );
                            if( indexAttr != null ) {
                                 return indexAttr;
                            }

                      you are reading component attributes, but why do you do it?

                       

                      Also where is the setter for "dayIndex"?

                      • 23. Re: CDK - creating a new component with action
                        igorg

                        Hi, Nick. Here the answers to your questions:

                        1. I don't know 'Why does input component queue ActionEvent'. It is inheritance from Odelya. Is it can harm the component functionality or it is just needless? In every case I'll remove it if Odelya don't use for something. I'll check it on Sunday.

                        2. My onclick function looks like: Event.fire( this, 'richfaces:weekdaysbuttons:ondayselect', '{dayIndex: ' + dayIndex + '}' );. I can see the exact syntax in code I've posted, but I think that you know how to do it better then me. I thought that dayIndex is stored in component attributes. getDayIndex() doesn't work so I was wrong, but I'll appreciate if you'll explain to me there it stored.

                        3. setDayIndex() method doesn't exist, because I don't need it in my test, but it would be added. It is not a problem to add it, if needed. Are you ask this question because JSF calls it via reflection, for some reason?

                         

                        Igor.

                         

                        Source files were added. Message was edited by: Igor Grinfeld.

                        • 24. Re: CDK - creating a new component with action
                          nbelaevski

                          1. Action events are usually generated by instances of ActionSource interface, but AjaxInputComponent is not such instance.

                          2. Is listener invoked when you click the element? What do you expect when component is clicked: event or change of some bean property? Which one?

                          3. Setter can be called by reflection if user specifies dayIndex="..." on the page.

                          • 25. Re: CDK - creating a new component with action
                            igorg

                            1. I've spoked with Odelya about 'ActionInputComponent'. The reason of the fact that UIWeekDaysButtons extends it the old copy paste. I've already  changed it to AjaxActionComponent.

                            2. I expect event when user clicked one of the days, but I also want to update inner property of UIWeekDaysButtons to save last selection. If all things will work as I want the developer will have two options: to add listener and wait for events or to write value="#{bean.selectedDays}" to synchronize user bean with set of days selected in component.

                            3. At this moment user can select only one day, but the component supposed to be multiselect. I added getDayIndex property to component just for me, because I thought that it will be more simple to work with one selection untill I'll solve other problems.

                             

                                Nick don't see the code I've sent as last design. It has pieces added by Odelya, by me and one another person in our team when we tried to make it work. That was the reason that I didn't attached all code from the beginning. The usecase I try to force to work is: user clicks on one of the day buttons -> DateSelctEvent with day clicked index created in decode() -> the event caught in broadcast() -> component's dayFlagArray updated. The problem that I don't know how to check what button clicked when I inside decode(). The way you did it in datascroller doesn't work for me because it is not facet and there is no dataModel.

                             

                                P.S. I wrote in answer 2, that I expect event, but I've started one another component 'WeeklyCalendar' that should has dataModel bean and I has problem with it too. I've asked two quiestions two months ago and nobody answered. Check it, if you can, please.

                            http://community.jboss.org/message/531976#531976

                            http://community.jboss.org/message/541020#541020

                            • 26. Re: CDK - creating a new component with action
                              igorg

                              Thank you for help with other issues, Nick. May you can help me with this one too. I need to get value of "event.memo.dayIndex" in decode() and create new DaySelectedEvent with it. I've removed all unnessecary code from source, to make it more clear, and attached it to this reply. This is the last issue I have, please help me to deal with it.

                               

                              Igor.

                              • 27. Re: CDK - creating a new component with action
                                nbelaevski

                                Looked over your code quickly. "param" in doDecode() should contain the value selected by the user. Does this actually happen?

                                • 28. Re: CDK - creating a new component with action
                                  igorg

                                  Thanks Nick. It is exactly what I need. Now

                                  Igor

                                  • 29. Re: CDK - creating a new component with action
                                    igorg

                                    Nick, may be article about how to extend existing components is needed. I think, the task to add some features to existing component is common, but I didn't see such chapter in CDK reference. I miss some simple things because I didn't knew where to see, both in this discussion and in two discussion about calendar components. I can try to create such article based on my discussions, or may be anything from CDK team can do it. They know this staff, much better then me.

                                     

                                    Igor.