当autoGroupColumnDef更改时,AgGridReact组件不会更新

时间:2019-08-15 21:12:01

标签: ag-grid ag-grid-react

我正在将ag-Grid与React一起使用以测试其企业row grouping功能。我想在运行时更改行分组列的名称,但是我无法执行此操作。

当我对AgGridReact的columnDefs道具进行更改时,更改将反映在表中。但是,即使调试日志显示已检测到更改,也不会呈现对autoGroupColumnDef道具的更改。这是TypeScript中的示例(使用React钩子来表示状态):

import React, { FC, useState } from 'react';
import 'ag-grid-enterprise';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-balham.css';

const AgGridTest: FC = () => {
  const rowData = [
    { col1: 'a', col2: 0, col3: 0 },
    { col1: 'b', col2: 1, col3: 1 },
    { col1: 'c', col2: 2, col3: 0 },
    { col1: 'd', col2: 3, col3: 1 },
    { col1: 'a', col2: 4, col3: 0 },
    { col1: 'b', col2: 5, col3: 1 },
    { col1: 'c', col2: 6, col3: 0 },
    { col1: 'd', col2: 7, col3: 1 },
  ];
  const [columnDefs, setColumnDefs] = useState([
    { headerName: 'Column 1', field: 'col1', rowGroup: true }, // initial group column
    { headerName: 'Column 2', field: 'col2', rowGroup: false },
    { headerName: 'Column 3', field: 'col3', rowGroup: false },
  ]);

  const [autoGroupColumnDef, setAutoGroupColumnDef] = useState(
    {headerName: 'col1 Initial'} // auto group column name is 'col1 Initial' to start
  );

  const groupByColumn = (field: string): void => {
    // this successfully changes the grouping column...
    setColumnDefs(
      prevColumnDefs => prevColumnDefs.map(
        colDef => colDef.field === field ? 
          {...colDef, rowGroup: true} :
          {...colDef, rowGroup: false}
      )
    );
    // ...but this won't change the auto group column name!
    setAutoGroupColumnDef({
      headerName: `${field} Group Column`,
    });
  }

  return (
    <div className="ag-theme-balham" style={{ height: '300px' }}>
      <button onClick={() => groupByColumn('col1')}>Group by Column 1</button>
      <button onClick={() => groupByColumn('col2')}>Group by Column 2</button>
      <AgGridReact 
        rowData={rowData}
        columnDefs={columnDefs}
        autoGroupColumnDef={autoGroupColumnDef}
        debug // enable debug logs
      />
    </div>
  );
}

const App: React.FC = () => <AgGridTest />

export default App;

我尝试调用网格API的api.refreshHeader,但这也不起作用:

const AgGridTest: FC = () => {
  // ...

  // store the api in an instance variable
  // when the on ready event makes it available
  const api = useRef<GridApi>();
  const onGridReady = (params: GridReadyEvent) => {
    api.current = params.api;
  }

  const groupByColumn = (field: string): void => {
    // ... re-assign the grouping cols ...
  }

  useEffect(() => {
    // run this after grouping changes have been rendered
    gridApi.current && gridApi.current.refreshHeader(); // doesn't work
  });

  return (
    <div>
      <AgGridReact
        onGridReady={onGridReady}
        // ...
      />
    </div>
  );
}

我试图寻找一种设置autoColumnGroupDef的API函数,但找不到。 documentation仅提及通过gridOptions设置属性。 API具有用于更新常规列定义的功能:api.setColumnDefs。当columnDefs属性更改时(相关代码段herehere),这就是AgGridReact调用的。但是,当更改autoGroupColumnDef属性时,AgGridReact会简单地覆盖gridOptions对象(relevant code snippet)中的autoGroupColumnDef属性。似乎没有任何关联的setter函数。

似乎甚至没有办法获得自动分组列对象。 columnApi.getAllColumns/getColumnGroup仅返回常规列。自动组列列表单独保存在columnController(relevant code snippet)的私有实例变量中,该变量具有公开的getPrimaryAndSecondaryAndAutoColumns访问器,但是我们无法访问columnController API。

关于如何在运行时修改自动组列名称的任何想法?还是我必须禁用它们并create my own group columns

有人问herehere这个问题,但他们从未收到答案(我不理解第二个问题中的评论)。

3 个答案:

答案 0 :(得分:2)

您可能已经知道,ag-grid是纯js,而React和Angular版本是围绕它的包装。

我已经使用Angular版本大约一年了,并且对其进行了大量的自定义,并且我发现许多gridOptions(即使它们是绑定属性)在初始实例化网格之后都无效。我怀疑autoGroupColumnDef是其中之一。

因此,我将尝试根据您引用的链接创建自己的列组。

如果这不起作用,(丑陋的)替代方法是销毁网格,然后使用反映您新的autoGroupColumnDef的新gridOptions重新创建它。

另一条来之不易的建议:  如果您使用setColumnDefs API更新列定义,则可以正常工作,但是,如果您以后要保存和恢复网格状态,则从API获取网格状态会产生列名称,并在列名称后附加“ _1”,并且设置列状态,您将收到关于找不到列的错误。解决方案是先将columnDefs设置为一个空数组,然后再设置实际的columnDefs。

答案 1 :(得分:0)

@GreyBeardedGeek mentioned使用setColumnDefs API会更改列名。这使我陷入困境,这也许对其他人有用。

tl; dr

如果要在运行时通过API(例如api.setColumnDefs)而不是仅通过ag-Grid的UI控件(例如,允许用户调整大小)来更改列顺序,宽度,排序状态,过滤,行分组或在运行时进行透视通过单击和拖动以创建列),您必须:

  1. 为每列指定唯一的colId
  2. gridOptions.deltaColumnMode设置为true。
  3. 如果您还希望允许用户修改这些属性(例如,单击-n-拖动调整大小),请添加一个显式处理程序来监视这些更改并相应地更新columnDef。

我现在将解释原因(某些地方-我仍然不清楚)。

如果提供colIds但将deltaColumnMode保留为假...

我做了一个plunkr,您可以在其中创建新列并更新新列以及现有列的排序顺序,宽度以及是否为行分组列。首先要注意的是,已应用了列1的columnDefs中的初始宽度,但是对于现有列或新列单击“增加宽度”没有任何作用。分组也不起作用-单元格渲染器将更改,将内容居中,但实际上不会进行分组。

这是因为deltaColumnMode默认情况下为false,并且当deltaColumnMode为false时,将忽略对某些属性(包括宽度和行分组)的运行时更改From the docs

  

默认情况下,当新列加载到网格中时,不使用以下属性:

     
      
  • 列顺序
  •   
  • 聚合函数(colDef.aggFunc)
  •   
  • 宽度(colDef.width)
  •   
  • 数据透视(colDef.pivot或colDef.pivotIndex)
  •   
  • 行组(colDef.rowGroup或colDef.rowGroupIndex)
  •   
  • 固定(colDef.pinned)
  •   
     

这样做是为了避免应用程序用户的意外行为。

     

例如-假设应用程序用户重新排列列的顺序。然后,如果应用程序设置新的列定义是为了向网格中添加一个额外的列,那么重置所有列的顺序将给用户带来糟糕的体验。

类似地,当我们创建新列时,我们将其首先放置在columnDefs列表中,但是此顺序被忽略,而是在末尾创建。这是因为当deltaColumnMode为false时,列顺序是忽略的属性之一。

第二点要注意的是,即使我们进行columnDefs更新,也会保留排序顺序。因为我们提供了colIds,所以ag-Grid知道当我们更改columnDefs时,我们将更新现有的列而不是创建新的列。当colId相同时,ag-Grid会保留内部状态,例如排序顺序和行分组。 From the docs

  

列定义的比较是在1)对象引用比较和2)列ID(例如colDef.colId)上完成的。如果对象引用匹配,或者列ID匹配,则网格会将列视为同一列。例如,如果网格中有一列的ID为“ country”,而用户设置了新列,其中一列的ID也为“ country”,则旧的country列将被替换为新的一列,并保持其内部状态,例如宽度,位置,排序和过滤条件。

如果您不提供任何colId并将deltaColumnMode保留为false ...

如果您未在初始columnDefs中提供colId,请ag-Grid will assign them internally

  

如果用户在列定义中提供colId,则将使用此字段,否则将使用该字段。如果同时使用colId和field,则colId将获得首选项。如果没有colId或字段,则提供数字。最后,通过添加'_ [n]'来确保ID是唯一的,其中n是允许唯一性的第一个正数。

每当columnDefs更改时,ag-Grid都会通过检查colId(或列对象引用)来确定更改是对现有列还​​是对新列。 From the docs

  

如果要更新列(而不是替换整个集合),则必须提供列ID或重新使用列定义对象实例。否则,网格将不会知道这些列实际上是相同的列。

当我们提供colIds时,ag-Grid知道我们正在更新现有的列。但是当我们不提供colIds时,ag-Grid不知道该怎么办。让我们看一下与之前相同的plunkr,但删除colIDs。现在,当我们单击按钮以增加列1的宽度时,会发生三件事:1)列1的宽度确实增加了,但是2)它的顺序发生了变化,因此它现在是最右边的列,以及3)如果我们继续单击“增加宽度”,我们在控制台中看到,列1的colId最初是未定义的,在第一次增加后变为“ col1”(字段名称),然后每增加一次,便在“ col1_1”和“ col1”之间交替。

我怀疑这种奇怪的行为是由于当未提供colIds时,ag-Grid解析columnDefs更新的方式。 由于未提供colId,ag-Grid似乎认为我们在更新列1的宽度时正在创建新列。由于ag-Grid认为这是新列,因此宽度更改实际上是即使deltaColumnMode为false,也要应用该属性(请记住,在上一个示例中设置了colIds的情况下,width属性是在创建列时应用的,而并非在运行时应用)。但是因为deltaColumnMode为false,所以当我们创建新列时,其顺序将被忽略-因此,列1被放置为最右边的列。 (不过,由于the docs say“在将新列加载到网格中时应忽略宽度/列顺序”,所以我不太清楚为什么要应用初始宽度,而没有应用列顺序)。

类似的事情发生在我们按列1分组时,但1)所有列的所有colId都更改,并且2)分组列被移到最左边的列。我怀疑第一个是因为应用分组时所有列都发生了变化,第二个是因为默认情况下分组列已移至左侧,因为当我们创建新列并对其进行分组时,它将移至左侧也是如此。

您可能会注意到,有时在更改第1列或新列之后,您将无法对其进行任何排序。还记得ag-Grid如何在向colId添加和删除“ _1”之间交替吗?由于api.setSortModel按colIds查找列,因此每次在colId后面附加“ _1”,api.setSortModel将无法找到要进行排序的列(并且将自动失败) ,显然)。我们可以通过使用columnApi.getAllColumns()获取当前colId并改用它们来解决此问题。

此外,由于未提供colId,因此ag-Grid不会在columnDef更改之间保留列状态(例如,排序顺序)。如果您对一列进行排序,然后增加其宽度或按其分组,则将重置排序。这是因为排序附加到了列对象的内部状态,但是当columnDefs更改时,列对象被破坏了。

如果提供colIds并将deltaColumnMode设置为true ...

这里有一个plunkr来演示。现在,事情按预期工作:应用运行时更改,在最左侧创建新列,并在columnDef更新之间保留排序顺序。但是,有一个原因默认情况下deltaColumnMode为false。同样,from the docs(重点是我):

  

这样做是为了避免应用程序用户的意外行为。

     

例如-假设应用程序用户重新排列列的顺序。然后,如果应用程序设置新的列定义是为了向网格中添加一个额外的列,那么重置所有列的顺序将给用户带来糟糕的体验。

     

同样,如果用户更改了聚合函数,列的宽度或列是否固定,则用户应撤消所有这些更改,因为应用程序决定更新列定义。

     

要更改此行为,并在每次应用程序更新网格列时使上面的列属性(顺序,宽度,行组等)生效,然后设置gridOption.deltaColumnMode = true。 然后,您有责任确保您提供的列定义与网格中的内容保持同步,如果您不希望发生不希望的可见变化-例如,如果用户更改了列的宽度,应用程序应侦听网格事件columnWidthChanged并使用新的宽度更新应用程序列定义-否则,在应用程序将列定义更新到网格中后,宽度将重置为默认值。

尝试将列1拖动到其他顺序,然后增加其宽度。您将在columnDefs中看到其顺序被重置为其原始顺序,就像文档所说的那样。要保留列顺序,您需要为列拖动事件设置一个侦听器,并以新的顺序更新columnDefs-有关示例,请参见此plunkr和onDragStopped处理程序。

如果您想允许用户通过ag-Grid默认UI控件更改聚合功能,宽度,旋转,行分组或固定,则还需要为这些更改添加侦听器。

答案 2 :(得分:0)

我解决这个问题已有一段时间了,我终于找到了解决方案(虽然有点开箱即用,但是可以用)

如果仅要更改的是标头,则可以使用dom操作。您只需要获取包含您想要更改标题的确切元素即可。就我而言,我想按选择的顺序显示所选聚合的列表,因此我可以通过以下方式完成此操作:

 aggregateMyColumn() {
    this.gridColumnApi.setRowGroupColumns(this.orderedAggregationFields);
    if(this.orderedAggregationFields.length > 0) {
      const container = document.querySelector("#agGrid");
      const matches = container.querySelector("div[col-id='ag-Grid-AutoColumn']");
      const final = container.querySelector("span[role='columnheader']");
      this.renderer.setProperty(final, 'innerHTML', this.orderedAggregationFields.toString());
    }
 }

注意:有理由使用renderer2设置属性/更改innerhtml(有些安全性),但是这里有一些有用的链接:

https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll https://www.ninjadevcorner.com/2019/04/dom-manipulation-using-angular-renderer2.html