Announcement

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

    Tabbing into a custom list grid record component

    We're successfully using createRecordComponent to embed a custom ListGrid inside each row. The only remaining issue is that when you start editing a record and tab through the editable fields in the row, the custom component field is skipped over. How do we get focus on to the custom component in the standard tab sequence?

    #2
    That's necessarily going to be custom (the grid doesn't know the recordComponent is even intended to be involved in editing). So you'll want to force focus to your sub-grid on editorExit of the prior field, then force focus to the next ListGrid field as focus leaves the sub-grid.

    Comment


      #3
      I've tried this a few different ways and have yet to find a solution. I first realized that the ListGridField that contains the component was not being considered as editable. We had a custom override to canEditCell() that was returning false for that field. I changed that so the component field is considered editable and now, as you tab from column to column the cursor moves into the component cell.

      What I can't tell is exactly what UI component is receiving focus when you tab into the component cell. I'd like to put a focusChangedHandler on that object and use it to startEditing on our grid component. I've tried a focusChangedHandler on the component grid itself and on the Canvas that it is a member of. Neither get fired.

      I then added an editorEnterHandler on the component grid field, which generates this warning when tabbing into that field. WARN:ListGrid:isc_PoItemAssortment2DLayout_1_0:suppressing editorEnter handlers on focus as listGrid.$30a is null. Is there some event here I can intercept to start editing on my component grid?

      I tried various ways to implement your suggestion of an editorExitHandler on the field prior to the component grid field. But I was not able to find a way to determine which field is the one with a tab position just prior to the component field. If that is still the best approach, can you tell me how to determine which field to put the editorExitHandler on? Since the users can change column order I'll need to dynamically determine which field comes before the component field to use that approach. ListGrid.getFieldNum() doesn't seem to be based on displayed column order.

      Comment


        #4
        I'm pretty close to what seems to be a good solution. I used setEditorType on the ListGrid field that houses the recordComponent and then used a FocusHandler on that FormItem to put focus into the component and start editing. That works well for getting focus into the component grid when the user tabs to that cell.

        Now I'm trying to handle exiting the component grid so I can restore the cursor back in the main grid, which could have additional columns following the component grid cell. So I have two questions.

        1) How can I tell when the user has tabbed out of the last editable cell in the grid? Essentially what I need is a hook into the ListEndEditAction handler so I can invoke some custom code at that point.

        2) Assuming there is an answer to 1, how can I resume editing with the next editable column?

        Comment


          #5
          You should use editorExit on the embedded grid and just detect last column & row. Move focus back to the main grid with startEditing, which takes coordinates.

          Comment


            #6
            @jay.l.fisher - do you mind posting your code for this? I've been struggling with the ability to do this for some time. Would be much appreciated.

            Comment


              #7
              The trick was using setEditorType for the ListGridField that will contain my custom record component. The createRecordComponent override takes care of creating the actual component for the cell, but setting an editorType for that field as well lets you add a FocusHandler which will be invoked when the user tabs into that grid cell. Once that FocusHandler is invoked it calls startEditing on the recordComponent grid using the componentGrid ID that was stored in the record when the component grid was created.
              Code:
              // Add the size grid record component field
              ListGridField sizeGridComponentField = new ListGridField(SIZE_GRID_COMPONENT, "Size Breakdown");
              sizeGridComponentField.setAlign(Alignment.CENTER);
              sizeGridComponentField.setWidth(350);
              FormItem shellItem = new FormItem();
              shellItem.addFocusHandler(new FocusHandler() {			
              	@Override
              	public void onFocus(FocusEvent event) {
              		String gridId = (String) event.getForm().getValue("gridId");
              		if (gridId!=null) {
              			SizeGridComponent theGridComponent = (SizeGridComponent) Canvas.getById(gridId);
              			ListGrid currSizeGrid = (ListGrid) theGridComponent.getMember(0);
              			currSizeGrid.startEditing();
              		}
              	}
              });
              sizeGridComponentField.setEditorType(shellItem);
              In case you aren't already using this technique, storing the component ID in the main ListGridRecord is accomplished by adding an onDraw handler to the recordComponent when it is created. There is some other stuff going on here as well, but this is the full createRecordComponent method.
              Code:
              protected Canvas createRecordComponent(final ListGridRecord record, final Integer colNum) {
              
              	// Get the field name for the column
              	String fieldName = grid.getFields()[colNum].getName();
              
              	// If this is the size field then create the sizeGrid here
              	if (fieldName.equals(SIZE_GRID_COMPONENT)) {
              
              		// Create sizeGrid
              		final SizeGridComponent sizeListGrid = new SizeGridComponent(sizeGridRecords, true, getValidSizes(record.getAttribute(ICLR)), record.getAttributeAsInt(ICLR), poItemDetailRecords);
              		sizeListGrid.addDrawHandler(new DrawHandler() {
              			@Override
              			public void onDraw(DrawEvent event) {
              				// Set the grids ID as an attribute so we can do a Canvas.getById later
              				record.setAttribute("gridId", sizeListGrid.getID());
              				// Load the data into this grid
              				setSizeGridData(record.getAttribute(ICLR), record.getAttributeAsRecordList(SIZE_GRID_RECORD));
              			}
              		});
              
              		// Add SizeGrid records to ParentGrid record set
              		record.setAttribute(SIZE_GRID_RECORD, ((ListGrid) sizeListGrid.getMember(0)).getRecords());						
              		
              		sizeListGrid.setWidth100();
              //						sizeListGrid.setHeight(175);
              		sizeListGrid.addFocusChangedHandler(new FocusChangedHandler() {		
              			@Override
              			public void onFocusChanged(FocusChangedEvent event) {
              				if (!event.getHasFocus())
              					grid.startEditing(grid.getRecordIndex(record), colNum, false);
              			}
              		});
              		return sizeListGrid;
              	} else
              		return null;
              }
              I haven't gotten to the other part of this, which is returning focus where it belongs after the recordComponent loses focus. Hopefully this will help you in the meantime.

              Isomorphic - are there any plans to eventually allow custom components like this to participate in the standard ListGrid edit form event stream? It seems like this area and the similar issues with CanvasItem participating in DynamicForm values management are areas for improvement.

              Comment


                #8
                Did you already try doing this based on setEditorType() and a CanvasItem and run into some roadblock? That's definitely the more direct way of doing something like this as opposed to tying together recordComponents and a fake editor, although it's certainly plausible there was some bug or limitation in the way of this approach.

                Note there are some new APIs on CanvasItem, new doc and samples that clarify how a CanvasItem is intended to work with the form's value management.

                Comment


                  #9
                  We didn't know enough about setEditorType and the use of CanvasItems when we started down the path of using recordComponents. If we had it to do over again it would probably be a better option. I'll take a look at the CanvasItem doc and samples the next time we need something like this. Thanks.

                  Comment


                    #10
                    Thank you very much Jay. That's really interesting. I had not been using the component ID - it is very handy to be able to interact with the actual component being used instead of just relying on setting properties. I've been reading through the ListGrid.js file to understand the mechanisms better.

                    Isomorphic; good to hear that CanvasItem API is improved. I echo Jay's sentiments that this is one area that could be greatly improved. Being able to override subcomponents of the ListGrid (and other complex components, ie, Calendar) would be very handy.

                    It would be very useful to have a change list for the nightly builds. I presume you keep such a list internally; it would be great if this was public. In the ideal world, if you introduce some new API, also publish some unit test code showing how to use it. For many cases, it is quite hard to use new API code without the examples, since unless you dive into the SmartClient code, you usually can't see what is going on.

                    Comment


                      #11
                      Originally posted by Isomorphic
                      You should use editorExit on the embedded grid and just detect last column & row. Move focus back to the main grid with startEditing, which takes coordinates.
                      I'm able to get control now when the user tabs out of the last field or shift-tabs out of the first field in the gridComponent, but I can't seem to find the right logic for calculating the coordinates to pass to startEditing.

                      I have the coordinates for the gridComponent cell I'm exiting, but how do replicate the same logic that a normal tab or shift-tab would do, i.e. advance to the next/editable cell with the appropriate RowEndEditAction and ListEndEditActions.

                      I can't seem to find a way to get a list of the editable cells in a row in displayed order so I can tell where to startEditing next.

                      Comment


                        #12
                        Isn't this just getFields(), find the field for your custom editor, grab the next and getColNum(fieldName) to get it's column? If you try to startEditing() at that coordinate, you should see that it tries to find the next editable cell for you.

                        Comment


                          #13
                          So can I count on getFields() returning only the visible fields and in their current displayed column order? The javadoc for that method only says " The ListGrid fields".

                          I also need to handle the shift-tab case from the first cell in the component grid. This needs to move editing back to the previous editable cell in that row or, if the component is the first field in the row, to the last cell of the previous row. I need some way of know if the previous cell is editable so I can skip it and keep looking backwards. Otherwise, if I call startEditing on the previous cell and that cell is not editable I'll end up with the cursor back in my original cell.

                          I've almost got it working, with some limitations, but it seems like logic that must be in the framework already so it would be nice to be able to leverage that, as it must also respect settings like ListEndEditAction and RowEndEditAction.

                          Is there some way to fire the TAB or SHIFT_TAB keypress event on a grid cell? That's the exact behavior I'm trying to mimic.

                          Comment

                          Working...
                          X