【问题标题】:Angular - Material Table, is it possible to update rows without entire table refresh?Angular - 材料表,是否可以在不刷新整个表的情况下更新行?
【发布时间】:2018-05-06 17:11:16
【问题描述】:

经过几周的谷歌搜索和到目前为止只有一个 Stackoverflown 问题,我终于设法使用 Material Table Component 构建了我的 Angular CRUD 应用程序。它显示来自后端 (JSON) 的数据,对于 CRUD 操作,我正在使用如图所示的对话框(这是编辑,抱歉克罗地亚语)。对话框可能不是最好的方法,内联编辑可能会更好。但是,要添加新项目,您需要对话框之类的东西。

我坚持的最后一件事是如何相应地更新表中的字段。因此,当您在对话框中按“保存”时,数据会在后端(在 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

编辑 28.11.2017:

现在只剩下构建添加、编辑和删除的逻辑了。因为添加很简单,它只是`value.push(data)'。谢谢大家的帮助。

【问题讨论】:

    标签: angular crud angular-material2


    【解决方案1】:

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

    你可以看看我的 CRUD 使用 Angular 6Material

    实现

    数据服务

    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

    【讨论】:

    • 欢迎来到 StackOverflow。虽然给出的链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。见meta.stackexchange.com/q/8231
    【解决方案2】:

    如果有人使用数组而不是 observable 作为 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
    • 必须使用@ViewChild(MatTable, {static: true}) 表:MatTable;
    【解决方案3】:

    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 用于要更新的​​行,并将该行的更新值插入到 values 数组的索引中。

    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;
        }
      });
    }
    

    【讨论】:

      【解决方案4】:

      如果你有以下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”)

      【讨论】:

      • 是的,这是合乎逻辑的,但是如何使用模态?
      • 可以通过引用传递数据
      【解决方案5】:

      我的答案在 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();
        }
      });
      

      【讨论】:

        【解决方案6】:

        花了我一些时间,但我终于让一切正常了。您的回答和不同的方法也有帮助。所以,如果有人遇到麻烦,这是我的 CRUD 实现:

        https://github.com/marinantonio/angular-mat-table-crud

        截图:

        或者您可以查看项目演示: 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 提供程序部分中为整个应用程序定义克罗地亚语翻译,如下所示:{ provide: MatPaginatorIntl, useClass: MatPaginatorIntlCro}。如果您对我的 MatPaginatorIntlCro 实现感兴趣,我可以发布它。
        • 关于翻译,当然:)。最近我已经弄清楚了前端表更新问题的全新逻辑,所以我将编写示例应用程序并放在 github 上。 ;)
        • 在我使用时为您的整个代码获取此错误:-core.js:6260 错误类型错误:无法读取未定义的属性“sortChange”
        【解决方案7】:

        你可以看看

        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”(小写 B)。也许请求因此而失败,并且可观察的订阅永远不会完成......您可以使用订阅上的错误处理程序仔细检查该理论。

        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 样本。
        • 我正在调查您的答案,DataService 中出现错误,updateItem 函数:src/app/services/data.service.ts(40,10) 中的错误:错误 TS2540:无法分配给' data',因为它是常量或只读属性。
        • 因为我们正在更改 BehaviourSubject 而不是简单的数据数组,所以我更新了答案以包含 set data 方法。您可以尝试添加吗?
        • TS2339:“Baza[]”类型上不存在“下一个”属性。我想你可能想用 push 代替:this.data.push(data);。然后我得到其他错误:TS2345:“Baza []”类型的参数不可分配给“Baza”类型的参数。类型“Baza[]”中缺少属性“id”。感谢您提供帮助,如果有帮助,我可以将整个代码上传到 plunker 或其他东西。
        • 对不起,我又改了一次设置数据的方法,有小错别字。如果您仍然遇到问题,Plunker 会很有帮助。
        【解决方案8】:

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

        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;
          }
        

        【讨论】:

        • 您的回答实际上帮助我弄清楚了一些事情。这个.dataSource.data;行不通。我已经更新了问题。
        【解决方案9】:

        删除项目和刷新数据表的方法略有不同。它再次调用 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”。
        • 是的,重新加载数据的调用是同一个想法。但是,我的代码不同,可以用另一种方法帮助其他人。我希望:-)
        【解决方案10】:

        从您的代码中看到您正在使用分页,您可以在 crud 操作后执行以下操作:

        this.dataSource.paginator = this.paginator;
        

        这将刷新当前页面。而且,很高兴来自克罗地亚的人正在使用有棱角的材料。

        这是我的代码中的重要部分:

        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 中定义。
        • 我在我的组件中执行此操作,而不是在服务中。对我来说,这似乎不是业务问题(服务),而是显示问题(组件)。无论如何,它工作得非常好(没有新的 http 请求就超级快)。如果你能复制一个 plunker,也许我可以帮忙。如果它不起作用,那么您可能没有正确更新数据源。
        • 快速更新,我已经成功了。现在我只需要为删除、编辑构建逻辑。您的代码将对此有所帮助。
        • 在我使用时为您的整个代码获取此错误:-core.js:6260 错误类型错误:无法读取未定义的属性“sortChange”
        猜你喜欢
        • 2021-12-27
        • 1970-01-01
        • 2018-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多