flow-components icon indicating copy to clipboard operation
flow-components copied to clipboard

Access to the internal API of the Grid [1-2]

Open Flaurite opened this issue 4 months ago • 2 comments

Describe your motivation

In Jmix, we are trying to develop a component based on the Grid. During the process, we faced issues with the private access of some methods and classes of the Grid.

Describe the solution you'd like

We would like to have the ability to override or change the logic of the Grid in the following cases, if possible:

  1. Grid.Column class.

    • A getter method for componentCallback.

      // New property to store the callback
      private SerializableFunction<T, ? extends Component> componentCallback;
      
      // The existing setter
      public Column<T> setEditorComponent(
              SerializableFunction<T, ? extends Component> componentCallback) {
          // ...
          if (editorRenderer != null) {
              editorRenderer.setComponentFunction(componentCallback);
      
              // Here store the component function
              this.componentCallback = componentCallback;
          }
          return this;
      }
      
      public SerializableFunction<T, ? extends Component> getEditorComponentCallback() {
          return componentCallback;
      }
      
    • A getter method for sortOrderProvider:

      public SortOrderProvider getSortOrderProvider() {
          return sortOrderProvider;
      }
      
    • A getter method for comparator.

      public SerializableComparator<T> getComparator() {
          return comparator;
      }
      

    The main goal of these changes is to be able to copy the whole state of a detached column and then apply it to another column (e.g., one newly added to a Grid). Could you clarify if there is a simple way to copy the state from one column to another? Or perhaps is there a better way to do this?

  2. Column data generators.

    Adding a generator using a separate protected method: in constructor and in setRenderer() method.

    • The case in constructor:

      public Column(Grid<T> grid, String columnId, Renderer<T> renderer) {
          // ...
      
          if (dataGenerator.isPresent()) {
              columnDataGeneratorRegistration = addDataGenerator(dataGenerator.get()); // To be used here
          }
      }
      
    • The case in setRenderer() method:

      public Column<T> setRenderer(Renderer<T> renderer) {
          this.renderer = Objects.requireNonNull(renderer,
                  "Renderer must not be null.");
          // ...
      
          columnDataGeneratorRegistration = rendering.getDataGenerator()
                  .map(dataGenerator -> addDataGenerator(dataGenerator)) // To be used here
                  .orElse(null);
      
          // ...
      
          return this;
      }
      
    • The protected addGenerator() method:

      protected Registration addDataGenerator(DataGenerator<T> dataGenerator) {
          return grid.addDataGenerator((DataGenerator) dataGenerator);
      }
      

    In the Jmix component, we should control the generated data from data generators in a Column depending on the item. This component uses data generator wrappers when a new generator is added.
    Is it possible to add a protected addGenerator() method to a Column? Or is there perhaps a better way to override the current logic?

  3. Grid.DetailsManager class.

    • The protected access to the class:
      protected class DetailsManager extends AbstractGridExtension<T> {
         // ...
      }
      
    • A protected separate method for creating an instance:
      protected DetailsManager createDetailsManager() {
          return new DetailsManager(this);
      }
      
    • The method in a constructor:
      protected <U extends GridArrayUpdater, B extends DataCommunicatorBuilder<T, U>> Grid(
         int pageSize,
         SerializableBiFunction<UpdateQueueData, Integer, UpdateQueue> updateQueueBuilder,
         B dataCommunicatorBuilder) {
          //...
      
          detailsManager = createDetailsManager(); // To be used here
      
          // ...
      }
      

    For the same reason as in the previous case (controlling generated data from data generators in a Column), we need to override the DetailsManager in the Grid.
    Is it possible to add a protected method to the Grid to override the DetailsManager? Or is there perhaps a better way to override the current logic?

  4. Grid#generatePartData() and Grid#generateSelectableData() methods

    • The protected access to the generatePartData() method:
      protected void generatePartData(T item, JsonObject jsonObject) {
         // ...
      }
      
    • The protected access to the generateSelectableData():
      protected void generateSelectableData(T item, JsonObject jsonObject) {
          // ...
      }
      

    In the Jmix component, we need to slightly change the logic of the generatePartData() and generateSelectableData() methods, but their access is private. Is there a simpler way to override these methods?

Describe alternatives you've considered

No response

Additional context

No response

Flaurite avatar Oct 21 '25 06:10 Flaurite

Could you clarify a few points regarding your suggested solutions?

  1. Is the case equivalent to adding back a column that has been previously removed via removeColumn? Or would hiding the column instead of removing help?
  2. Regarding introducing API for adding data generators to columns, could you give some examples on what kind of adjustments do you need on the columns?
  3. Again, for modifying the part data and selectable data, can you provide an example on what you are trying to achieve? For example would the combination of setItemSelectableProvider and addDataGenerator not provide the required modification?

ugur-vaadin avatar Oct 22 '25 10:10 ugur-vaadin

Hello @ugur-vaadin,

  1. Yes, this is a case where a removed column is adding back. We abandoned the idea of hiding the column using the visible property because, in our case, users control the visibility through a separate component.

To better understand why some API elements do not suit the Jmix component: the component contains items that, in some cases, should not be accessible through the public API or should be accessible but as a different type. For example, when such an item is clicked, we should not invoke details for that item.

  1. It would be helpful if the Column class had a protected method for adding a data generator to a Grid. I provided an example in the issue description at point 2). I would use Grid#addDataGenerator(), but I cannot detect which data generator belongs to a column. The reason for this change is that we need to control the generated data in a column for specific rows.

  2. The setItemSelectableProvider() is a good option. I've checked, and we can use it, thank you for the suggestion. The case for the part data generator is as follows. In the Jmix component, we split the part name generator function into:

    • Group part name generator, which takes a different type (not the item type);
    • Part name data generator, which is the same as setPartNameGenerator().

    Depending on the item, the required generator is invoked. This feature exists for both the Column and Grid classes. Therefore, in the generatePartData() method, we should invoke not just partNameGenerator but a general function or method that decides which generator to invoke. For example:

    @Override
    protected void generatePartData(E item, JsonObject jsonObject) {
        // ...
    
       String rowPartName = partNameGenerator.apply(item); // invoke e.g. generatePartName(item);
    
       // ...
    
        getColumns().forEach(c -> {
            DataGridColumn<E> dataGridColumn = (DataGridColumn<E>) c;
            String cellPartName = dataGridColumn.getPartNameGenerator().apply(item); // invoke e.g. dataGridColumn.generatePartName(item);
    
               // ...
        });
        // ...
    }
    

Flaurite avatar Oct 23 '25 11:10 Flaurite