我正在使用一个垫子表列出用户选择的语言的内容。他们还可以使用对话框面板添加新的语言。在他们添加了一门语言并返回之后。我希望刷新数据源以显示他们所做的更改。

我通过从服务获取用户数据并在刷新方法中将其传递到数据源来初始化数据存储。

Language.component.ts

import { Component, OnInit } from '@angular/core';
import { LanguageModel, LANGUAGE_DATA } from '../../../../models/language.model';
import { LanguageAddComponent } from './language-add/language-add.component';
import { AuthService } from '../../../../services/auth.service';
import { LanguageDataSource } from './language-data-source';
import { LevelbarComponent } from '../../../../directives/levelbar/levelbar.component';
import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { MatSnackBar, MatDialog } from '@angular/material';

@Component({
  selector: 'app-language',
  templateUrl: './language.component.html',
  styleUrls: ['./language.component.scss']
})
export class LanguageComponent implements OnInit {

  displayedColumns = ['name', 'native', 'code', 'level'];
  teachDS: any;
  user: any;

  constructor(private authService: AuthService, private dialog: MatDialog) { }

  ngOnInit() {
    this.refresh();
  }

  add() {
    this.dialog.open(LanguageAddComponent, {
      data: { user: this.user },
    }).afterClosed().subscribe(result => {
      this.refresh();
    });
  }

  refresh() {
    this.authService.getAuthenticatedUser().subscribe((res) => {
      this.user = res;
      this.teachDS = new LanguageDataSource(this.user.profile.languages.teach);   
    });
  }
}

language-data-source.ts

import {MatPaginator, MatSort} from '@angular/material';
import {DataSource} from '@angular/cdk/collections';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';

export class LanguageDataSource extends DataSource<any> {

  constructor(private languages) {
    super();
  }

  connect(): Observable<any> {
    return Observable.of(this.languages);
  }

  disconnect() {
    // No-op
  }

}

因此,我尝试调用一个刷新方法,我从后端再次获得用户,然后重新初始化数据源。然而,这并没有起作用,没有发生任何变化。


在refresh()方法中使用ChangeDetectorRef触发变更检测 在接收到新数据后,在构造函数中注入ChangeDetectorRef,并像这样使用detectChanges:

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { LanguageModel, LANGUAGE_DATA } from '../../../../models/language.model';
import { LanguageAddComponent } from './language-add/language-add.component';
import { AuthService } from '../../../../services/auth.service';
import { LanguageDataSource } from './language-data-source';
import { LevelbarComponent } from '../../../../directives/levelbar/levelbar.component';
import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { MatSnackBar, MatDialog } from '@angular/material';

@Component({
  selector: 'app-language',
  templateUrl: './language.component.html',
  styleUrls: ['./language.component.scss']
})
export class LanguageComponent implements OnInit {
  displayedColumns = ['name', 'native', 'code', 'level'];
  teachDS: any;

  user: any;

  constructor(private authService: AuthService, private dialog: MatDialog,
              private changeDetectorRefs: ChangeDetectorRef) { }

  ngOnInit() {
    this.refresh();
  }

  add() {
    this.dialog.open(LanguageAddComponent, {
      data: { user: this.user },
    }).afterClosed().subscribe(result => {
      this.refresh();
    });
  }

  refresh() {
    this.authService.getAuthenticatedUser().subscribe((res) => {
      this.user = res;
      this.teachDS = new LanguageDataSource(this.user.profile.languages.teach);
      this.changeDetectorRefs.detectChanges();
    });
  }
}

由于您正在使用MatPaginator,您只需要对分页器做任何更改,这将触发数据重新加载。

简单的技巧:

this.paginator._changePageSize(this.paginator.pageSize); 

这将页面大小更新为当前页面大小,因此基本上没有任何变化,除了也调用私有_emitPageEvent()函数,从而触发重新加载表。


做到这一点的最佳方法是向Datasource实现中添加一个额外的可观察对象。

在connect方法中,你应该已经使用了Observable。合并以订阅包含分页器的可观察对象数组。页面中,排序。sortChange等等。您可以添加一个新的主题,并在需要引起刷新时调用它。

就像这样:

export class LanguageDataSource extends DataSource<any> {

    recordChange$ = new Subject();

    constructor(private languages) {
      super();
    }

    connect(): Observable<any> {

      const changes = [
        this.recordChange$
      ];

      return Observable.merge(...changes)
        .switchMap(() => return Observable.of(this.languages));
    }

    disconnect() {
      // No-op
    }
}

然后可以调用recordChange$.next()来启动刷新。

当然,我会将调用包装在refresh()方法中,并将其从组件中的数据源实例w/中调用,以及其他适当的技术。


this.dataSource = new MatTableDataSource<Element>(this.elements);

将这一行添加到添加或删除特定行的操作下面。

refresh() {
  this.authService.getAuthenticatedUser().subscribe((res) => {
    this.user = new MatTableDataSource<Element>(res);   
  });
}

我不知道在创建问题时是否需要ChangeDetectorRef,但现在这就足够了:

import { MatTableDataSource } from '@angular/material/table';

// ...

dataSource = new MatTableDataSource<MyDataType>();

refresh() {
  this.myService.doSomething().subscribe((data: MyDataType[]) => {
    this.dataSource.data = data;
  }
}

例子: StackBlitz


import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';

export class LanguageComponent implemnts OnInit {
  displayedColumns = ['name', 'native', 'code', 'leavel'];
  user: any;
  private update = new Subject<void>();
  update$ = this.update.asObservable();

  constructor(private authService: AuthService, private dialog: MatDialog) {}

   ngOnInit() {
     this.update$.subscribe(() => { this.refresh()});
   }

   setUpdate() {
     this.update.next();
   }

   add() {
     this.dialog.open(LanguageAddComponent, {
     data: { user: this.user },
   }).afterClosed().subscribe(result => {
     this.setUpdate();
   });
 }

 refresh() {
   this.authService.getAuthenticatedUser().subscribe((res) => {
     this.user = res;
     this.teachDS = new LanguageDataSource(this.user.profile.languages.teach);   
    });
  }
}

所以对我来说,没有人能给出和@Kay一样好的答案。对我来说,这是关于排序,排序表不发生变化垫。 我的目的是这个答案,因为这是我通过搜索谷歌找到的唯一主题。 我使用的是Angular 6。

正如这里所说:

由于表对性能进行了优化,因此它不会自动检查数据数组的更改。相反,当在数据数组上添加、删除或移动对象时,您可以通过调用其renderRows()方法来触发对表所呈现行的更新。

所以你只需要在你的refresh()方法中调用renderRows()来显示你的更改。

请参见这里的积分。


这招对我很管用:

refreshTableSorce() {
    this.dataSource = new MatTableDataSource<Element>(this.newSource);
}

我认为MatTableDataSource对象在某种程度上与你传递给MatTableDataSource构造函数的数据数组相关联。

例如:

dataTable: string[];
tableDS: MatTableDataSource<string>;

ngOnInit(){
   // here your pass dataTable to the dataSource
   this.tableDS = new MatTableDataSource(this.dataTable); 
}

所以,当你需要改变数据时;改变原始列表dataTable,然后通过调用tableDS上的_updatechangesub兵役()方法反映表上的变化。

例如:

this.dataTable.push('testing');
this.tableDS._updateChangeSubscription();

这就是我在Angular 6中的工作。


这对我来说很管用:

dataSource = new MatTableDataSource<Dict>([]);
    public search() {
        let url = `${Constants.API.COMMON}/dicts?page=${this.page.number}&` + 
        (this.name == '' ? '' : `name_like=${this.name}`);
    this._http.get<Dict>(url).subscribe((data)=> {
    // this.dataSource = data['_embedded'].dicts;
    this.dataSource.data =  data['_embedded'].dicts;
    this.page = data['page'];
    this.resetSelection();
  });
}

所以你应该声明你的数据源实例为MatTableDataSource


在我的例子中(Angular 6+),我继承了MatTableDataSource来创建MyDataSource。事后都不打电话。data = someArray

this.entitiesSubject.next(this.data as T[])

未显示的数据

类的一个

export class MyDataSource<T extends WhateverYouWant> extends MatTableDataSource<T> {

    private entitiesSubject = new BehaviorSubject<T[]>([]);


    loadDataSourceData(someArray: T[]){
        this.data = someArray //whenever it comes from an API asyncronously or not
        this.entitiesSubject.next(this.data as T[])// Otherwise data not displayed
    }

    public connect(): BehaviorSubject<T[]> {
        return this.entitiesSubject
    }

}//end Class 

我做了更多的研究,发现这个地方给我我需要的-感觉干净,涉及更新数据时,从服务器刷新: https://blog.angular-university.io/angular-material-data-table/

大部分的演职员表都在上面一页。下面是如何使用mat-selector在选择更改时更新绑定到数据源的mat-table的示例。我使用的是Angular 7。对不起,我写得太广泛了,我想要完整而简洁——我已经把尽可能多不需要的部分删掉了。希望这能帮助其他人更快地前进!

organization.model.ts:

export class Organization {
    id: number;
    name: String;
}

organization.service.ts:

import { Observable, empty } from 'rxjs';
import { of } from 'rxjs';

import { Organization } from './organization.model';

export class OrganizationService {
  getConstantOrganizations(filter: String): Observable<Organization[]> {
    if (filter === "All") {
      let Organizations: Organization[] = [
        { id: 1234, name: 'Some data' }
      ];
      return of(Organizations);
     } else {
       let Organizations: Organization[] = [
         { id: 5678, name: 'Some other data' }
       ];
     return of(Organizations);
  }

  // ...just a sample, other filterings would go here - and of course data instead fetched from server.
}

organizationdatasource.model.ts:

import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { Observable, BehaviorSubject, of } from 'rxjs';
import { catchError, finalize } from "rxjs/operators";

import { OrganizationService } from './organization.service';
import { Organization } from './organization.model';

export class OrganizationDataSource extends DataSource<Organization> {
  private organizationsSubject = new BehaviorSubject<Organization[]>([]);

  private loadingSubject = new BehaviorSubject<boolean>(false);

  public loading$ = this.loadingSubject.asObservable();

  constructor(private organizationService: OrganizationService, ) {
    super();
  }

  loadOrganizations(filter: String) {
    this.loadingSubject.next(true);

    return this.organizationService.getOrganizations(filter).pipe(
      catchError(() => of([])),
      finalize(() => this.loadingSubject.next(false))
    ).subscribe(organization => this.organizationsSubject.next(organization));
  }

  connect(collectionViewer: CollectionViewer): Observable<Organization[]> {
    return this.organizationsSubject.asObservable();
  }

  disconnect(collectionViewer: CollectionViewer): void {
    this.organizationsSubject.complete();
    this.loadingSubject.complete();
  }
}

organizations.component.html:

<div class="spinner-container" *ngIf="organizationDataSource.loading$ | async">
    <mat-spinner></mat-spinner>
</div>

<div>
  <form [formGroup]="formGroup">
    <mat-form-field fxAuto>
      <div fxLayout="row">
        <mat-select formControlName="organizationSelectionControl" (selectionChange)="updateOrganizationSelection()">
          <mat-option *ngFor="let organizationSelectionAlternative of organizationSelectionAlternatives"
            [value]="organizationSelectionAlternative">
            {{organizationSelectionAlternative.name}}
          </mat-option>
        </mat-select>
      </div>
    </mat-form-field>
  </form>
</div>

<mat-table fxLayout="column" [dataSource]="organizationDataSource">
  <ng-container matColumnDef="name">
    <mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
    <mat-cell *matCellDef="let organization">{{organization.name}}</mat-cell>
  </ng-container>

  <ng-container matColumnDef="number">
    <mat-header-cell *matHeaderCellDef>Number</mat-header-cell>
    <mat-cell *matCellDef="let organization">{{organization.number}}</mat-cell>
  </ng-container>

  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>

organizations.component.scss:

.spinner-container {
    height: 360px;
    width: 390px;
    position: fixed;
}

organization.component.ts:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs';

import { OrganizationService } from './organization.service';
import { Organization } from './organization.model';
import { OrganizationDataSource } from './organizationdatasource.model';

@Component({
  selector: 'organizations',
  templateUrl: './organizations.component.html',
  styleUrls: ['./organizations.component.scss']
})
export class OrganizationsComponent implements OnInit {
  public displayedColumns: string[];
  public organizationDataSource: OrganizationDataSource;
  public formGroup: FormGroup;

  public organizationSelectionAlternatives = [{
    id: 1,
    name: 'All'
  }, {
    id: 2,
    name: 'With organization update requests'
  }, {
    id: 3,
    name: 'With contact update requests'
  }, {
    id: 4,
    name: 'With order requests'
  }]

  constructor(
    private formBuilder: FormBuilder,
    private organizationService: OrganizationService) { }

  ngOnInit() {
    this.formGroup = this.formBuilder.group({
      'organizationSelectionControl': []
    })

    const toSelect = this.organizationSelectionAlternatives.find(c => c.id == 1);
    this.formGroup.get('organizationSelectionControl').setValue(toSelect);

    this.organizationDataSource = new OrganizationDataSource(this.organizationService);
    this.displayedColumns = ['name', 'number' ];
    this.updateOrganizationSelection();
  }

  updateOrganizationSelection() {
    this.organizationDataSource.loadOrganizations(this.formGroup.get('organizationSelectionControl').value.name);
  }
}

使用"concat"可以很容易地更新表中的数据:

例如:

language.component.ts

teachDS: any[] = [];

language.component.html

<table mat-table [dataSource]="teachDS" class="list">

并且,当你更新数据(language.component.ts)时:

addItem() {
    // newItem is the object added to the list using a form or other way
    this.teachDS = this.teachDS.concat([newItem]);
 }

当你使用"concat"时,angular检测对象(this.teachDS)的变化,你不需要使用其他东西。

PD:这是我在angular 6和7的工作,我没有尝试其他版本。


您可以只使用数据源连接函数

this.datasource.connect().next(data);

像这样。'data'是数据表的新值


在阅读材料表未更新后,数据更新#11638错误报告 我发现最好的(读,最简单的解决方案)是最后的评论者“shhdharmen”建议使用EventEmitter。

这涉及到对生成的数据源类进行一些简单的更改

例如,在你的数据源类中添加一个新的私有变量

import { EventEmitter } from '@angular/core';
...
private tableDataUpdated = new EventEmitter<any>();

当我将新数据推入内部数组(this.data)时,我会触发一个事件。

public addRow(row:myRowInterface) {
    this.data.push(row);
    this.tableDataUpdated.emit();
}

最后,更改'connect'方法中的'dataMutation'数组,如下所示

const dataMutations = [
    this.tableDataUpdated,
    this.paginator.page,
    this.sort.sortChange
];

// this is the dataSource    
this.guests = [];

this.guests.push({id: 1, name: 'Ricardo'});

// refresh the dataSource
this.guests = Array.from(this.guest);

我利用两种资源获得了一个很好的解决方案:

刷新dataSource和paginator:

this.dataSource.data = this.users;
this.dataSource.connect().next(this.users);
this.paginator._changePageSize(this.paginator.pageSize);

例如,dataSource定义在这里:

    users: User[];
    ...
    dataSource = new MatTableDataSource(this.users);
    ...
    this.dataSource.paginator = this.paginator;
    ...

好吧,我遇到了一个类似的问题,我向数据源添加了一些东西,但它没有重新加载。

我发现最简单的方法就是重新分配数据

let dataSource = ['a','b','c']
dataSource.push('d')
let cloned = dataSource.slice()
// OR IN ES6 // let cloned = [...dataSource]
dataSource = cloned

你也可以使用renderRows()方法。

@ViewChild(MatTable, {static: false}) MatTable //初始化

然后 this.table.renderRows ();

作为参考,看看这个: https://www.freakyjolly.com/angular-7-8-edit-add-delete-rows-in-material-table-with-using-dialogs-inline-row-operation/


我已经尝试了ChangeDetectorRef, Subject和行为Subject,但对我有用

dataSource = [];
this.dataSource = [];
 setTimeout(() =>{
     this.dataSource = this.tableData[data];
 },200)

有两种方法可以做到这一点,因为Angular Material是不一致的,而且这一点的文档非常少。当新行到达时,Angular的材质表不会更新。令人惊讶的是,它被告知是因为性能问题。但这看起来更像是一个设计问题,他们无法改变。当出现新行时,表应该进行更新。如果默认情况下不应该启用这种行为,应该有一个开关来关闭它。

不管怎样,我们不能改变Angular Material。但我们基本上可以用一种记录很差的方法来做到这一点:

一,如果你直接使用数组作为源:

call table.renderRows()

mat-table的ViewChild在哪里

第二-如果你使用排序和其他功能

table.renderRows()令人惊讶地不会工作。因为这里的垫子和桌子不一致。你需要使用黑客来告诉源代码改变了。你可以用这个方法:

this.dataSource.data = yourDataSource;

其中dataSource是用于排序和其他功能的MatTableDataSource包装器。


在Angular 9中,秘密是this.dataSource.data = this.dataSource.data;

例子:

import { MatTableDataSource } from '@angular/material/table';

dataSource: MatTableDataSource<MyObject>;

refresh(): void {
    this.applySomeModif();
    // Do what you want with dataSource

    this.dataSource.data = this.dataSource.data;
}

applySomeModif(): void {
    // add some data
    this.dataSource.data.push(new MyObject());
    // delete index number 4
    this.dataSource.data.splice(4, 0);
}

在Angular 10中,这对我来说是有效的: 在HTML中:

<mat-table [dataSource]="myArray">

在组件TS中:

    myArray: MyObject[] = [];

 addObjectToTable(object:MyObject): void {
      //TO PREVENT DUPLICATED OBJECTS
    if (object&& !this.myArray.includes(object)) {
      this.myArray.push(object);
      // TO FORCE DATA-TABLE's DATASOURCE TO REFRESH
      this.myArray= [...this.myArray];
    }
  }

我已经尝试了之前的一些建议。它确实更新了表格,但我有一些担忧:

更新数据源。数据及其克隆。如。

this.dataSource.data = [...this.dataSource.data];

如果数据很大,这将重新分配大量内存。此外,MatTable认为表内的所有内容都是新的,因此可能会导致性能问题。我发现我的表有大约300行。

调用paginator._changePageSize。如。

this.paginator._changePageSize(this.paginator.pageSize);

它将发出页面事件。如果您已经对页面事件进行了一些处理。您可能会觉得很奇怪,因为事件可能会被多次触发。如果事件以某种方式间接触发_changePageSize(),可能会导致无限循环……

我建议另一种解决方案。如果您的表不依赖于dataSource的过滤器字段。

你可以更新过滤字段来触发表刷新:

this.dataSource.filter = ' '; // Note that it is a space, not empty string

通过这样做,表将执行过滤,从而更新表的UI。但是它需要有自己的dataSource.filterPredicate()来处理过滤逻辑。


试试这个也许能帮到你

从加载用户的函数开始。

loadUser() {
    this.userService.getListOfUsers().subscribe()
        (response: any) => {
            this.dataSource = response
            this.dataSource.paginator = this.paginator;
        }

}

定义刷新函数,用于删除用户后刷新表。

refresh() {
    this.loadUser();
    this.dataSource.data = [...this.dataSource.data];
    this.dataSource.paginator = this.paginator;
}

现在,您可以在删除用户进程后调用refresh()函数,如下所示。

deleteUser() {
    ...... 
    this.refresh()
}

在添加一个新的数据行之后,我通过更新dataSource而不使用它的实例刷新了材料表。

HTML格式的表格:

<table mat-table #table [dataSource]="myDataArray">

component.ts中的addUser():

public USER_DATA: user[] = [];


public newUser = {userName: "ABC", email: "abc@gmail.com"};
public myDataArray: any;


addUser() {
    const newUsersArray = this.USER_DATA;
    newUsersArray.push(this.newUser);
    this.myDataArray = [...newUsersArray];//refresh the dataSource
    
  }

当你设置你的数据源。Ts文件,而不是使用一个简单的数组的数据使用一个主题和getter/setter的数据,如下所示:

 dataStream = new BehaviorSubject<DataSourceItem[]>(EXAMPLE_DATA);
 set data(v: DataSourceItem[]) { this.dataStream.next(v); }
 get data(): DataSourceItem[] { return this.dataStream.value; }

然后在connect()函数中使用dataStream主题,如下所示:

return merge(this.dataStream, this.paginator.page, this.sort.sortChange)

所以现在当你改变数据源时。数据数组…像这样:

this.dataSource.data = ['my','new','item']

它将触发setter中的this.dataStream.next(v), connect()函数将正确更新您的表。

完整的工作示例可以在这里找到: https://stackblitz.com/edit/angular-material2-issue-yzhsml?file=app%2Fdata-table-datasource.ts


似乎您的refresh()函数正在适当地向teachDS数据源添加一个新实例。但是,表没有被更新,因为它没有被指示这样做。

你可以指示Angular使用ChangeDetectorRef服务监控数据源的变化,并在必要时更新表。

ChangeDetectorRef服务必须首先注入到你的组件中:

import { ChangeDetectorRef } from '@angular/core';

constructor(
            private authService: AuthService, 
            private dialog: MatDialog, 
            private changeDetectorRef: ChangeDetectorRef
) { }

然后,在refresh()函数中刷新数据源后,你可以指示Angular检查更改并通过changeDetectorRef上的detectChanges()方法更新表:

refresh() {
  this.authService.getAuthenticatedUser().subscribe((res) => {
    this.user = res;
    this.teachDS = new LanguageDataSource(this.user.profile.languages.teach);
    this.changeDetectorRef.detectChanges();  // <-- Tell Angular to check for changes
  });
}

因此,应该更新表以反映数据源的更改。