如何使用 Observable 中的数据更新我的 Angular Table 数据源

How do I update my Angular Table dataSource with data from an Observable

我正在尝试在我的 Angular Material Table 中显示从后端收到的数据。

只要我的 _timelogService.getAllTimelogs() 函数 return 是我硬编码到函数中的虚拟数据 Observable,一切正常。一旦我向我的后端发出一个实际的 HTTP POST 请求 - 并且 return 一个该数据的 Observable - 尽管我的数据源包含所需数据。

当我使用 logData() 时,即使我使用实际的 Observable,它也会显示正确的数据。

我怀疑我的 Angular Table 在数据源更新时没有更新。

有没有其他人遇到过这样的问题?

export class OverviewComponent implements OnInit {

  displayedColumns: string[] = ['roomId', 'startTime', 'endTime', 
  'duration'];
  dataSource = new MatTableDataSource<TimeLogTable>();

  @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
  @ViewChild(MatSort, { static: true }) sort: MatSort;  

  constructor(private _timelogService: TimelogService) {
    //In the Docs example the dummy data gets created here
  }

  ngOnInit(): void {
    this._timelogService.getAllTimelogs().subscribe(
      dataObs=>{
        console.log("Display received data");
        console.log(dataObs);
        this.dataSource.data = dataObs;
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;
      }
    )
  }

  logData(){
    console.log(this.dataSource);
  }
}

我的服务

getAllTimelogs(): Observable<TimeLogTable[]>{
  var timelogsArr: TimeLogTable[] = [];
  var timelogsArrDummy: TimeLogTable[] = [];

  //Call to backend
  this._http.get<any>('http://localhost:3000/gettimelogs?token=' + 
  localStorage.getItem('token'), this.httpOptions)
  .subscribe(
    res=>{
      //Create timeLogTable from json with backend response
      for (let i = 0; i < res.timelogs.length; i++) {
        timelogsArr.push({
          roomId: res.timelogs[i].roomId,
          startTime: new Date(res.timelogs[i].startTime),
          endTime: new Date (res.timelogs[i].endTime), 
          duration: "test"
        });
      }
    },
    err=>{
      //Error handling
    }
  );
  //Return the Observable - DOES NOT WORK
  return of (timelogsArr);

  /*
  //Return the dummy data as an Observable - DOES WORK
  //Dummy data
  timelogsArrDummy = [
  {roomId: 'kit_A', startTime: new Date(1631185600000), endTime: new 
  Date (1631185661311), duration: "test"},
  {roomId: 'din_A', startTime: new Date(1631185600000), endTime: new 
  Date (1631195661311), duration: "test"},
  {roomId: 'off_A', startTime: new Date(1631185600000), endTime: new 
  Date (1632185661311), duration: "test"},
  {roomId: 'kit_B', startTime: new Date(1631185600000), endTime: new 
  Date (1631885661311), duration: "test"},
  ]
  return of (timelogsArrDummy);
  */
}

这里是 console.log(dataObs) 的屏幕截图:

我从来没有用过这个 table 但是 docs 说:

Since the table optimizes for performance, it will not automatically check for changes to the data array. Instead, when objects are added, removed, or moved on the data array, you can trigger an update to the table's rendered rows by calling its renderRows() method.

所以我猜你只需要在加载数据后调用这个方法。

要调用该方法,您需要将 ViewChild 引用添加到 table:

<table #myTable mat-table ... >

然后添加:

@ViewChild(MatTable) myTable!: MatTable<any>;

加载数据后调用this.myTable.renderRows();

您应该在收到新数据后立即触发更改检测。

export class OverviewComponent implements AfterViewInit {
    
      displayedColumns: string[] = ['roomId', 'startTime', 'endTime', 'duration'];
      tableData: any = [];

      dataSource = new MatTableDataSource<TimeLogTable>(this.tableData);
    
      @ViewChild(MatPaginator) paginator: MatPaginator;
      @ViewChild(MatSort) sort: MatSort;  
      
      constructor(private _timelogService: TimelogService, private _cd: ChangeDetectorRef) {  }
    
      ngAfterViewInit() {
        this._timelogService.getAllTimelogs().subscribe(
        (dataObs: any)=> {
          this.dataSource.data = dataObs;
          this.dataSource.paginator = this.paginator;
          this.dataSource.sort = this.sort;

          this._cd.detectChanges(); 
        });
        
      }
}

请注意,如果在客户端更改数据源,建议调用 renderRows() 方法而不是触发更改检测。

我遵循了下面的方法,它对我有用,你可以试试-

店内:

private userBehaviorSubject = new BehaviorSubject<any[]>([]);
  private userApiBehaviorSubject = new BehaviorSubject<any>(null);

  private users$: Observable<User[]> = this.userBehaviorSubject.asObservable();
  private usersApi$: Observable<UserApi> = this.userApiBehaviorSubject.asObservable();

  constructor(private readonly userService: UserService
  ) {
  }

/*  loadUsersApi(url: string): void {
    this.userService.getUsersApi(url).subscribe(userApi => {this.userApiBehaviorSubject.next(userApi);
    });
  }*/

  loadUsers(url: string): void {
    this.userService.getUsersApi(url).subscribe(userApi => {
      this.userBehaviorSubject.next(userApi.items);
      this.userApiBehaviorSubject.next(userApi);
    });
  }

  getUsers(): Observable<User[]> {
    return this.users$;
  }

  getUserApi(): Observable<UserApi> {
    return this.usersApi$;
  }

在您的 get api 函数定义或 UserService

getUsersApi(url: string): Observable<UserApi> {
    if (!url){
      url = urlConstants.usersListGetUrl;
    }
    /**
     * Pass the URL parameters, prepare get URL, http method
     */
    this.options = {
      url, // urlConstants.usersListGetUrl,
      method: httpMethod.GET
    } as RestOptions;
    /**
     * Invoke the rest service for get
     */
    return this.invokeRest.invokeRest(this.options)
      .pipe(
        catchError(this.handleErrorService.handleError<any>('getUsersApi'))
      );
  }

你的TS:

import {UserStore} from '../../store/user-store';
import {Observable} from 'rxjs';
import {User} from '../../model/user/user';

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

  displayedColumns: string[] = ['userid', 'firstname', 'lastname', 'email'];

  users$: Observable<User[]>;
  usersApi$: Observable<UserApi>;

  isNext: boolean;
  isPrevious: boolean;
  isFirst: boolean;

  constructor(private userStore: UserStore,
              private userService: userService
              ) {
  }

  ngOnInit(): void {
    this.userStore.loadUsers(null);
    this.usersApi$ = this.userStore.getUserApi();
    this.users$ = this.userStore.getUsers();
    this.enableButtons();
  }

我找到了导致我遇到问题的问题。 我的 getAllTimelogs() 函数首先返回一个空对象,然后用数据填充它。 MatTableDataSource 不知何故无法处理。这是我的工作代码:

export class OverviewComponent implements OnInit, AfterViewInit {

  displayedColumns: string[] = ['roomId', 'startTime', 'endTime', 'duration'];
  dataSource = new MatTableDataSource<TimeLogTable>();

  @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
  @ViewChild(MatSort, { static: true }) sort: MatSort;  
  
  constructor(private _timelogService: TimelogService) { }

  timelogsArr: TimeLogTable[] = [];

  ngOnInit(): void {
    this._timelogService.getAllTimelogs().subscribe(
      res=>{
        //Create timeLogTable from json with backend response
        console.log("alert");
        for (let i = 0; i < res.timelogs.length; i++) {
          //Change the format of the data
          this.timelogsArr.push({
            roomId: res.timelogs[i].roomId,
            startTime: new Date(res.timelogs[i].startTime),
            endTime: new Date (res.timelogs[i].endTime), 
            duration: this.msToTime(res.timelogs[i].endTime - res.timelogs[i].startTime)
          });
        }
        //Update table datasource
        this.dataSource.data = this.timelogsArr;
      }
    );
  }

  ngAfterViewInit(): void {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }

这里是 getAlltimelogs() 函数:

getAllTimelogs(): Observable<any>{
    return this._http.get<any>('http://localhost:3000/gettimelogs?token=' + localStorage.getItem('token'), this.httpOptions)
  }

感谢所有好心帮助过我的人