1 Reply Latest reply on Jul 2, 2011 6:18 PM by Nick Belaevski

    hi. about <rich:dataTable/> and get the row model object...

    imcharsi Newbie

      hi

       

      at first, i'm not native english speaker.

       

      this text is not bug reporting, instead this is my suggestion.

       

      while i study richfaces, i found something not handy function for <rich:dataTable/>

       

      first, let's look at below code.

       

      ================

      <rich:dataTable value="...">
          ...
          <rich:column>
              <a4j:commandButton actionListener="#{someBean.actLstn}" ...>
                  <a4j:ajax event="click" execute="@this" .../>
              </a4j:commandButton>
          </rich:column>
      </rich:dataTable>
      

       

      @ManagedBean
      @WhatEverScoped
      public class SomeBean implements WhatEver, Blah {
          public void actLstn(ActionEvent event) {
              ...
              return;
          }
      }
      

      =================

       

      at here, what i want to know is which row is selected with <a4j:commandButton/>

       

      so, as richfaces component document, we can use <a4j:param/>.

       

      code is below.

       

      ==============

      <rich:dataTable value="..." var="row">
          ...
          <rich:column>
              <a4j:commandButton actionListener="#{someBean.actLstn}" ...>
                  <a4j:ajax event="click" execute="@this" .../>
                  <a4j:param value="#{row.somevalue}" assignTo="#{someBean.dest}"/>
              </a4j:commandButton>
          </rich:column>
      </rich:dataTable>
      

       

      @ManagedBean
      @WhatEverScoped
      public class SomeBean implements WhatEver, Blah {
          public void actLstn(ActionEvent event) {
              ...
              return;
          }
          
          public SomeType getDest() {return dest;}
          public void setDest(SomeType s) {dest=s;}
      }
      

      ================

       

      but i thought this style is not cleanly.

       

      so i search another way that is UIDataAdaptor.getRowKey/setRowKey api method.

      this UIDataAdaptor is UIDataTable's one of super classes.

       

      so code may be as follow.

       

      ==============

      <rich:dataTable value="..." var="row" binding="#{someBean.table}">
          ...
          <rich:column>
              <a4j:commandButton actionListener="#{someBean.actLstn}" ...>
                  <a4j:ajax event="click" execute="@this" .../>
              </a4j:commandButton>
          </rich:column>
      </rich:dataTable>
      

       

      @ManagedBean
      @WhatEverScoped
      public class SomeBean implements WhatEver, Blah {
          public void actLstn(ActionEvent event) {
              ...
              table.getRowKey();
              return;
          }
      
          UIDataTable table;
          public UIDataTable getTable() {return table;}
          public void setTable(UIDataTable t) {table=t;}    
      }
      

      ====================

       

      as this code, we can know which row is selected with dataTable component's inner implementation before start our "actLstn" method.

       

      i thought this style is more cleanly, but i didnt like this style too.

      at first, let's assume we dont use rowKeyConverter.

      if we want to get actually model object, we have to calculate with "rowKey" which means selected row number.

       

      is it possible that the component give model object itself to our, instead of we find model object?

       

      for example, usecase is below.

       

      =======================

      <rich:dataTable value="..." newSelectedRow="#{someBean.target}">
          ...
          <rich:column>
              <a4j:commandButton actionListener="#{someBean.actLstn}" ...>
                  <a4j:ajax event="click" execute="@this" .../>
              </a4j:commandButton>
          </rich:column>
      </rich:dataTable>
      

       

      @ManagedBean
      @WhatEverScoped
      public class SomeBean implements WhatEver, Blah {
          public void actLstn(ActionEvent event) {
              ...
              target.doSomeMethod();
              return;
          }
      
          SomeData target;
          public SomeData getTarget() {return target;}
          public void setTarget(SomeData d) {target=d;}
      }
      

      ========================

       

      ideally, we should be able to get actually model object through "target" property which is updated at "update model phase", and to use updated "target"  property.

      if we can do this, we dont have to use <a4j:param/> or <f:setPropertyActionListener/> or calculating with "rowKey".

       

      first, i wrote my understanding for UIDataAdaptor's executing.

       

      1. find component which fire user event, with visit.

      2. in that component, make event. (for example, ActionEvent..., next, call component.queueEvent(...)).

      3. UIDataAdaptor.queueEvent() will be called by chaining super.queueEvent(...) call.

      4. in this method, the event is wrapped for special usecase.

      5. after, this event is processed at specified phase(getPhaseId() {return wrappedEvent.getPhaseId();}).

      in this event class's broadcast method, richfaces impl do follow two things.

      5.1. update component's rowKey value.

      5.2. call wrappedEvent.broadcast(...).

      so, rowKey updating and event processing will be right.

      for example, if ActionEvent.phaseId is set InvokeApp phase, this event will be processed at Invoke phase and component value will be updated at same phase too.

       

      so i thought my own patch that meets my needs. summary is follow.

       

      1. we dont have to use special class that is "RowKeyContextEventWrapper".

      2. we should replace UIDataAdaptor.queueEvent as follow.

      2.1. test UIDataAdaptor.rowKey!=null and partialViewContext.isPartialRequest()==true

      2.2. if test is passed, partialViewContext.getExecuteIds().add(getClientId()); of course we have to care duplicated executeIds.

      2.3. and make class field "submittedRowKey" and copy "rowKey" value to "submittedRowKey". this value is not saved with getStateHelper().put(...).

      3. now, UIDataAdaptor's processValidators or processUpdates methods will be processed.

      3.1. in processUpdates, we replace "doUpdate" method.

      3.2. test submittedRowKey!=null && getValueExpression("newSelectedRow")!=null

      3.3. copy. (getValueExpression("newSelectedRow").setValue(...getELContext(), submittedRowKey);)

       

      follow is actually my code in UIDataAdaptor.

       

      =============================

      private Object submittedRowKey;
      
      enum PropertyKeys { ..., newSelectedRow}
      
      @Override
      public void queueEvent(FacesEvent event) {
          /*
           * extension
           */
          /*
          super.queueEvent(wrapEvent(event));
           */
      
          submittedRowKey = rowKey;
          if(submittedRowKey != null && submittedRowKey instanceof Number) {
              PartialViewContext context = FacesContext.getCurrentInstance().getPartialViewContext();
              if(context.isPartialRequest()) {
                  Collection<String> executeIds = context.getExecuteIds();
                  String clientId = getClientId();
                  if(!executeIds.contains(clientId)) {
                      executeIds.add(clientId);
                  }
              }
          }
          super.queueEvent(event);
          /*
           * end
           */
      }
      
      @Attribute
      public Object getNewSelectedRow() {
          return getStateHelper().eval(PropertyKeys.newSelectedRow, null);
      }
      public void setNewSelectedRow(Object o) {
          getStateHelper().put(PropertyKeys.newSelectedRow, o);
      }
      
      protected void doUpdate() {
          /*
           * extension
           */
          ValueExpression valueExpression = getValueExpression("newSelectedRow");
          if(submittedRowKey !=null && submittedRowKey instanceof Number && valueExpression!=null) {
              int oldRowIndex = getExtendedDataModel().getRowIndex();
              int newRowIndex = ((Number) submittedRowKey).intValue();
              getExtendedDataModel().setRowIndex(newRowIndex);
              if(getExtendedDataModel().isRowAvailable()) {
                  valueExpression.setValue(FacesContext.getCurrentInstance().getELContext(), getExtendedDataModel().getRowData());
              }
              getExtendedDataModel().setRowIndex(oldRowIndex);
          }
          /*
           * end
           */
      }
      

      ============================

       

      so let's take above's example.

       

      ====================

      <rich:dataTable value="..." var="row" newSelectedRow="#{someBean.target}">
          ...
          <rich:column>
              <a4j:commandButton actionListener="#{someBean.actLstn}" ...>
                  <a4j:ajax event="click" execute="@this" .../>
              </a4j:commandButton>
          </rich:column>
      </rich:dataTable>
      

       

      @ManagedBean
      @WhatEverScoped
      public class SomeBean implements WhatEver, Blah {
          public void actLstn(ActionEvent event) {
              ...
              target.doSomeMethod();
              return;
          }
      
          SomeData target;
          public SomeData getTarget() {return target;}
          public void setTarget(SomeData d) {target=d;}
      }
      

      ========================

       

      in this example, it's work correctly.

       

       

      indeed, i didnt test many. i just tested with only <rich:dataTable/> and <a4j:commandButton/>.

      i didnt test any <rich:extendedDataTable/> or <rich:dataGrid/> or nested dataTable, etc...

       

      so i cant sure this code is perfectly right.

      what i means is let's think abount function improvement, instead of use this code as exactly this.

       

      i'm sorry maybe you cant understand this text easy because grammar and everything is not good.

       

       

      how do you think about this?