Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Vis Builder] Support UiState in Vis Builder #3650

Closed
abbyhu2000 opened this issue Mar 22, 2023 · 2 comments · Fixed by #3751
Closed

[Vis Builder] Support UiState in Vis Builder #3650

abbyhu2000 opened this issue Mar 22, 2023 · 2 comments · Fixed by #3751
Assignees
Labels
bug Something isn't working v2.7.0 vis builder

Comments

@abbyhu2000
Copy link
Member

Describe the bug
Currently Vis Builder does not support reading UiState, and this is blocking data table vis from being able to sort columns and persist the sorting columns.

This is the related bug issue: #3642, and this issue occurs after 2.5 release when data table vis is replacing with React and DataGrid. Here is the PR: #2863

@ananzh
Copy link
Member

ananzh commented Mar 22, 2023

Here is a table vis branch with uiState implementation: https://github.com/ananzh/OpenSearch-Dashboards/blob/table-polish-latest/src/plugins/vis_type_table/public/utils/ui_state_management.ts

It is using visBuilder 2.5. But could copy the whole vis_type_table for test purpose

@ananzh
Copy link
Member

ananzh commented Mar 22, 2023

I think the issue is that in visBuilder, we haven't implement a mechanism to allow visBuilder to listen the uiState change. I will use visualization to show what I mean by this:

In VisualizeEmbeddable class, it has

  • vis: vis is an object of Vis class which has uiState (PersistedState):
export class Vis<TVisParams = VisParams> {
  public readonly type: BaseVisType<TVisParams>;
  public readonly id?: string;
  public title: string = '';
  public description: string = '';
  public params: TVisParams;
  public data: VisData = {};

  public readonly uiState: PersistedState;
  • Then it adds an event listener to the uiState property of the vis object:
this.vis.uiState.on('change', this.uiStateChangeHandler);
this.vis.uiState.on('reload', this.reload);
  • Then this part of the code subscribes to the merged Observable from the previous step. When a new event is emitted by either the getOutput$() or getInput$() streams, the anonymous arrow function () => { this.handleChanges(); } is called, which in turn calls the handleChanges() method of the current object (this).
this.subscriptions.push(Rx.merge(this.getOutput$(), this.getInput$()).subscribe(() => {
      this.handleChanges();
    }));

  • Then handleChanges() will call this.updateHandler(); which calls this.handler.update(this.expression, expressionParams);. The handler is an ExpressionLoader object where defines update method:
update(expression, params) {
    this.setParams(params);
    this.loadingSubject.next(true);

    if (expression) {
      this.loadData(expression, this.params);
    } else if (this.data) {
      this.render(this.data);
    }
  }

loadData() will call this.dataSubject.next(data); which loads data to the dataSubject (another RxJS Subject). This enables other parts of the code to react to new data being available:

this.data$.subscribe(data => {
      this.render(data);
 });
    
render(data) {
    this.renderHandler.render(data, this.params.uiState); //ExpressionRenderHandler
  } 
  • When new data is emitted, the anonymous arrow function data => { this.render(data); } is called, which in turn calls the render() method with the received data. This renderHandler is an instance of an ExpressionRenderHandler class:
_defineProperty(this, "render", async (data, uiState) => {
      if (!data || typeof data !== 'object') {
        return this.handleRenderError(new Error('invalid data provided to the expression renderer'));
      }

      if (data.type !== 'render' || !data.as) {
        if (data.type === 'error') {
          return this.handleRenderError(data.error);
        } else {
          return this.handleRenderError(new Error('invalid data provided to the expression renderer'));
        }
      }

      if (!getRenderersRegistry().get(data.as)) {
        return this.handleRenderError(new Error(`invalid renderer id '${data.as}'`));
      }

      try {
        // Rendering is asynchronous, completed by handlers.done()
        await getRenderersRegistry().get(data.as).render(this.element, data.value, { ...this.handlers,
          uiState
        }); // call getTableVisRenderer.render
      } catch (e) {
        return this.handleRenderError(e);
      }
    });

this render method will call getTableVisRenderer in table vis to render table.

In visBuilder, we don't have such mechanism.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working v2.7.0 vis builder
Projects
None yet
3 participants