Announcement

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

    Custom, Dynamic editor on grid cell. Is there a better way?

    I'm using Smart GWT Pro version 2.1, GWT 2.0.1

    I'd like to create a grid such that when in the process of editing it, the cell editor changes based on other changes in the row. I'd like it to behave in very much the same way as the FilterBuilder works. When an editor item is changed by the user, the other editors change type in reaction to this.

    For instance, I have a "type" (Enum) as the first column, and if the user selects the first item in the combobox, I'd like the second widget to change from a text to an enum. If the user selects the second item in the enum, I'd like it to be a text area, and so on.

    I have tried setting a custom CanvasItem editor on the ListGridField for the item I want to change and overrode "onDraw()", inspecting the grid's getEditedRecord() to change the members in the canvasitem prior to calling super.onDraw(). This seems to do the right thing in the display, but when I click on the canvas to use the widgets it drew, it takes the grid out of edit mode and I'm not able to interact with it. Is this a bug or is it a flaw in my approach?

    Any ideas?

    #2
    Rather than doing this onDraw(), do it on editorEnter for the item in question, or on changed() for another item that triggers a difference in your custom editor. Then just use show() and hide() on the subcomponents of your CanvasItem to switch the display.

    Comment


      #3
      I've tried moving my code to the editorEnter handler, and it never got called. I moved it to the editorExit of the field driving the change, but I'm getting the same results I got earlier. I get a proper looking editor, but when I try to click it, it takes the row out of edit mode. Below is a simplified example that illustrates what I'm seeing. Am I doing something wrong?

      Code:
      package eg;
      
      import com.google.gwt.core.client.EntryPoint;
      import com.smartgwt.client.data.DataSource;
      import com.smartgwt.client.data.DataSourceField;
      import com.smartgwt.client.data.fields.DataSourceEnumField;
      import com.smartgwt.client.data.fields.DataSourceIntegerField;
      import com.smartgwt.client.types.ListGridEditEvent;
      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.TextAreaItem;
      import com.smartgwt.client.widgets.form.fields.TextItem;
      import com.smartgwt.client.widgets.grid.ListGrid;
      import com.smartgwt.client.widgets.grid.ListGridField;
      import com.smartgwt.client.widgets.grid.ListGridRecord;
      import com.smartgwt.client.widgets.grid.events.EditorExitEvent;
      import com.smartgwt.client.widgets.grid.events.EditorExitHandler;
      import com.smartgwt.client.widgets.layout.VLayout;
      
      public class Example implements EntryPoint
      {
        public void onModuleLoad()
        {
          final ListGrid grid = new ListGrid();
      
          VLayout vl = new VLayout();
          Button b = new Button( "Add row" );
          b.addClickHandler( new ClickHandler()
          {
            public void onClick( ClickEvent event )
            {
              grid.startEditingNew();
            }
          });
      
          
          grid.setDataSource( new LGData() );
      
          ListGridField f2 = new ListGridField( "variable", 300 );
          final CustomCanvasItem c = new CustomCanvasItem();
          f2.setEditorType( c );
      
          ListGridField f1 = new ListGridField( "enum", "Pick me", 100 );
          f1.addEditorExitHandler( new EditorExitHandler()
          {
            @Override
            public void onEditorExit( EditorExitEvent event )
            {
              c.fixForm( grid );
            }
          });
      
          grid.setCanEdit( true );
          grid.setEditByCell( false );
          grid.setShowRecordComponents(true);
          grid.setShowRecordComponentsByCell( true );
          grid.setShowAllRecords( true );
          grid.setValidateByCell( false );
          grid.setEditEvent( ListGridEditEvent.CLICK );
      
          grid.setFields( f1, f2 );
      
          vl.setWidth( 500 );
          vl.setHeight( 500 );
          vl.setMembers( b, grid );
          vl.draw();
        }
      
        public class CustomCanvasItem extends CanvasItem
        {
          private DynamicForm editForm;
      
          public CustomCanvasItem()
          {
            this.editForm = new DynamicForm();
            TextItem f1 = new TextItem( "text", "Text" );
            f1.setShowTitle( false );
            f1.setVisible( false );
            TextAreaItem f2 = new TextAreaItem( "textarea", "Text Area" );
            f2.setShowTitle( false );
            f2.setVisible( false );
            editForm.setFields( f1, f2 );
            setCanvas( editForm );
          }
      
          public void fixForm( ListGrid listGrid )
          {
            if ( listGrid != null )
            {
              try
              {
                setupRecord( ( ListGridRecord ) listGrid.getEditedRecord( listGrid.getEditRow() ) );
              }
              catch ( Throwable t )
              {
                t.printStackTrace();
              }
            }
          }
      
          public void setupRecord( ListGridRecord record )
          {
            String type = record.getAttribute( "enum" );
            if ( type != null )
            {
              FormItem item = new FormItem();
              for ( FormItem i : editForm.getFields() )
              {
                i.setVisible( false );
              }
              if ( "A".equals( type ) )
              {
                item = editForm.getField( "text" );
              }
              else
              {
                item = editForm.getField( "textarea" );
              }
              item.setVisible( true );
              editForm.redraw();
              setHeight( editForm.getHeight() );
              redraw();
            }
          }
      
        }
      
        public class LGData extends DataSource
        {
      
          public LGData()
          {
            DataSourceField field;
      
            field = new DataSourceIntegerField( "id", "Id", 1, true );
            field.setPrimaryKey( true );
            field.setHidden( true );
            addField( field );
      
            field = new DataSourceEnumField( "enum", "Pick me", 50, true );
            field.setValueMap( "A", "B", "C" );
            addField( field );
      
            field = new DataSourceField();
            field.setName( "variable" );
            field.setRequired( true );
            addField( field );
      
          }
      
        }
      
      }

      Comment


        #4
        Hi Ron,

        Just to let you know, we believe there's a bug here regarding focus handling, but we're still looking into it.

        In the meantime, a similar interface could be created with the showRecordComponents functionality that allows you to embed widgets in cells.

        Comment


          #5
          Thank you. I will look into your workaround

          Comment


            #6
            I took your advice and can now create a component for my cell using the ListGrid.createRecordComponent overrides. I ran into a small issue and then a larger one:

            ListGrid.createRecordComponent does not get called if the grid is empty and you do a startEditingNew() call on the grid. I got around this by creating a record and calling startEditing on its index explicitly.

            However, I'm stuck here because I'm not clear on how I can get access to the record components in the listgrid's edit row during an event such as editorExit so that I can change another component in the row. There doesn't seem to be anything in the api doc that is obvious to me pointing me in the right direction.

            I'm also not clear on how the component is supposed to get its "value" or set it back into the record. Is there an example of using this that I missed in the showcase or elsewhere?

            Comment


              #7
              When you're using RecordComponents you are basically independent of the built-in cell editors / editing subsystem. You can set an edit value for a field via setEditValue(), or, you can directly modify the data and just use refreshCell(). It depends on how you ultimately want to do persistence: setting editValues means you can ultimately call saveEdits()/saveAllEdits() to persist changes to a DataSource, but you can also do this manually.

              As far as referencing the component, you most likely just want a single instance of the component that reconfigures itself for each row, and if so, you can give it an ID or store it as an attribute of the grid.

              Comment


                #8
                Hi ron.lawrence,

                Can you please share your solution? That would help me a lot.
                I cant understand how "createRecordComponent" generates cell editors.


                thank you

                Comment


                  #9
                  Originally posted by la123
                  I cant understand how "createRecordComponent" generates cell editors.
                  Here it is my solution: it uses a custom widget as record component to render a hyperlink and reuses the built-in editors.
                  The record component is referenced using an dedicated record attribute and then shown/hidden simply through EditorEnter and EditorExit notifications.
                  Built-inrendering is disabled to avoid strange overlappings between record component and built-in cell formatter.

                  Code:
                  listGrid = new ListGrid () {
                  	@Override
                  	protected com.smartgwt.client.widgets.Canvas createRecordComponent(final ListGridRecord record, Integer colNum) {
                  		String fieldName = getFieldName (colNum);
                  		if ("name".equals (fieldName)) {
                  			final CellWidget cw = new CellWidget (record, listGrid.getDataSource ());
                  			return cw;
                  		} else {
                  			return null;
                  		}
                  	};
                  };
                  listGrid.setShowRecordComponents (true);
                  listGrid.setShowRecordComponentsByCell (true);
                  listGrid.setRecordComponentPosition (EmbeddedPosition.WITHIN);
                  ...
                  final ListGridField nameField = new ListGridField ("name");
                  nameField.setCellFormatter (new CellFormatter() {
                  	@Override
                  	public String format (Object value, ListGridRecord record, int rowNum, int colNum) {
                  		// hide built-in rendering
                  		return null;
                  	}
                  });
                  nameField.addEditorEnterHandler (new EditorEnterHandler() {
                  	@Override
                  	public void onEditorEnter (EditorEnterEvent event) {
                  		CellWidget cellWidget = fromRecord (event.getRecord ());
                  		cellWidget.setVisible (false);
                  	}
                  });
                  nameField.addEditorExitHandler (new EditorExitHandler() {
                  	@Override
                  	public void onEditorExit (EditorExitEvent event) {
                  		CellWidget cellWidget = fromRecord (event.getRecord ());
                  		cellWidget.setContents ((String) event.getNewValue ());
                  		cellWidget.setVisible (true);
                  	}
                  });
                  ...
                  private class CellWidget extends Hyperlink {
                  	public CellWidget (final Record record, DataSource dataSource) {
                  		super (record.getAttribute ("name"));
                  		addHyperLinkEventHandler (new Closure<Object, BrowserEvent<?>>() {
                  				
                  			@Override
                  			public Object call (BrowserEvent<?> input) {
                  				goToEditPage (record);
                  				return null;
                  			}
                  		});
                  		record.setAttribute ("cellwidget", (Object)this);
                  	}
                  }
                  public static CellWidget fromRecord (final Record record) {
                  	return (CellWidget) record.getAttributeAsObject ("cellwidget");
                  }
                  Cheers
                  Davide

                  (using SmartClient Version: SC_SNAPSHOT-2010-12-31/LGPL Development Only (built 2010-12-31))

                  Comment

                  Working...
                  X