首页 > 解决方案 > 为什么 Angular Mat Table 2 选择模型失败?

问题描述

这是一个足够简单的问题(TLDR;):

为什么 Angular Mat Table 2 选择模型失败?

select()特别是为什么它会因传递给它或toggle()方法的对象的副本而失败。

但是我包含了很多调试过程,因此长度:

不要被它吓到,尽管这是任何人都可以在一分半钟内读完的东西。

语境 :

什么失败:

-click 方法添加Shift到选择中的项目在选择数组中,但不会在视觉上显示为选中状态,与随后的点击/选择无关(这将产生相同的结果 EG:在选择中保持当前视觉错误,同时保持一个完美的选择数组。是的,如果console.log中的整个数组是正确的,那么人们会认为点击“无错误”类型至少会修复错误的类型,但不是)。

编码 :

处理 Shift-select :

html:

  ...
  </div>
      </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="pinnedColumnsWSelect"></mat-header-row>
    <mat-row *matRowDef="let row; columns: pinnedColumnsWSelect;"
             class="noselect"
             [ngClass]="{ 'selected': selection.isSelected(row)}"
             (click)="addToSelection(row, $event, false)"></mat-row>
  </mat-table>
  ...

(false 是针对,单击是源自行首的复选框元素还是仅单击该行的其余部分?)

ts:

  addToSelection(row, event, checkbox){
    if(event.shiftKey && this.previous !== -1 && this.previous !== row.numberOfRow) {
      if(this.previous > row.numberOfRow){
        for(let previous = this.previous - 1; previous >= row.numberOfRow; previous-- ){
          this.selection.toggle(this.originalDataSet.filter(x => x['numberOfRow'] === previous)[0] as object[]);
        }
      }else{
        for(let previous = this.previous + 1; previous <= row.numberOfRow; previous++ ){
          this.selection.toggle(this.originalDataSet.filter(x => x['numberOfRow'] === previous)[0] as object[]);
        }
      }
    } else if(event.ctrlKey || checkbox) {
      this.selection.toggle(row);
    } else {
      if(this.originalDataSet){
        if(this.selection.selected.length === 1 && this.selection.selected[0] === row) {
        this.selection.clear();
      } else {
          this.selection.clear();
          this.selection.select(row);
        }
      }
    }
    this.previous = row.numberOfRow;
  }

正如您可能从上面推断的那样,我首先检查Shift键是否被按下。如果是,我应用我当前导致问题的选择,如果Shift没有被保留但是Ctrl ,我添加到选择中(这有效),最后我处于两个键都没有被按住的情况,我只是清除选择并将新项目设置为唯一选定项目。

您也可以通过阅读我上面的代码得出结论,我在“按住 shift”部分尝试做的是为要选择的每一行获取相应的行,并将其传递给选择的切换功能。

调试:

之前 console.logged 行并注意到该行确实是表的整个当前行,我推断我可以模拟正确的对象被传递给点 A 和 B 之间的“切换”方法。

我的材料表接受一组对象作为数据集。每个对象对应一行,每个对象的键对应表的标题。到目前为止,一切都很好。

自己从数组中拉出正确的行(使用我匹配的过滤器numberOfRow,我的唯一标识符恰好计数行(0、1、2、3,等等))应该给我同样的东西。

控制台记录这两者给了我同样的东西

const y = this.originalDataSet.filter(x => x['numberOfRow'] === previous)[0];
console.log('filtered item ', y, ' row ', row, ' equal ', y === row, y == row );

然而,噩梦始于两人宣布彼此不相等。

现在===很公平,但是==,为什么?

这是怎么发生的,我不知道:

filtered item  {numberOfRow: 2, nCommande: "4500131111", nLigne: "00010", nEcheance: "0001", id: {…}, …}  row  {numberOfRow: 2, nCommande: "4500131111", nLigne: "00010", nEcheance: "0001", id: {…}, …}  equal  false false

图片

这是我以前在 javascript 中从未见过的。这两个对象是相同的,我现在通过打开所有节点手动检查了 15 次。然而 == 失败了。

但是请记住,尽管它令人着迷,但我有一些更令人着迷的东西给你。

一个更深层次的问题:

让我们 console.log 我们的选择 ( console.log(this.selection.selected);)

  1. 然后单击我们垫表的第一项
  2. shift-click第四个
  3. ctrl 点击 fith

你期望会发生什么?

由于选择的格式不正确,最终没有被选中?我希望只有第一个最终被选中,因为选择的数组超出了这一点?正确选择所有 5 个项目也有意义吗?一个人可以梦想

好吧,不:
图片

好的,让我们看看日志:

(5) [{…}, {…}, {…}, {…}, {…}]
0 : {numberOfRow: 0, nCommande: "2284595", nLigne: "1", nEcheance: "0", id: {…}, …}
1 : {numberOfRow: 1, nCommande: "2284595", nLigne: "2", nEcheance: "0", id: {…}, …}
2 : {numberOfRow: 2, nCommande: "4500131111", nLigne: "00010", nEcheance: "0001", id: {…}, …}
3 : {numberOfRow: 3, nCommande: "4500131111", nLigne: "00020", nEcheance: "0001", id: {…}, …}
4 : {numberOfRow: 4, nCommande: "4500634818", nLigne: "00010", nEcheance: "0001", id: {…}, …}
length : 5
__proto__:Array(0)

我很困惑。

您使用此数组正确选择了数组项 0 和 4,但对于介于两者之间的所有内容,即使 4 的选择(第 5 项)排在最后,也不行。

如何?

这种人眼无法在“错误”和“正确”之间“区分”的模式会在您无限期地堆叠 shift 选择和 ctrl 选择时继续。如果您的 this.selection.selected 的长度为 100000 个项目,则它仍然不会在实际的视觉表示中选择所有通过 shift 添加到其数组中的项目,并且正确选择了所有使用 ctrl 添加的项目。

Shift-点击?:

让我们把它混合起来,因为这就是我们如何得到一些最终偏离常规的东西。

让我们尝试用 shift 取消选择

如果我再做一次糟糕的点击,打算再添加三个项目,然后再点击一次控制点击下面的项目,然后将点击向上移动到第二个项目: 图片

至少在视觉上与我们目前的混乱没有什么不同:

(9) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0 : {numberOfRow: 0, nCommande: "2284595", nLigne: "1", nEcheance: "0", id: {…}, …}
1 : {numberOfRow: 1, nCommande: "2284595", nLigne: "2", nEcheance: "0", id: {…}, …}
2 : {numberOfRow: 2, nCommande: "4500131111", nLigne: "00010", nEcheance: "0001", id: {…}, …}
3 : {numberOfRow: 3, nCommande: "4500131111", nLigne: "00020", nEcheance: "0001", id: {…}, …}
4 : {numberOfRow: 4, nCommande: "4500634818", nLigne: "00010", nEcheance: "0001", id: {…}, …}
5 : {numberOfRow: 5, nCommande: "4500634818", nLigne: "00020", nEcheance: "0001", id: {…}, …}
6 : {numberOfRow: 6, nCommande: "4500634818", nLigne: "00030", nEcheance: "0001", id: {…}, …}
7 : {numberOfRow: 7, nCommande: "4500634818", nLigne: "00040", nEcheance: "0001", id: {…}, …}
8 : {numberOfRow: 8, nCommande: "4500634818", nLigne: "00050", nEcheance: "0001", id: {…}, …}
length : 9

(4) [{…}, {…}, {…}, {…}]
0 : {numberOfRow: 0, nCommande: "2284595", nLigne: "1", nEcheance: "0", id: {…}, …}
1 : {numberOfRow: 4, nCommande: "4500634818", nLigne: "00010", nEcheance: "0001", id: {…}, …}
2 : {numberOfRow: 8, nCommande: "4500634818", nLigne: "00050", nEcheance: "0001", id: {…}, …}
3 : {numberOfRow: 4, nCommande: "4500634818", nLigne: "00010", nEcheance: "0001", id: {…}, …}
length : 4

好吧,这是出乎意料的。

为什么它保留第 4 项而不是像其他项一样取消选择它,然后再添加它?

这让我觉得还有另一个数组用于我不知道的比较。

怎么样onChange?:

此外

onChange结果完全符合预期:

  ngOnInit() {
    this.selection.onChange.subscribe(x=> {
      console.log(x);
    });
  }

对于从第一项到第六项的 shift-click 并再次返回:

{source: SelectionModel, added: Array(1), removed: Array(0)}
{source: SelectionModel, added: Array(1), removed: Array(0)}
{source: SelectionModel, added: Array(1), removed: Array(0)}
{source: SelectionModel, added: Array(1), removed: Array(0)}
{source: SelectionModel, added: Array(1), removed: Array(0)}
{source: SelectionModel, added: Array(1), removed: Array(0)}
{source: SelectionModel, added: Array(0), removed: Array(1)}
{source: SelectionModel, added: Array(0), removed: Array(1)}
{source: SelectionModel, added: Array(0), removed: Array(1)}
{source: SelectionModel, added: Array(0), removed: Array(1)}
{source: SelectionModel, added: Array(1), removed: Array(0)}

我检查了他们指示的内容是否完全符合他们的要求(正确的项目编号以正确的顺序)。

然而视觉并没有效仿。

复写真的会失败吗?:

另一个实验:

我在介绍中提到该对象的副本将被拒绝:这是真的。

如果我对我们迄今为止的功能性 ctrl 代码执行此操作,它就会停止运行(顺便说一句,这是导入的 underscorejs 库,它是一个浅克隆,但它没有省略下属,它使用它们的内存引用):

} else if(event.ctrlKey || checkbox) {
      const bb = _.clone(row);
      this.selection.toggle(bb);
}

这种方法也会发生同样的事情(这是一个深度克隆):

} else if(event.ctrlKey || checkbox) {
      const bb = jQuery.extend(true, {}, row);
      this.selection.toggle(bb);
}

不再使用 ctrl-click 直观地选择行,但控制台记录的选择数组和选择的所有其他方面仍然完美无缺。

哪些版本的 Angular、Material、OS、TypeScript、浏览器?:

windows pro 10 64bit 镀铬

{
  "name": "web.ui",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve --aot",
    "build": "ng b --prod",
    "test": "ng test",
    "lint": "ng lint"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^6.0.3",
    "@angular/cdk": "^6.1.0",
    "@angular/common": "^6.0.3",
    "@angular/compiler": "^6.0.3",
    "@angular/core": "^6.0.3",
    "@angular/forms": "^6.0.3",
    "@angular/http": "^6.0.3",
    "@angular/material": "^6.1.0",
    "@angular/platform-browser": "^6.0.3",
    "@angular/platform-browser-dynamic": "^6.0.3",
    "@angular/router": "^6.0.3",
    "@types/underscore": "^1.8.7",
    "angular-font-awesome": "^3.1.2",
    "bootstrap": "^4.0.0",
    "classlist.js": "^1.1.20150312",
    "core-js": "^2.5.3",
    "file-saver": "^1.3.8",
    "font-awesome": "^4.7.0",
    "jquery": "^3.3.1",
    "lodash": "^4.17.5",
    "ng2-ion-range-slider": "^2.0.0",
    "ngx-bootstrap": "^3.0.0",
    "ngx-dropzone-wrapper": "^6.1.0",
    "rxjs": "^6.2.0",
    "rxjs-compat": "^6.0.0-rc.0",
    "typescript": "2.7.2",
    "underscore": "^1.8.3",
    "web-animations-js": "^2.3.1",
    "zone.js": "^0.8.20"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.6.5",
    "@angular/cli": "^6.0.5",
    "@angular/compiler-cli": "^6.0.3",
    "@angular/language-service": "^6.0.3",
    "@types/jasmine": "^2.8.6",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "~10.1.3",
    "codelyzer": "^4.2.1",
    "postcss-modules": "^1.1.0",
    "protractor": "~5.3.0",
    "ts-node": "~6.0.5",
    "tslint": "~5.10.0"
  }
}

标签: javascriptjqueryangularangular-material2multipleselection

解决方案


好的,我想通了,在你们中的一些人看来,这在我给出的问题中是一个未知数,所以你们不可能猜到,因为我没有给你们我的全部组件,而对你们其他人来说,它会看起来很明显,因为您对 mat table 非常了解,并且您可以从我的变量的命名中看出有些不对劲,

我有一组四个本地变量,用于处理我的对象的四个步骤,从 API 到达到它在 mat-table 中以可视方式显示。

  // [ 1 ] this first gets the api object and will serve as a store
  originalDataSet:Array<object>;

  // [ 2 ] rendered after filters
  filteredDataSet:Array<object>;

  // [ 3 ] sclice step (needs to be second to last due to dual slider for size)
  dataSlice = [];

  // [ 4 ] final. what actually is queried by Mat Table
  @ViewChild(MatSort) sort: MatSort;
  dataSource: MatTableDataSource<any>;

行(对象)在 1 和 4 之间确实是相同的,但其中很多已经丢失了。

这不能回答为什么 javascript 认为一个对象的两个副本不一样,但它确实回答了为什么我提供给我的toggle()方法的对象被认为不一样row

现在,this.dataSource.filteredData我实际上是在迭代与我的 mat 表相同的数组。

并且这不会导致例如切换页面和 shift-click 的缺陷。

(除了不从第一页选择项目之外,它不能再迭代,因为它们不在当前列表中,这是我一开始就想避免的,但我会解决这个错误)

如果有人对为什么 js 看不到与其自身相同的对象副本有一个答案,我很想得到一个答案。

如果有人能看到这个对象如何无法满足 mat table 的选择模型的标准以及我如何愚弄它,我也很想知道。

我愿意将你标记为答案并给予 50 分赏金。


推荐阅读