#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);
*/
}
Комментарии:
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();
}