Angular - Material 表格,是否可以在不刷新整个表格的情况下更新行?

36
几周来,我一直在谷歌上搜索,并且仅找到了一个Stackoverflown问题,最终成功构建了我的Angular CRUD应用程序,使用Material Table Component。它显示来自后端(JSON)的数据,对于CRUD操作,我使用像图片中所示的对话框(这是编辑,为克罗地亚语,很抱歉)。对话框可能不是最好的选择,内联编辑可能更好。但无论如何,添加新项目还需要像对话框这样的东西。

enter image description here

我卡在的最后一件事是如何相应地更新表中的字段。因此,当您在对话框上按“保存”时,数据会在后端(MySQL表中)中更新,但不会在前端中更新。目前,我有一个丑陋的解决方法,每次进行更新时,它也会刷新整个表格。

无论如何,以下是代码:

表格组件:

export class BazaComponent implements OnInit {
  ....
  constructor(public httpClient: HttpClient, public dialog: MatDialog) {
  }

  ngOnInit() {
    this.loadData();
  }

  // TODO: Simplfy this...
  addNew(ident: number, naziv: string, mt: number, kutija: number,
         komada: number, jm: string, orginal: number, lokacija: number, napomena: string) {
    console.log('add new clicked');
    const dialogRef = this.dialog.open(AddDialogComponent, {
      data: {ident: ident, naziv: naziv, mt: mt, kutija: kutija,
        komada: komada, jm: jm, orginal: orginal, lokacija: lokacija, napomena: napomena }
    });

    dialogRef.afterClosed().subscribe(result => {
      console.log(result);
      if (result === 1) {
        this.loadData();  // --> This is a temp workaround, every time when I do CRUD operation just redraw whole thing again
      }
    });
  }

  startEdit(id: number, ident: number, naziv: string, mt: number, kutija: number,
            komada: number, jm: string, orginal: number, lokacija: number, napomena: string) {

    const dialogRef = this.dialog.open(EditDialogComponent, {
      data: {id: id, ident: ident, naziv: naziv, mt: mt, kutija: kutija,
        komada: komada, jm: jm, orginal: orginal, lokacija: lokacija, napomena: napomena}
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result === 1) {
        this.loadData(); // --> This is a temp workaround, every time when I do CRUD operation just redraw whole thing again
      }
    });
  }

  deleteItem(id: number, ident: number, naziv: string, mt: number) {
    const dialogRef = this.dialog.open(DeleteDialogComponent, {
      data: {id: id, ident: ident, naziv: naziv, mt: mt}
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result === 1) {
        this.loadData();
      }
    });
  }


  public loadData() {
    this.exampleDatabase = new DataService(this.httpClient);
    this.dataSource = new ExampleDataSource(this.exampleDatabase, this.paginator, this.sort);
    Observable.fromEvent(this.filter.nativeElement, 'keyup')
      .debounceTime(150)
      .distinctUntilChanged()
      .subscribe(() => {
        if (!this.dataSource) {
          return;
        }
        this.dataSource.filter = this.filter.nativeElement.value;
      });
  }
}


export class ExampleDataSource extends DataSource<Baza> {
  _filterChange = new BehaviorSubject('');

  get filter(): string {
    return this._filterChange.value;
  }

  set filter(filter: string) {
    this._filterChange.next(filter);
  }

  filteredData: Baza[] = [];
  renderedData: Baza[] = [];

  constructor(private _exampleDatabase: DataService,
              private _paginator: MatPaginator,
              private _sort: MatSort) {
    super();
    // Reset to the first page when the user changes the filter.
    this._filterChange.subscribe(() => this._paginator.pageIndex = 0);
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<Baza[]> {
    // Listen for any changes in the base data, sorting, filtering, or pagination
    const displayDataChanges = [
      this._exampleDatabase.dataChange,
      this._sort.sortChange,
      this._filterChange,
      this._paginator.page,
    ];

    this._exampleDatabase.getAllItems();

    return Observable.merge(...displayDataChanges).map(() => {
      // Filter data
      this.filteredData = this._exampleDatabase.data.slice().filter((item: Baza) => {
        const searchStr = (item.ident + item.naziv + item.mt + item.lokacija + item.napomena).toLowerCase();
        return searchStr.indexOf(this.filter.toLowerCase()) !== -1;
      });

      // Sort filtered data
      const sortedData = this.sortData(this.filteredData.slice());

      // Grab the page's slice of the filtered sorted data.
      const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
      this.renderedData = sortedData.splice(startIndex, this._paginator.pageSize);
      return this.renderedData;
    });
  }

  disconnect() {
  }

  /** Returns a sorted copy of the database data. */
  sortData(data: Baza[]): Baza[] {
  ... sort stuff
}

这里是DataService,我猜我应该进行字段更新:
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http';
import { Baza } from '../models/kanban.baza';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

    @Injectable()
    export class DataService {
      private readonly API_URL = 'http://localhost/api/'

      /** Stream that emits whenever the data has been modified. */
      dataChange: BehaviorSubject<Baza[]> = new BehaviorSubject<Baza[]>([]);

      constructor(private httpClient: HttpClient) {
      }

      get data(): Baza[] {
        return this.dataChange.value;
      }

      getAllItems(): void {
        this.httpClient.get<Baza[]>(this.API_URL).subscribe(data => {
          this.dataChange.next(data['items']);
        });
      }

    addItem(baza: Baza): void {
      this.httpClient.post(this.API_URL, Baza).subscribe(data => {
          //THIS WAS MY BEST TRY BUT IT DOESN'T WORK :(
          const copiedData = this.data.slice();
          copiedData.push(baza);
          console.log(copiedData);
          this.dataChange.next(copiedData);
      });
    }


      updateItem(baza: Baza): void {
        this.httpClient.put(this.API_URL + baza.id, baza).subscribe();
      }

      deleteItem(id: number): void {
        this.httpClient.delete(this.API_URL + id, {headers: new HttpHeaders().set('Access-Control-Allow-Origin', '*')} ).subscribe();
    }
}

更新于2017年11月27日:

好的,我终于找到了如何触发新行添加的方法。我必须在表格组件中调用dataChange.value。一旦你加载了一些数据,新行就会立即出现。

const data = {id: 208, ident: 233, naziv: 'test', mt: 291, komada: 2, jm: 'a', orginal: 100, lokacija: 3, napomena: 'pls work'};
this.exampleDatabase.dataChange.value.push(data);

在DataService中同样的方法是行不通的:

this.dataChange.value.push(data); 

Plunker在这里:

https://plnkr.co/edit/IWCVsBRl54F7ylGNIJJ3?p=info

编辑于2017年11月28日:

现在只剩下添加、编辑和删除的逻辑需要构建。添加很容易,只需使用 `value.push(data)` 即可。感谢大家的帮助。

10个回答

27
花了些时间,但我终于把一切都搞定了。你们的答案和不同的方法也帮了我一些忙。所以,如果有人遇到问题,这是我的CRUD实现:https://github.com/marinantonio/angular-mat-table-crud。截图如下:Alt Text或者你可以查看项目演示:https://marinantonio.github.io/angular-mat-table-crud/。关键部分在table.ts文件中。
....
addNew(issue: Issue) {
    const dialogRef = this.dialog.open(AddDialogComponent, {
      data: {issue: issue }
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result === 1) {
        this.exampleDatabase.dataChange.value.push(this.dataService.getDialogData());
        this.refreshTable();
      }
    });
  }

  startEdit(i: number, id: number, title: string, state: string, url: string, created_at: string, updated_at: string) {
    this.index = i;
    this.id2 = id;
    console.log(this.index);
    const dialogRef = this.dialog.open(EditDialogComponent, {
      data: {id: id, title: title, state: state, url: url, created_at: created_at, updated_at: updated_at}
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result === 1) {
        // Part where we do frontend update, first you need to find record using id
        const foundIndex = this.exampleDatabase.dataChange.value.findIndex(x => x.id === this.id2);
        // Then you update that record using dialogData
        this.exampleDatabase.dataChange.value[foundIndex] = this.dataService.getDialogData();
        // And lastly refresh table
        this.refreshTable();
      }
    });
  }

  deleteItem(i: number, id: number, title: string, state: string, url: string) {
    this.index = i;
    this.id2 = id;
    const dialogRef = this.dialog.open(DeleteDialogComponent, {
      data: {id: id, title: title, state: state, url: url}
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result === 1) {
        const foundIndex = this.exampleDatabase.dataChange.value.findIndex(x => x.id === this.id2);
        this.exampleDatabase.dataChange.value.splice(foundIndex, 1);
        this.refreshTable();
      }
    });
  }


  private refreshTable() {
    // If there's no data in filter we do update using pagination, next page or previous page
    if (this.dataSource._filterChange.getValue() === '') {
      if (this.dataSource._paginator.pageIndex === 0) {
        this.dataSource._paginator.nextPage();
        this.dataSource._paginator.previousPage();
      } else {
        this.dataSource._paginator.previousPage();
        this.dataSource._paginator.nextPage();
      }
      // If there's something in filter, we reset it to 0 and then put back old value
    } else {
      this.dataSource.filter = '';
      this.dataSource.filter = this.filter.nativeElement.value;
    }
}
....

很好。只是提一下,你可以在app.module.ts的provider部分定义整个应用程序的克罗地亚语翻译,像这样:{ provide: MatPaginatorIntl, useClass: MatPaginatorIntlCro }。如果你对我的MatPaginatorIntlCro实现感兴趣,我可以发布它。 - zszep
关于翻译,当然可以 : )。最近我已经想出了前端表格更新问题的全新逻辑,所以我打算编写一个示例应用程序并将其放在Github上。;) - besthiroeu
在我使用以下代码时,整个代码出现了以下错误:core.js:6260 ERROR TypeError: Cannot read property 'sortChange' of undefined - rocker-hacker

9
从您的代码中我看到您使用了分页,您可以在crud操作后执行以下操作:
this.dataSource.paginator = this.paginator;

这将刷新当前页面。很高兴有来自克罗地亚的人使用angular material。
以下是我代码中的重要部分:
dialogRef.afterClosed().subscribe(result => {
    if (result === null) { return; }
    switch (mode) {               // add new
        case 'C': {
            data.push(result.vendor);
            this.refreshTable();
            break;
        }
        case 'U': {               // update
            const index = data.findIndex((item) => item.buFmisVendorId === result.vendor.buFmisVendorId);
            if (index > -1) {
                data[index] = vendor;
                this.refreshTable();
            }
            break;
        }

    }
});

private refreshTable() {
    this.dataSource.paginator = this.paginator;
}

我实际上是Android开发者,所以我有点迷失了。:D 无论如何refreshTable确实有效,因为我可以看到页面闪烁一秒钟,但我仍然遇到问题:“data.push(result.vendor);”。我最好的猜测是,由于我在那里从对话框获取值,所以我应该在我的dataService.ts中执行data.push。表behaviorSubject也在DataServices.ts中定义。 - besthiroeu
我是在我的组件中实现的,而不是在服务中。对我来说,它似乎不是业务关注点(服务),而是显示关注点(组件)。无论如何,它的工作非常好(超快,没有新的http请求)。如果您能够重现一个plunker,也许我可以帮忙解决。如果它没有工作,那么您可能没有正确更新数据源。 - zszep
快速更新,我已经成功完成了。现在我只需要构建删除、编辑的逻辑。你的代码对此将有很大帮助。 - besthiroeu
在我使用以下代码时,您的整个代码都会出现以下错误:core.js:6260 ERROR TypeError: Cannot read property 'sortChange' of undefined - rocker-hacker

7

我有一些解决方案可以在不使用模态窗口的情况下编辑表格中的数据。

您可以查看我的基于Angular 6MaterialCRUD实现。

数据服务

import {Injectable} from '@angular/core';
import {HttpClient, HttpParams, HttpHeaders} from '@angular/common/http';
import {User} from './user';

@Injectable()
export class UserService{
private url = "http://localhost:51120";

constructor(private http: HttpClient){ }
getUsers(){
    let getUrl = this.url + "/api/all/";
    return this.http.get(getUrl);
}
createUser(user: User){
    let saveUrl = this.url + "/api/Users";
    return this.http.post(saveUrl, user); 
}
updateUser(id: number, user: User) {
    const urlParams = new HttpParams().set("id", id.toString());
    return this.http.post(this.url + "/api/update", user);
}
deleteUser(id: number){
    const urlParams = new HttpParams().set("id", id.toString());
    return this.http.delete(this.url + "/api/delete/" + id);
 }
}

组件
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [UserService]
})
export class AppComponent implements OnInit {

@ViewChild(MatPaginator) paginator: MatPaginator;

addNewUser: User[] = [
    { Id: 0, Name: null, Age: null, Email: null, Surname: null }
];

users: Array<User>;
showTable: boolean;
statusMessage: string;
isLoaded: boolean = true;
displayedColumnsUsers: string[] = ['Id', 'Name', 'Surname', 'Age', 'Email', 'Change', 'Delete'];
displayedColumnsAddUser: string[] = ['Name', 'Surname', 'Age', 'Email', 'Save', 'Cancel'];
dataSourceUsers: any;
dataSourceAddUser: any;
newUser : User;

constructor(private serv: UserService, public dialog: MatDialog, public snackBar: MatSnackBar) {
    this.users = new Array<User>();
}

@ViewChild(MatSort) sort: MatSort;

ngOnInit() {
    this.loadUsers();
    this.dataSourceAddUser = new MatTableDataSource();
}

applyFilter(filterValue: string) {
    this.dataSourceUsers.filter = filterValue.trim().toLowerCase();

    if (this.dataSourceUsers.paginator) {
        this.dataSourceUsers.paginator.firstPage();
    }
}

private loadUsers() {
    this.isLoaded = true;
    this.serv.getUsers().subscribe((data: User[]) => {
        this.users = data;
        this.users.sort(function (obj1, obj2) {
            // Descending: first id less than the previous
            return obj2.Id - obj1.Id;
        });
        this.isLoaded = false;
        this.dataSourceUsers = new MatTableDataSource(this.users);
        this.dataSourceAddUser = new MatTableDataSource(this.addNewUser);
        this.dataSourceUsers.sort = this.sort;
        this.dataSourceUsers.paginator = this.paginator;
    },
        error => {
            alert("Error: " + error.name);
            this.isLoaded = false;
        }
    );
}

deleteUserForDialog(user: User) {
    this.serv.deleteUser(user.Id).subscribe(data => {
        this.statusMessage = 'User ' + user.Name + ' is deleted',
            this.openSnackBar(this.statusMessage, "Success");
        this.loadUsers();
    })
}

editUser(user: User) {
    this.serv.updateUser(user.Id, user).subscribe(data => {
        this.statusMessage = 'User ' + user.Name + ' is updated',
        this.openSnackBar(this.statusMessage, "Success");
        this.loadUsers();
    },
        error => {
            this.openSnackBar(error.statusText, "Error");
        }
    );
}

saveUser(user: User) {
    if (user.Age != null && user.Name != null && user.Name != "" && user.Age != 0) {
        this.serv.createUser(user).subscribe(data => {
            this.statusMessage = 'User ' + user.Name + ' is added',
            this.showTable = false;
            this.openSnackBar(this.statusMessage, "Success");
            this.loadUsers();
        },
            error => {
                this.showTable = false;
                this.openSnackBar(error.statusText, "Error");
            }
        );
    }
    else {
        this.openSnackBar("Please enter correct data", "Error")
    }
}

show() {
    this.showTable = true;
    this.addNewUser = [{ Id: 0, Name: null, Age: null, Email: null, Surname: null }];

}
cancel() {
    this.showTable = false;
}

//snackBar
openSnackBar(message: string, action: string) {
    this.snackBar.open(message, action, {
        duration: 3000,
    });
}

//material dialog
openDialog(element): void {
    const dialogRef = this.dialog.open(DialogOverviewExampleDialogComponent, 
{
        width: '250px',
        data: element,
    });

    dialogRef.afterClosed().subscribe(result => {
        console.log('The dialog was closed');
        if (result == "Confirm") {
            this.deleteUserForDialog(element);
        }
    });
}

//   Form field with error messages 
name = new FormControl('', [Validators.required]);

getErrorMessage() {
    return this.name.hasError('required') ? 'You must enter a value' :
        this.name.hasError('name') ? 'Not a valid name' : '';
}

age = new FormControl('', [Validators.required]);

email = new FormControl('', [Validators.required, Validators.email]);
surnameFormControl= new FormControl('', [Validators.required]);
emailGetErrorMessage() {
    return this.email.hasError('required') ? 'You must enter a value' :
        this.email.hasError('email') ? 'Not a valid email' :
            '';
}

onSubmit(newUser:User){
    this.newUser = new User(0,"",0,"","");
}
}

https://github.com/AleksandrChuikov/AngularMaterialCRUD

这里是演示链接: https://crud-angular6.azurewebsites.net

已更新至Angular 8

已更新至Angular 12

Click here to see screenshot


欢迎来到StackOverflow。虽然提供链接可以回答问题,但最好在此处包含答案的关键部分并提供链接以供参考。如果链接页面发生更改,则仅有链接的答案可能无效。请参见https://meta.stackexchange.com/q/8231 - Adrian W

7
实际上,在编辑后你不需要刷新表格,如果你有以下HTML代码:
<mat-table [dataSource]="dataSource" matSort>
      <ng-container matColumnDef="userName">
        <mat-header-cell mat-sort-header> UserName </mat-header-cell>
        <mat-cell *matCellDef="let row"> {{row.userName}} </mat-cell>
      </ng-container>
      <ng-container matColumnDef="actions">
        <mat-cell *matCellDef="let user">
          <button mat-icon-button matTooltip="Edit" (click)="editUser(user)">
            <mat-icon>edit</mat-icon>
          </button>
        </mat-cell>
      </ng-container>
      <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
      <mat-row *matRowDef="let row; columns: displayedColumns;">
      </mat-row>
</mat-table>

同时,在.ts文件中:

private editUser(user?: User) {
    let userTest: User = user;
    userTest.userName = "user123";
  }

当您单击“编辑”时,可以看到行自动更改用户名(在此示例中更改为“user123”)


是的,这很合乎逻辑,但是如何处理模态框呢? - EgoistDeveloper
你可以通过引用传递数据。 - 321X
我使用了建议的答案,它有效,并且不需要刷新表格,只需在模态框关闭后更新行内容,谢谢! - vcg

5

这个解决方案使用了我现有的删除代码,但同样适用于更新代码。关键问题是找到编辑或删除的项的数组索引。请注意,一旦结果成功,我调用一个成功模态框来通知用户,然后调用一个函数从数据表中删除该行。或者您可以使用略有不同的代码将数据推入对象数组中,以更新该行的数据。这样我们就不必再次下载所有数据。

public deleteMember(memberId) {
      // Call the confirm dialog component
      this.confirmService.confirm('Confirm Delete', 'This action is final. Gone forever!')
          .switchMap(res => {if (res === true) {
              return this.appService.deleteItem(this.dbTable, memberId);
          }})
          .subscribe(
              result => {
                this.success();
                // Refresh DataTable to remove row.
                this.updateDataTable (memberId);
              },
              (err: HttpErrorResponse) => {
                  console.log(err.error);
                  console.log(err.message);
                this.messagesService.openDialog('Error', 'Delete did not happen.');
              }
          );
  }

现在,让我们移除或更新已删除或编辑的行。
private dsData: any;
  // Remove the deleted row from the data table. Need to remove from the downloaded data first.
  private updateDataTable (itemId) {
    this.dsData = this.dataSource.data;
    if (this.dsData.length > 0) {
      for (let i = 0; i < this.dsData.length; i++ ) {
        if (this.dsData[i].member_id === itemId) {
          this.dataSource.data.splice(i, 1);
        }
      }
    }
    this.dataSource.paginator = this.paginator;
  }

你的回答实际上帮助我弄清楚了一些事情。不过,这个数据源(this.dataSource.data)不起作用。我已经更新了问题。 - besthiroeu
我忘记添加 this.dataSource.paginator = this.paginator; 现在它可以工作了。谢谢你。 - A.Casanova

4
我的答案涉及到 Angular 6 Material 2
我使用了 splice 函数,它的参数包括编辑行的索引、要删除的行数(在您的情况下为1),以及将插入该索引处的已编辑行的新版本。
dialogRef.afterClosed().subscribe(result => {
  if(result !== '' && result !== null) {
    const idx_editedRow = this.mattabledatasource.data.indexOf(row);
    this.mattabledatasource.data.splice(idx_editedRow, 1, result);
    loadData();
  }
});

3

删除项目并刷新数据表的方法略有不同。它再次调用API,但这可能适用于较小的数据集。

public deleteMember(memberId) {
      // Call the confirm dialog component
      this.confirmService.confirm('Confirm Delete', 'This action is final. Gone forever!')
          .switchMap(res => {if (res === true) {
              return this.appService.deleteItem(this.dbTable, memberId);
          }})
          .subscribe(
              result => {
                this.success();
                // Refresh DataTable to remove row.  This solution calls the db and is a hack.
                this.ngAfterViewInit();
              },
              (err: HttpErrorResponse) => {
                  console.log(err.error);
                  console.log(err.message);
                this.messagesService.openDialog('Error', 'Delete did not happen.');
              }
          );
  }

这个通常在组件的顶部被调用,但这里仅供参考。

private dbTable = 'members';
dataSource = new MatTableDataSource();

ngAfterViewInit() {
    this.appService = new AppService(this.http);
    this.dataSource.sort = this.sort;
    this.dataSource.paginator = this.paginator;


    // Populate the Material2 DataTable.
    Observable.merge(this.paginator.page)
      .startWith(null)  // Delete this and no data is downloaded.
      .switchMap(() => {
        return this.appService.getItems( this.dbTable,
          this.paginator.pageIndex);
      })
      .map(data => {
        return data.resource;
      })
      .subscribe(data => {
        this.dataLength = data.length;
        this.dataSource.data = data;
      });
  }

你的代码不是和我的临时解决方案很像吗?在进行CRUD操作后,我只需调用“this.loadData();”来调用“getAllData”。 - besthiroeu
是的,重新加载数据的调用是相同的想法。然而,我的代码不同,可能会帮助其他人采用另一种方法。我希望如此 :-) - Preston

2

Can you take a look at

addItem(baza: Baza): void {
  this.httpClient.post(this.API_URL, Baza).subscribe(data => {
      //THIS WAS MY BEST TRY BUT IT DOESN'T WORK :(
      const copiedData = this.data.slice();
      copiedData.push(baza);
      console.log(copiedData);
      this.dataChange.next(copiedData);
  });
}

POST请求是否正常工作并发送数据?您在POST请求中引用了Baza,应该是小写'baza'。也许请求失败是因为这个原因,可通过对订阅的错误处理程序进行双重检查来验证该理论。

addItem(baza: Baza): void {
  this.httpClient.post(this.API_URL, baza).subscribe(data => {
      const copiedData = this.data.slice();
      copiedData.push(baza);
      console.log(copiedData);
      this.dataChange.next(copiedData);
  }, (errror) => {
    console.log(error);
  });
}

关于编辑,我的方法略有不同。将相同的 DataService 实例注入到组件中,并将此引用传递给表格 DataSource,而不是新实例。接下来,将整个 baza 对象传递到编辑对话框中,而不仅仅是它的属性。然后,在对话框关闭时,将原始(未编辑的)对象以及新属性(或更好的做法是使用已编辑字段的 Baza 类新对象)一起传递。将它们发送到我们的数据服务中,使用“编辑/更新”方法。编辑/更新方法将过滤现有的数据数组,查找与我们的未编辑对象匹配的任何条目,并将它们设置为等于我们的新对象。以下是稍微抽象的示例。

//例如,组件

export class BazaComponent implements OnInit {
  ....
  constructor(
    public httpClient: HttpClient, 
    public dialog: MatDialog,
    public dataService: DataService
  ){}
  ....
  public loadData() {
    this.dataSource = new ExampleDataSource(this.dataService, this.paginator, this.sort);
    Observable.fromEvent(this.filter.nativeElement, 'keyup')
      .debounceTime(150)
      .distinctUntilChanged()
      .subscribe(() => {
        if (!this.dataSource) {
          return;
        }
        this.dataSource.filter = this.filter.nativeElement.value;
      });
  }
  ....
  startEdit(baza: Baza) {
    const dialogRef = this.dialog.open(EditDialogComponent, {
      data: {
        baza: baza
      }
    });

    dialogRef.afterClosed().subscribe(result => {
      // result will be simple array of our 'old' baza object that we passed in, and the 'new' baza object that contains the edits
      this.dataService.updateItem(result[0], result[1]);
    });
  }

  dialogRef.close(['close',editBaza,baza]);

// 例如,服务

export class DataService {
  ....
  set data(data: Baza[]) {
    this.dataChange.next(data);
  }
  ....
  updateItem(oldBaza: Baza, newBaza: Baza){
    this.data = this.data.map((baza: Baza) => {
      if(baza === oldBaza) return newBaza;
      return baza;
    });
  }

是的,Http部分可以工作,实际上所有的CRUD操作都可以工作。 :) Baza只是一个模型:import { Baza } from '../models/baza';。那里没有什么花哨的东西,如果您想查看它,我可以编辑问题。我只有在Mat Table方面遇到问题,因为一切都是新的,在网络上没有可用的工作crud示例。 - besthiroeu
我正在查看你的回答,在DataService中遇到了错误,updateItem函数:ERROR in src/app/services/data.service.ts(40,10): error TS2540: Cannot assign to 'data' because it is a constant or a read-only property. - besthiroeu
因为我们正在更改BehaviorSubject而不是简单的数据数组,所以我已经更新了答案,包括一个设置数据方法。你可以尝试添加它。 - NJ.
TS2339:在类型“Baza []”上不存在属性“next”。我认为您可能希望改用push:this.data.push(data); 然后我得到其他错误:TS2345:类型“Baza []”的参数无法分配给类型“Baza”。类型“Baza []”中缺少属性“id”。感谢您的尝试帮助,如果有帮助,我可以将整个代码上传到plunker或其他地方。 - besthiroeu
抱歉,我又修改了一下设置数据的方法,有个小笔误。如果你还有问题,Plunker可能会有所帮助。 - NJ.
仍然不起作用,我已经更新了问题,请您看一下。现在我必须想办法在Table组件内获取对话框数据。目前它是这样工作的:Table组件打开对话框->AddDialogComponent从表单中获取数据->DataService获取数据并执行HttpRequest。如果我可以从那里更新表格,那就最好了,因为服务从表单收集数据。稍后我会制作plunker。 - besthiroeu

2

jobposting.component.ts文件的结构:

export class JobPostingComponent implements OnInit {
  values: JobPosting[];
  columns: string[] = ['title', 'vacancies','division.name'];
  displayedColumns: string[] = ['actions'].concat(this.columns);
  dataSource: MatTableDataSource<JobPosting>;

我使用findIndex来找到需要更新的行,并将更新后的行值插入到该索引处的值数组中。"Original Answer"翻译成"最初的回答"。
onEdit(data: JobPosting) {
  const dialogRef = this.dialog.open(AddJobPostingComponent, {
    data,
    width: '1000px'
  });

  dialogRef.afterClosed().subscribe(res => {
    if (res !== undefined) {
      const id = res.id;
      const index = this.values.findIndex(x => x.id === id);
      this.values[index] = res;
      this.dataSource.data = this.values;
    }
  });
}

2
如果有人使用数组而不是可观察对象作为MatDataSource,我发现一种解决方法适用于我。这是我的数据源。
this.dataSource = new MatTableDataSource(ingredientList);

这是我的更新方法。
updateRowData(id, newData): void {
   const index = this.dataSource.data.findIndex((inc: any) => inc.ingredientId === id);
   if (index !== -1) {
     this.dataSource.data[index] = newData;
     this.dataSource.data = this.dataSource.data.slice(0);

     this.table.renderRows();
   }
  }

什么是 this.table - anatol
必须使用 @ViewChild(MatTable, {static: true}) table: MatTable<>; - headmelon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接