Search code examples
angulartypescriptobservablemat-table

How to populate matTableDataSource from Observable on first loading


I am having trouble with populating matTableDataSource on startup. On startup I get empty table with *matNoDataRow message. When I click sort on any column or type anything in filter input, the data appears immediately.

I tried to use hardcoded values for table rows and it works fine. Everything is loaded on startup. I have a working stackblitz example here where I copied everything except subscription, because it gets values from my database: StackblitzExampleOfHardcodedArray

I also tried to populate array in service and call it in component after (done) but it also did not show the rows.

Here is my original method

parseUserData: any;
returnValue: any = [];
getAllBooksFromDatabase() : BooksData[] { //all books from Bookshelf
  this.returnValue.splice(0, this.returnValue.length);
  this.data.getBooks().subscribe((data: any) => {
  this.parseUserData = JSON.parse(JSON.stringify(data));
  this.parseUserData.map((item:any) => {
    if(item.fine != 0){
        this.returnValue.push(
          {
            uid: item.uid,
            bid: item.bid,
            bookTitle: item.bookTitle,
            authorLastName: item.authorLastName,
            authorFirstName: item.authorFirstName,
            issuedDate: item.issuedDate,
            period: item.period,
            fine: item.fine,
            warning: item.warning
          }
       );
    }
})
return this.returnValue;
}

EDIT: this is my service method for obtaining data

public getBooks(): Observable<any> {
return this.http.get('http://localhost:8080/books')
  .pipe(map((books: any) => books));
}

EDIT: and this is part of result - books:

    [
  {
"warning": "",
"issuedDate": "in library",
"period": "0",
"bookTitle": "Balade Petrice Kerempuha",
"bid": "158744286",
"fine": "0",
"authorLastName": "Krleža",
"authorFirstName": "Miroslav",
"bookGenre": "poetry",
"uid": ""
  },
  {
"warning": "",
"issuedDate": "in library",
"period": "0",
"bookTitle": "Na rubu pameti",
"bid": "653621302",
"fine": "0",
"authorLastName": "Krleža",
"authorFirstName": "Miroslav",
"bookGenre": "poetry",
"uid": ""
 },
 {
"warning": "",
"issuedDate": "in library",
"period": null,
"bookTitle": "Kule u zraku",
"bid": "746388428",
"fine": null,
"authorLastName": "Larsson",
"authorFirstName": "Stieg",
"bookGenre": "triler",
"uid": null
 },
 {
"warning": "",
"issuedDate": "borrowed",
"period": "1",
"bookTitle": "U zemlji klanova",
"bid": "542455118",
"fine": "0",
"authorLastName": "Rudan",
"authorFirstName": "Igor",
"bookGenre": "popularization of science",
"uid": "0231027834"
 },
 {
"warning": "",
"issuedDate": "borrowed",
"period": "1",
"bookTitle": "Točna boja neba",
"bid": "542455578",
"fine": "0",
"authorLastName": "Rudan",
"authorFirstName": "Igor",
"bookGenre": "popularization of science",
"uid": "0231027834"
 },
 {
"warning": "",
"issuedDate": "in library",
"period": "0",
"bookTitle": "Očekujući vatre",
"bid": "548754578",
"fine": "0",
"authorLastName": "Rudan",
"authorFirstName": "Igor",
"bookGenre": "popularization of science",
"uid": ""
 },
 {
"warning": "",
"issuedDate": "in library",
"period": "0",
"bookTitle": "Zao zrak",
"bid": "548755578",
"fine": "0",
"authorLastName": "Rudan",
"authorFirstName": "Igor",
"bookGenre": "popularization of science",
"uid": ""
 }
 ]

How can I show all table rows on startup? Any advice is welcome. Thanks in advance!


Solution

  • The this.returnValue variable is assigned asynchronously. So by the time you're returning it, it's still empty. You could instead modify your data using RxJS map operator along with Array map and filter functions and assign it directly in the ngOnInit hook.

    Try the following

    ngOnInit() {
      this.getAllBooksFromDatabase().subscribe(
        books => {
          this.dataSourceBook = new MatTableDataSource(books);
          this.dataSourceBook.paginator = this.paginator;
          this.dataSourceBook.sort = this.sort;
        }
      );
    }
    
    getAllBooksFromDatabase(): Observable<BooksData[]> { // all books from Bookshelf
      return this.data.getBooks().pipe(
        map(data => {
          return data
            .filter(item => item.fine != 0)        // <-- your condition
            .map(item => {
              return {
                uid: item.uid,
                bid: item.bid,
                bookTitle: item.bookTitle,
                authorLastName: item.authorLastName,
                authorFirstName: item.authorFirstName,
                issuedDate: item.issuedDate,
                period: item.period,
                fine: item.fine,
                warning: item.warning
              }
            })
          })
      );
    }
    

    More info on async data here.