Vaadin Tree Grid 抛出递归错误

Vaadin Tree Grid throws a recursion error

我正在使用 Vaadin 14.7.5,所以 vaadin-grid 5.9.0。

包含单个 vaadin-grid-tree-toggle 元素的单个 vaadin-grid 元素位于 Lit 元素内。

import {css, html, LitElement} from 'lit-element';
import {render} from "lit-html";

import '@vaadin/vaadin-grid';
import '@vaadin/vaadin-grid/vaadin-grid-column';
import '@vaadin/vaadin-grid/vaadin-grid-tree-column';
import '@vaadin/vaadin-grid/vaadin-grid-tree-toggle';

class ProjectTaskTreeElement extends LitElement {

  static get properties() {
    return {
      project: {
        attribute: false
      }
    };
  }

  constructor() {
    super();
  }

  static get styles() {
    return css`
      :host {
        display: block;

        width: 100%;
      }

      :host([hidden]) {
        display: none !important;
      }
    `;
  }

  render() {
    return html`
        <vaadin-grid id="tree"
                     .items="${this.project ? this.project.top_level_tasks : []}">
            <vaadin-grid-tree-column header="Name" path="name" item-has-children-path="subtasks"></vaadin-grid-tree-column>
        </vaadin-grid>
    `;
  }

}

customElements.define('project-task-tree', ProjectTaskTreeElement);

export {
  ProjectTaskTreeElement
};

这里有一个 projects 的例子:

{
  "id": 30,
  "name": "Test Project",
  "archived": false,
  "top_level_tasks": [
    {
      "id": 18,
      "name": "1",
      "complete": false,
      "subtasks": [
        {
          "id": 19,
          "name": "1.1",
          "complete": false,
          "subtasks": [],
          "teammates": []
        }
      ],
      "teammates": []
    }
  ],
  "teammates": []
}

我希望它能正常工作。 ArrayDataProviderMixin#_arrayDataProvider 应作为数据提供者处理 items。网格呈现良好,预期的根行折叠和展开。但是当我点击展开时,报错:

Uncaught InternalError: too much recursion
    set http://localhost:8080/node_modules/@polymer/polymer/lib/mixins/properties-changed.js:167
    _arrayDataProvider http://localhost:8080/node_modules/@vaadin/vaadin-grid/src/vaadin-grid-array-data-provider-mixin.js:66
    _loadPage http://localhost:8080/node_modules/@vaadin/vaadin-grid/src/vaadin-grid-data-provider-mixin.js:347
    ensureSubCacheForScaledIndex http://localhost:8080/node_modules/@vaadin/vaadin-grid/src/vaadin-grid-data-provider-mixin.js:70
    _loadPage http://localhost:8080/node_modules/@vaadin/vaadin-grid/src/vaadin-grid-data-provider-mixin.js:364
    _loadPage http://localhost:8080/node_modules/@vaadin/vaadin-grid/src/vaadin-grid-data-provider-mixin.js:359
    _arrayDataProvider http://localhost:8080/node_modules/@vaadin/vaadin-grid/src/vaadin-grid-array-data-provider-mixin.js:75
properties-changed.js:167:51

我试过模板化 vaadin-grid-column 元素,但出现同样的错误:

<vaadin-grid-column>
    <template class="header">Name</template>
    <template>
        <vaadin-grid-tree-toggle leaf="[[!item.subtasks.length]]"
                                 expanded="{{expanded}}"
                                 level="[[level]]">
            [[item.name]]
        </vaadin-grid-tree-toggle>
    </template>
</vaadin-grid-column>

想通了。仔细检查 ArrayDataProviderMixin 后,我发现它不支持我的数据格式。

首先,我转换了我的数据:

_flattenTasks(tasks) {
  function traveler(task, parent, accumulator) {
    accumulator.push({
      id: task.id,
      name: task.name,
      hasSubtasks: task.subtasks.length > 0,
      parentTaskId: parent ? parent.id : null
    });

    for(const subtask of task.subtasks) {
      accumulator = traveler(subtask, task, accumulator);
    }

    return accumulator;
  }

  let accumulator = [];

  for(const task of tasks) {
    accumulator = traveler(task, null, accumulator);
  }

  return accumulator;
}

然后,我写了一个数据提供者:

_treeDataProvider(parameters, callback) {
  const getSubtasks = (parentTaskId, callback) => {
    callback(this._tasks.filter((task) => {
      if(parentTaskId) {
        return (task.parentTaskId === parentTaskId);
      } else {
        return !task.parentTaskId;
      }
    }));
  };

  const parentTaskId = parameters.parentItem ? parameters.parentItem.id : null;

  getSubtasks(parentTaskId, (subtasks) => {
    const startIndex = parameters.page * parameters.pageSize;
    const pageItems = subtasks.slice(startIndex, startIndex + parameters.pageSize);
    const treeLevelSize = subtasks.length;

    callback(pageItems, treeLevelSize);
  });
}