Как я могу обновить свой источник данных угловой таблицы с помощью данных из наблюдаемого

#angular #observable #datasource #mat-table #angular-changedetection

Вопрос:

Я пытаюсь отобразить данные в своей таблице угловых материалов, которые я получаю из своего бэкенда.

Все работает нормально, пока моя функция _timelogService.getAllTimelogs() возвращает наблюдаемые фиктивные данные, которые я жестко закодировал в функцию. Как только я делаю фактический запрос HTTP POST на мой сервер и возвращаю Наблюдаемые данные, таблица html не отображает никаких строк, хотя мой источник данных содержит необходимые данные.

Когда я использую logData() его, отображаются правильные данные, даже если я использую фактические наблюдаемые.

Я подозреваю, что моя угловая таблица не обновляется при обновлении источника данных.

Кто-нибудь еще когда-нибудь сталкивался с такой проблемой?

 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) :
введите описание изображения здесь

Комментарии:

1. Вероятно, стоит отметить, что, хотя данные не отображаются в html, когда я вызываю console.log(this.datasource), данные там есть. Так что, должно быть, это какая-то проблема с обновлением, верно?

2. Вы пытались console.log(dataObs) и видели вывод, что данные возвращаются? Вы также можете приложить исходный код TimelogService и данные ответа к вопросу.

3. Я добавил недостающую информацию, как вы просили

Ответ №1:

Я нашел проблему, которая была причиной моей проблемы. Моя функция 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)
  } 

Спасибо всем, кто любезно помог мне.

Ответ №2:

Я никогда не использовал эту таблицу, но в документах говорится :

Поскольку таблица оптимизируется для производительности, она не будет автоматически проверять наличие изменений в массиве данных. Вместо этого, когда объекты добавляются, удаляются или перемещаются в массиве данных, вы можете запустить обновление отображаемых строк таблицы, вызвав его метод renderRows ().

Поэтому, я полагаю, вам просто нужно вызвать этот метод после загрузки ваших данных.

Чтобы вызвать метод, вам нужно добавить ссылку ViewChild в таблицу:

 <table #myTable mat-table ... >
 

Затем добавьте:

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

И после того, как данные будут загружены, просто позвоните this.myTable.renderRows();

Комментарии:

1. Я добавил функцию renderRows() в свою функцию .subscribe() и ссылку ViewChild на мой класс, но данные, к сожалению, все еще не загружаются в мою html-таблицу.

Ответ №3:

Вы должны инициировать обнаружение изменений сразу после получения новых данных.

 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() метод вместо запуска обнаружения изменений.

Комментарии:

1. Поэтому я попробовал ваш фрагмент кода, но он по-прежнему не загружает никаких данных в таблицу html.

2. Я обновил свой ответ. Вам нужно передать объект данных MatTableDataSource во время создания dataSource , как dataSource = new MatTableDataSource<TimeLogTable>(this.tableData); также показано в официальных документах

Ответ №4:

Я следовал нижеприведенному подходу, и он работает для меня, вы можете попробовать-

В магазине:

 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 или в сервисе пользователей

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

Ваши ТС:

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