如何刷新一个数据源(mat-table)

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

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

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
}


}

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

346277 次浏览

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.merge订阅了包含分页器的可观察对象数组。页面中,排序。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;
}
}
< p > 例子: < br > StackBlitz < / p >
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上的_updateChangeSubscription()方法反映表上的变化。

例如:

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来创建MyDataSourcethis.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/ < / p >

大部分的演职员表都在上面一页。下面是如何使用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);

例如,数据源定义在这里:

    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 //初始化

< p >然后 < / p > this.table.renderRows ();

参考检查这个-: https://www.freakyjolly.com/angular-7-8-edit-add-delete-rows-in-material-table-with-using-dialogs-inline-row-operation/ < / p >

我已经尝试了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];
}
}

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

  1. 更新dataSource.data及其克隆。如。
this.dataSource.data = [...this.dataSource.data];

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

  1. 调用paginator._changePageSize。如。
this.paginator._changePageSize(this.paginator.pageSize);

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

我建议另一种解决方案。如果你的表不依赖于dataSourcefilter字段。

  1. 你可以更新filter字段来触发表刷新:
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
    

}