Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Exception during grouped grid editing while copying row

    Hi,
    Sorry for not providing stand alone test case, but seems to be many factors involved.

    In short: we have ListGrid as part of CanvasItem. This grid is grouped by date field (remark: no error when grid isn't grouped).
    Now if user chooses to copy a selected record and then modify some values in new/copied record and hit Enter (remark: no error when Tab and then Enter is hit) the following error "TypeError: Cannot read property 'startTime' of null" occurs:
    Click image for larger version

Name:	nullRecord.png
Views:	151
Size:	156.9 KB
ID:	266594
    Looking at the code you can see that record (obtained by getCellRecord() function) is null as this is new record editing, so exception is thrown at line ListGrid.js:44282 because of record[field].

    This is SmartClient Version: SNAPSHOT_v13.0d_2021-09-09, but the same error could be spotted in v12.1p_2020-06-19.

    Could you have a look, please?
    Thanks,
    MichalG

    #2
    Hi Michael

    From your description we're not yet sure whether you've hit a framework error or edge case or whether there's some invalid application code that's getting into this state.

    Can you clarify this part of your description:
    "Now if user chooses to copy a selected record and then modify some values in new/copied record and hit Enter"
    How is the user doing this copy? Are you referring to a framework feature to do this copy/paste, or is this done by application code? If the latter, can you show us exactly what this code does.

    Also - is your grid bound to a DataSource? When the user hits enter to save the value is a data-bound save occurring against the server or do you have saveLocally enabled or autoSaveEdits:false?
    If this is a dataBound save, does the transaction appear to be successful from looking at the RPC tab of the developer console?

    It might also help to see a stack trace for the bad case

    Thank you!
    Isomorphic Software

    Comment


      #3
      Thank you for your time.
      Below additional information.

      "How is the user doing this copy? Are you referring to a framework feature to do this copy/paste, or is this done by application code? If the latter, can you show us exactly what this code does."
      This is done by application code but it basically uses startEditingNew() function like this:
      Code:
                                      grid.startEditingNew(selectedRecord);
      "Also - is your grid bound to a DataSource? When the user hits enter to save the value is a data-bound save occurring against the server or do you have saveLocally enabled or autoSaveEdits:false?"
      This grid is bound to DataSource but not directly. It is a canvas of CanvasItem, so hitting Enter stores grid's data as CanvasItem value: autoSaveEdits=true and saveLocally=true.

      Here is a stack trace:
      Code:
      10:36:26.663:WARN:Log:TypeError: Cannot read property 'startTime' of null
      Stack from error.stack:
          cons._editCompleteCallback(<no args: exited>) on[ListGrid ID:isc_LG_3] @ ListGrid.js:44282:99
          cons._saveLocally(<no args: exited>) on[ListGrid ID:isc_LG_3] @ ListGrid.js:43905:10
          cons.saveEditedValues(<no args: exited>) on[ListGrid ID:isc_LG_3] @ ListGrid.js:43723:21
          cons.saveEdits(<no args: exited>) on[ListGrid ID:isc_LG_3] @ ListGrid.js:43145:26
          cons._saveAndHideEditor(<no args: exited>) on[ListGrid ID:isc_LG_3] @ ListGrid.js:42292:14
          cons.cellEditEnd(<no args: exited>) on[ListGrid ID:isc_LG_3] @ ListGrid.js:41688:14
          cons.editorKeyPress(<no args: exited>) on[ListGrid ID:isc_LG_3] @ ListGrid.js:13355:18
          cons.itemKeyPress(<no args: exited>) on [DynamicForm ID:isc_DynamicForm_2] @ ListGrid.js:13232:30
          cons.handleItemKeyPress(<no args: exited>) on [DynamicForm ID:isc_DynamicForm_2] @ DynamicForm.js:10005:21
          cons._fireKeyPressHandlers(<no args: exited>) on [FloatItem ID:isc_FloatItem_11 name:workingTime] @ FormItem.js:18909:30
          cons.handleKeyPress(<no args: exited>) on [FloatItem ID:isc_FloatItem_11 name:workingTime] @ FormItem.js:18742:30
          cons.invokeSuper(<no args: exited>) on [FloatItem ID:isc_FloatItem_11 name:workingTime] @ Class.js:1668:44
          cons.Super(<no args: exited>) on [FloatItem ID:isc_FloatItem_11 name:workingTime] @ Class.js:1485:21
          cons.handleKeyPress(<no args: exited>) on [FloatItem ID:isc_FloatItem_11 name:workingTime] @ TextItem.js:2099:30
          cons.bubbleEvent(<no args: exited>) on [Class EventHandler] @ EventHandler.js:6049:44
          cons.handleKeyPress(<no args: exited>) on [Class EventHandler] @ EventHandler.js:1787:32
          cons._handleNativeKeyPress(<no args: exited>) on [Class EventHandler] @ EventHandler.js:1687:24
          cons.dispatch(handler=>[c]EventHandler._handleNativeKeyPress(), event=>[object KeyboardEvent]) on [Class EventHandler] @ EventHandler.js:7828:30
          HTMLDocument.eval(event=>[object KeyboardEvent]) @ [no file]:3:127
      What I also tried is slight temporary modification of offending line of code in _editCompleteCallback() function from:
      Code:
                  var mustRefresh = this.isDrawn() && !this.isDirty() && !this.body.isDirty() &&
                                      !this.fieldValuesAreEqual(fieldObj, submittedValues[field], record[field]);
      to:
      Code:
                  var mustRefresh = this.isDrawn() && !this.isDirty() && !this.body.isDirty() &&
                                      !this.fieldValuesAreEqual(fieldObj, submittedValues[field], record != null ? record[field] : null);
      and brief testing shows it helps.

      It is worth to note that in the very same situation, different only that no grouping in the grid is done, there is no exception.
      What I noticed while debugging is that when grouping is off then
      Code:
      !this.isDirty()
      condition is not met, so record which is null in both cases is not considered.

      Thanks again,
      MichalG

      Comment


        #4
        Hi Michael
        I think we may ultimately need a test case to pursue this further.

        The method that is crashing for you - "editCompleteCallback()" - should be executed at the end of a successful save, *after* the grid's data has been updated with the new or updated record. The stack trace you've shared appears to confirm this pattern in your case - the internal _saveLocally() method invokes this callback after updating the data.
        As such, when editCompleteCallback runs we wouldn't expect the 'record' to be null, so it seems like your application code is somehow getting into an invalid state.

        It's possible that there's an edge case where this can validly occur (in which case you've hit a framework bug) but we're not seeing any obvious path for this to occur.

        Here's a simple test case, of the basic premise, based on the grouped editing example here: https://www.smartclient.com/smartcli...groupedEditing

        It doesn't wrap the grid up in a CanvasItem or anything but it's not clear how that would be related to this issue.

        Code:
        var countryList = isc.ListGrid.create({
          ID: "countryList",
          saveLocally:true,
          width:450, height:224,
          alternateRecordStyles:true,
        
          dataSource: countryDS,
          // display a subset of fields from the datasource
          fields:[
            {name:"countryName"},
            {name:"government"},
            {name:"continent"}
          ],
          canEdit: true,
          groupStartOpen:"all",
          groupByField: 'continent',
          autoFetchData: true
        });
        
        isc.Button.create({
          left:500, title:"CopyPasteRecord",
          click : function () {
            // Duplicate the record (and clear the primary key so we don't have duplicate PK's)
            var record = isc.addProperties({}, countryList.getSelectedRecord());
            delete record.pk;
        
            countryList.startEditingNew(record);
          }
        });
        If you'd like us to pursue this further, please see if you can come up with a test case we can use to actually reproduce the problem

        EDIT: We realize this is a SmartGWT question rather than a SmartClient question. The information we've shared still holds, but we realize that the sample JavaScript code is likely to be confusing.
        Here's some equivalent code written in Java for SmartGWT, based on the sample here: https://www.smartclient.com/smartgwt...ouping_editing

        Code:
        public class GroupedEditingSample implements EntryPoint {
        
          public void onModuleLoad() {
        
          CountryXmlDS dataSource = CountryXmlDS.getInstance();
        
          final ListGrid countryGrid = new ListGrid();
          countryGrid.setCanEdit(true);
        
          countryGrid.setWidth(450);
          countryGrid.setHeight(224);
          countryGrid.setCanEdit(true);
          countryGrid.setGroupStartOpen(GroupStartOpen.ALL);
          countryGrid.setGroupByField("continent");
          countryGrid.setDataSource(dataSource);
          countryGrid.setSaveLocally(true);
        
          ListGridField countryCodeField = new ListGridField("countryCode", "Flag", 40);
          countryCodeField.setAlign(Alignment.CENTER);
          countryCodeField.setType(ListGridFieldType.IMAGE);
          countryCodeField.setImageURLPrefix("flags/16/");
          countryCodeField.setImageURLSuffix(".png");
          countryCodeField.setCanEdit(false);
        
          ListGridField nameField = new ListGridField("countryName");
        
          ListGridField governmentField = new ListGridField("government");
          ListGridField continentField = new ListGridField("continent");
          countryGrid.setFields(countryCodeField, nameField, governmentField, continentField);
        
          countryGrid.setAutoFetchData(true);
        
          countryGrid.draw();
        
          Button copyPasteButton = new Button("Copy and paste record");
          copyPasteButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
             ListGridRecord editRecord = new ListGridRecord();
             JSOHelper.addProperties(editRecord.getJsObj(), countryGrid.getSelectedRecord().getJsObj());
             editRecord.setAttribute("pk", (Object)null);
            countryGrid.startEditingNew(editRecord);
           }
          });
         }
        }

        Thanks and regards
        Isomorphic Software
        Last edited by Isomorphic; 8 Oct 2021, 13:40.

        Comment


          #5
          Yes, Yes, Yes! I finally managed to spot it in stand alone test case:
          Code:
          package pl.com.tech4.client;
          
          import com.google.gwt.core.client.EntryPoint;
          import com.smartgwt.client.data.DataSource;
          import com.smartgwt.client.data.DataSourceField;
          import com.smartgwt.client.data.OperationBinding;
          import com.smartgwt.client.data.fields.DataSourceDateField;
          import com.smartgwt.client.data.fields.DataSourceDateTimeField;
          import com.smartgwt.client.data.fields.DataSourceFloatField;
          import com.smartgwt.client.data.fields.DataSourceTextField;
          import com.smartgwt.client.types.DSDataFormat;
          import com.smartgwt.client.types.DSOperationType;
          import com.smartgwt.client.types.DSProtocol;
          import com.smartgwt.client.types.GroupStartOpen;
          import com.smartgwt.client.widgets.Button;
          import com.smartgwt.client.widgets.events.ClickEvent;
          import com.smartgwt.client.widgets.events.ClickHandler;
          import com.smartgwt.client.widgets.form.DynamicForm;
          import com.smartgwt.client.widgets.form.fields.CanvasItem;
          import com.smartgwt.client.widgets.form.fields.FormItem;
          import com.smartgwt.client.widgets.form.fields.events.FormItemInitHandler;
          import com.smartgwt.client.widgets.form.fields.events.ShowValueEvent;
          import com.smartgwt.client.widgets.form.fields.events.ShowValueHandler;
          import com.smartgwt.client.widgets.grid.ListGrid;
          import com.smartgwt.client.widgets.layout.VLayout;
          
          public class MainEntryPoint implements EntryPoint {
          
              public void onModuleLoad() {
          
                  layout();
              }
          
              private void layout() {
          
                  DataSource timeSheetDs = new DataSource();
                  timeSheetDs.setID("TimeSheet");
                  OperationBinding fetchBinding = new OperationBinding();
                  fetchBinding.setOperationType(DSOperationType.FETCH);
                  fetchBinding.setDataFormat(DSDataFormat.XML);
                  fetchBinding.setDataProtocol(DSProtocol.POSTXML);
                  timeSheetDs.setOperationBindings(fetchBinding);
                  timeSheetDs.setDataURL("TimeSheet.xml");
                  DataSourceField idField = new DataSourceField();
                  idField.setName("id");
                  idField.setPrimaryKey(true);
                  idField.setHidden(true);
                  DataSourceTextField timeSheetNoField = new DataSourceTextField();
                  timeSheetNoField.setName("timeSheetNo");
                  DataSourceField itemsField = new DataSourceField();
                  itemsField.setName("items");
                  itemsField.setMultiple(true);
                      DataSource timeSheetItemDs = new DataSource();
                      timeSheetItemDs.setID("TimeSheetItem");
                      DataSourceField idItemField = new DataSourceField();
                      idItemField.setName("id");
                      idItemField.setPrimaryKey(true);
                      idItemField.setHidden(true);
                      DataSourceDateField workDateField = new DataSourceDateField();
                      workDateField.setName("workDate");
                      DataSourceFloatField workingTimeField = new DataSourceFloatField();
                      workingTimeField.setName("workingTime");
                      DataSourceDateTimeField startTimeField = new DataSourceDateTimeField();
                      startTimeField.setName("startTime");
                      DataSourceDateTimeField endTimeField = new DataSourceDateTimeField();
                      endTimeField.setName("endTime");
                      timeSheetItemDs.setFields(idItemField, workDateField, workingTimeField, startTimeField, endTimeField);
                  itemsField.setTypeAsDataSource(timeSheetItemDs);
                      CanvasItem timeSheetItem = new CanvasItem();
                      final ListGrid itemsLg = new ListGrid();
                      itemsLg.setDataSource(timeSheetItemDs);
                      itemsLg.setUseAllDataSourceFields(true);
                      itemsLg.setWidth(500);
                      itemsLg.setHeight(300);
                      itemsLg.setCanMultiGroup(true);
                      itemsLg.setShowGroupSummaryInHeader(true); //<--problem is here
                      itemsLg.setGroupStartOpen(GroupStartOpen.NONE);
                      itemsLg.setGroupByField("workDate");
                      itemsLg.setShowGroupSummary(true);
                      itemsLg.setAutoSaveEdits(true);
                      itemsLg.setSaveLocally(true);
          
                      timeSheetItem.setCanvas(itemsLg);
                      timeSheetItem.setShouldSaveValue(true);
                      timeSheetItem.setInitHandler(new FormItemInitHandler() {
                          public void onInit(FormItem item) {
                              CanvasItem ci = (CanvasItem) item;
                              ci.addShowValueHandler(new ShowValueHandler() {
                                  public void onShowValue(ShowValueEvent event) {
                                      CanvasItem item = (CanvasItem) event.getSource();
                                      ListGrid lg = (ListGrid) item.getCanvas();
                                      lg.setData(event.getDataValueAsRecordList());
                                  }
                              });
                          }
                      });
                  itemsField.setEditorProperties(timeSheetItem);
                  timeSheetDs.setFields(idField, timeSheetNoField, itemsField);
          
                  DynamicForm form = new DynamicForm();
                  form.setDataSource(timeSheetDs);
                  form.fetchData();
          
                  Button copyButton = new Button("Copy");
                  copyButton.addClickHandler(new ClickHandler() {
                      @Override
                      public void onClick(ClickEvent event) {
                          itemsLg.startEditingNew(itemsLg.getSelectedRecord());
                      }
                  });
          
                  VLayout layout = new VLayout();
                  layout.addMembers(form, copyButton);
          
                  layout.draw();
              }
          }
          This TimeSheet.xml data file is needed to run above example:
          Code:
          <response>
              <requestId>TimeSheet_request5</requestId>
              <startRow>0</startRow>
              <endRow>1</endRow>
              <totalRows>1</totalRows>
              <data>
                  <TimeSheet>
                      <id>125</id>
                      <createdOn>2021-09-03T07:28:30Z</createdOn>
                      <modifiedOn>2021-09-23T12:57:28Z</modifiedOn>
                      <version>10</version>
                      <createdBy>
                          <id>jacekr</id>
                          <code>jacekr</code>
                          <name>Jacek</name>
                          <surname>Rymarz</surname>
                      </createdBy>
                      <modifiedBy>
                          <id>jacekr</id>
                          <code>jacekr</code>
                          <name>Jacek</name>
                          <surname>Rymarz</surname>
                      </modifiedBy>
                      <aclGroup>
                          <id>0</id>
                          <code>Grupa zerowa</code>
                      </aclGroup>
                      <period>
                          <id>104</id>
                          <code>09</code>
                          <description>wrzesień 2021</description>
                          <year>
                              <id>24</id>
                              <code>21</code>
                              <description>2021</description>
                          </year>
                      </period>
                      <worker>
                          <id>253</id>
                          <department>
                              <id>2</id>
                              <code>DUR</code>
                              <description>Dział Utrzymania Ruchu</description>
                          </department>
                          <code>1300</code>
                          <personalData>
                              <lastName>Niepękalski</lastName>
                              <firstName>Marek</firstName>
                          </personalData>
                      </worker>
                      <timeSheetNo>000074</timeSheetNo>
                      <closed>false</closed>
                      <items>
                          <TimeSheetItem>
                              <id>421</id>
                              <workOrder>
                                  <id>67835</id>
                                  <status>
                                      <id>2</id>
                                      <code>Trwające</code>
                                      <description>W trakcie realizacji</description>
                                      <styleName>VIOLET</styleName>
                                  </status>
                                  <orderNo>028410</orderNo>
                                  <description>Wymiana łoźysk</description>
                              </workOrder>
                              <workDate>2021-09-03</workDate>
                              <workingTime>1.53</workingTime>
                              <startTime>2021-09-03T07:28:00Z</startTime>
                              <endTime>2021-09-03T09:00:00Z</endTime>
                              <pending>false</pending>
                              <activity>wymieniono łożyska</activity>
                          </TimeSheetItem>
                          <TimeSheetItem>
                              <id>423</id>
                              <workOrder>
                                  <id>67838</id>
                                  <status>
                                      <id>7</id>
                                      <code>Planowane</code>
                                      <description>Prace ujęte w planie</description>
                                      <styleName>GREEN</styleName>
                                  </status>
                                  <orderNo>028413</orderNo>
                                  <description>wymiana łożysk</description>
                              </workOrder>
                              <workDate>2021-09-03</workDate>
                              <workingTime>0.23</workingTime>
                              <startTime>2021-09-03T10:30:00Z</startTime>
                              <endTime>2021-09-03T10:44:00Z</endTime>
                              <pending>false</pending>
                              <activity>Wymieniono łożyska</activity>
                          </TimeSheetItem>
                          <TimeSheetItem>
                              <id>424</id>
                              <workOrder>
                                  <id>67838</id>
                                  <status>
                                      <id>7</id>
                                      <code>Planowane</code>
                                      <description>Prace ujęte w planie</description>
                                      <styleName>GREEN</styleName>
                                  </status>
                                  <orderNo>028413</orderNo>
                                  <description>wymiana łożysk</description>
                              </workOrder>
                              <workDate>2021-09-06</workDate>
                              <workingTime>8</workingTime>
                              <startTime>2021-09-06T04:00:00Z</startTime>
                              <endTime>2021-09-06T12:00:00Z</endTime>
                              <pending>false</pending>
                          </TimeSheetItem>
                          <TimeSheetItem>
                              <id>425</id>
                              <workOrder>
                                  <id>68047</id>
                                  <status>
                                      <id>2</id>
                                      <code>Trwające</code>
                                      <description>W trakcie realizacji</description>
                                      <styleName>VIOLET</styleName>
                                  </status>
                                  <orderNo>028610</orderNo>
                                  <description>wymienić sito</description>
                              </workOrder>
                              <workDate>2021-09-20</workDate>
                              <workingTime>8</workingTime>
                              <startTime>2021-09-20T04:00:00Z</startTime>
                              <endTime>2021-09-20T12:00:00Z</endTime>
                              <pending>false</pending>
                          </TimeSheetItem>
                          <TimeSheetItem>
                              <id>426</id>
                              <workOrder>
                                  <id>68050</id>
                                  <status>
                                      <id>7</id>
                                      <code>Planowane</code>
                                      <description>Prace ujęte w planie</description>
                                      <styleName>GREEN</styleName>
                                  </status>
                                  <orderNo>028612</orderNo>
                                  <description>Przegląd okresowy ze względu na ilość przepracowanych motogodzin</description>
                              </workOrder>
                              <workDate>2021-09-20</workDate>
                              <workingTime>0.22</workingTime>
                              <startTime>2021-09-20T06:29:00Z</startTime>
                              <endTime>2021-09-20T06:42:00Z</endTime>
                              <pending>false</pending>
                              <activity>przegląd</activity>
                          </TimeSheetItem>
                          <TimeSheetItem>
                              <id>427</id>
                              <workOrder>
                                  <id>67965</id>
                                  <status>
                                      <id>4</id>
                                      <code>Ukończone</code>
                                      <description>Prace zostały wykonane ale nie są jeszcze rozliczone</description>
                                      <styleName>BLUE</styleName>
                                  </status>
                                  <orderNo>028528</orderNo>
                                  <description>zacięty silnik</description>
                              </workOrder>
                              <workDate>2021-09-23</workDate>
                              <workingTime>0.8</workingTime>
                              <startTime>2021-09-23T12:09:00Z</startTime>
                              <endTime>2021-09-23T12:57:00Z</endTime>
                              <pending>false</pending>
                              <activity>wymiana silnika</activity>
                          </TimeSheetItem>
                      </items>
                      <workingTimeTotal>18.78</workingTimeTotal>
                      <overTimeATotal>0</overTimeATotal>
                      <overToReturnATotal>0</overToReturnATotal>
                      <overTimeBTotal>0</overTimeBTotal>
                      <overToReturnBTotal>0</overToReturnBTotal>
                      <nightWorkTotal>0</nightWorkTotal>
                  </TimeSheet>
              </data>
              <requestedDataSource>TimeSheet</requestedDataSource>
              <requestedOperationType>fetch</requestedOperationType>
              <status>STATUS_SUCCESS</status>
          </response>
          Run it and open node "20.09.2021". Then select first record in that node.
          Press "Copy" button and modify new record as follows:
          - change "Working date" from "20.09.21" to today
          - change "Working time" to "0.8" to "1"
          Still with focus in "Working time" field hit Enter.
          You should get "TypeError: Cannot read property 'startTime' of null" exception like on following video screen shot:
          Click image for larger version

Name:	groupedGridEditProblem.gif
Views:	112
Size:	1.14 MB
ID:	266639
          What I noticed that among other factors required to rise this ex there is one more:
          we are using this setting for grouped grids:
          Code:
                      itemsLg.setShowGroupSummaryInHeader(true); //<--problem is here
          If comment out above line then everything works as expected.
          HTH
          Thanks,
          MichalG

          Comment


            #6
            Hi,
            Have you got a chance to look into this?
            Thanks,
            MichalG

            Comment


              #7
              Hi Michael
              One obvious thing that's going on in this test case is the fact that when you "copy/paste" a record you're setting up a new edit record with the primary key value set to the pk of an existing record in the data set.
              This will confuse the editing code - the primary key value is used to look up the record from its edit-session while ensuring the editor shows up in the right place and performing various other actions.
              If you duplicate the selected record and give it a new PK, this may resolve your problem..

              Try something like this:

              Code:
              Record newRecordValues = itemsLg.getDataSource().copyRecord(itemsLg.getSelectedRecord());
              newRecordValues.setAttribute("id", "new_id");
              itemsLg.startEditingNew(newRecordValues);
              Obviously the "new_id" should be a calculated value. Or, if you're doing a databound save against a dataSource and the pk field is type sequence, you should be able to rely on the server populating this field for you - the important thing is to not set it to a colliding value as the current code does.

              If the problem persists, please let us know

              Thanks
              Isomorphic Software
              Last edited by Isomorphic; 1 Nov 2021, 15:19. Reason: Used a more appropriate API to duplicate and update the record

              Comment


                #8
                Thanks for this tip:
                the "new_id" .. the important thing is to not set it to a colliding value as the current code does
                Clearing id in a new record solved the problem.
                I very much appreciate it.
                MichalG

                Comment

                Working...
                X