Search code examples
angulartypescriptangular-materialangular11mat-table

DataSource from APIs method "get" is not loaded in mat-table


I am new to angularCLI. In this case, I want to create a table with the dataSource is retrieved from an API (fake APIs).

Here is my component class:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { Blog, MyapiService } from 'src/app/services/myapi.service';

@Component({
  selector: 'app-blogs-put',
  templateUrl: './blogs-put.component.html',
  styleUrls: ['./blogs-put.component.css']
})
export class BlogsPutComponent implements OnInit {
  updateForm!: FormGroup;

  myArr = [
    {id: 11, title: 'MyTitle1', body: 'MyBody1'},
    {id: 12, title: 'MyTitle2', body: 'MyBody2'},
    {id: 13, title: 'MyTitle3', body: 'MyBody3'},
    {id: 14, title: 'MyTitle4', body: 'MyBody4'},
    {id: 15, title: 'MyTitle5', body: 'MyBody5'},
  ]

  // table
  blogs: Blog[] = [];
  displayedColumns: string[] = ['id', 'title'];
  dataSource = this.myArr;

  constructor(
    private apiS: MyapiService,
    private fb: FormBuilder
  ) { }

  ngOnInit(): void {
    this.getBlogs();

    this.updateForm = this.fb.group({
      Utitle: new FormControl('', Validators.required),
      Ubody: new FormControl('', Validators.required)
    })
  }

  pushBlog(){
    console.log(JSON.stringify(this.blogs))
  }

  getBlogs(){
    this.apiS.getBlog().subscribe(data => {
      this.blogs = data
    })
  }

  onSubmit(){
    console.log(this.updateForm.value)
  }

  injectData(){
    let newData = {
      Utitle: 'Lorem Ipsum',
      Ubody: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Fuga repellendus accusantium, quod cupiditate deleniti soluta cum voluptate quibusdam ut laborum ipsum praesentium earum reprehenderit delectus natus adipisci suscipit eum excepturi!'
    }

    this.updateForm.setValue(newData)
  }

}

And here's my template:

<div class="container">
  <div class="row">
    <mat-card class="full-width">
      <mat-card-title>Blogs list</mat-card-title>
      <mat-card-content>
        <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
          <ng-container matColumnDef="id">
            <th mat-header-cell *matHeaderCellDef>Id</th>
            <td mat-cell *matCellDef="let blog">{{blog.id}}</td>
          </ng-container>
          <ng-container matColumnDef="title">
            <th mat-header-cell *matHeaderCellDef>Title</th>
            <td mat-cell *matCellDef="let blog">{{blog.title}}</td>
          </ng-container>

          <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
          <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
        </table>
      </mat-card-content>
    </mat-card>
  </div>
</div>

If I assign the dataSource with this.blogs (which is an APIs get data), the table content wouldn't be loaded. But if I use this.myArr which is a local data, it loaded successfully in the table template.

Any helps would be really appreciated :)


Solution

  • According to Angular Material Table documentation,

    While an array is the simplest way to bind data into the data source, it is also the most limited. For more complex applications, using a DataSource instance is recommended. See the section "Advanced data sources" below for more information.

    Hence, it is recommended to pass [dataSource] Input property with the value of MatTableDataSource type.


    SOLUTION

    The solution should be as below:

    blogs-put.component.html (Only provide with mat-table HTML element)

    <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
      <ng-container matColumnDef="id">
          <th mat-header-cell *matHeaderCellDef>Id</th>
          <td mat-cell *matCellDef="let blog">{{blog.id}}</td>
      </ng-container>
      <ng-container matColumnDef="title">
          <th mat-header-cell *matHeaderCellDef>Title</th>
          <td mat-cell *matCellDef="let blog">{{blog.title}}</td>
      </ng-container>
    
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
    </table>
    

    blogs-put.component.ts

    import { MatTableDataSource } from '@angular/material/table';
    
    @Component({
      selector: 'app-blogs-put',
      templateUrl: './blogs-put.component.html',
      styleUrls: ['./blogs-put.component.css']
    })
    export class BlogsPutComponent implements OnInit {
      ..
    
      dataSource: MatTableDataSource<Blog>;
    
      constructor(private apiS: MyapiService, private fb: FormBuilder) {
        this.dataSource = new MatTableDataSource<Blog>();
      }
      ..
    
      getBlogs() {
        this.apiS.getBlog().subscribe(data => {
          this.blogs = data;
          this.dataSource = new MatTableDataSource(this.blogs);
        });
      }
    }
    

    Solution on StackBlitz

    EDITED:

    In case you get this warning message below:

    "Property 'dataSource' has no initializer and is not definitely assigned in the constructor."

    This is due to strictPropertyInitialization is enabled in tsconfig.json. Hence you can solve this issue by initializing the value in constructor or ngOnInit methods.

    You may refer to: Property '…' has no initializer and is not definitely assigned in the constructor.