angularinner-joinangular-material-tableangular-json

How to get data from 2 api and view data in angular material table?


Ihave 2 api ( users and category ), and I have relationship between this api in category id, how can I make join this api like inner join in sql.

My Code :

{
  "list": [
    {
      "id": 1,
      "title": "samsung",
      "body": "samsung is ......",
      "userId": "1"
    },
    {
      "id": 2,
      "title": "google",
      "body": "google is ......",
      "userId": "1"
    },
    {
      "id": 1,
      "title": "yahoo",
      "body": "yahoo is ......",
      "userId": "2"
    }
  ],
  "count": 3,
  "success": true
}
{
  "list": [
    {
      "userId": 1,
      "username": "Michil"
    },
    {
      "userId": 2,
      "username": "Alix"
    },
    {
      "userId": 3,
      "username": "Jon"
    }
  ],
  "count": 3,
  "success": true
}
import { PostsList } from "./PostsList "

export interface Posts{
    list: PostsList[]
    count: number
    success: boolean
}
export interface PostsList {
    id: number
    title: string
    body: string
    userId: string
}
import { UsersList} from "./UsersList"

export interface Users{
    list: List[]
    count: number
    success: boolean
}
export interface UsersList{
    userId: number
    username: string
}
getPosts(): Observable<Posts>{
    return this.http.get<Posts>(`https://api.api.com/post/list`).pipe(
      tap(posts=> console.log(posts)),
    );
 }

getUsers(): Observable<Users>{
    return this.http.get<Users>(`https://api.api.com/users/list`).pipe(
      tap(users => console.log(users)),
    );
 }

export class UsersComponent implements OnInit{
  displayedColumns: string[] = ['id', 'title', 'body', 'userId', 'username'];
  users:any;

  constructor(private myService: MyService){ }

  ngOnInit(): void {
    this.onGetUsers();
  }

  onGetPosts(): void{
    this.myService.getPosts().subscribe(
      (response => {
        this.users = new MatTableDataSource<Posts>(response);
      })
    );
  }

  onGetUsers(): void{
    this.myService.getUsers().subscribe(
      (response => {
        this.Posts = new MatTableDataSource<Posts>(response);
        this.Users = new MatTableDataSource<Users>(response);
      })
    );
  }

<table mat-table [dataSource]="users" class="mat-elevation-z8">
  
     Position Column 
    <ng-container matColumnDef="id">
      <th mat-header-cell *matHeaderCellDef> id</th>
      <td mat-cell *matCellDef="let element"> {{element.id}} </td>
    </ng-container>

    <ng-container matColumnDef="title">
      <th mat-header-cell *matHeaderCellDef> title</th>
      <td mat-cell *matCellDef="let element"> {{element.title}} </td>
    </ng-container>
  
    <ng-container matColumnDef="body">
      <th mat-header-cell *matHeaderCellDef> body</th>
      <td mat-cell *matCellDef="let element"> {{element.body}} </td>
    </ng-container>
  
    <ng-container matColumnDef="userId">
      <th mat-header-cell *matHeaderCellDef> userId </th>
      <td mat-cell *matCellDef="let element"> {{element.userId}} </td>
    </ng-container>

    <ng-container matColumnDef="username">
      <th mat-header-cell *matHeaderCellDef> username </th>
      <td mat-cell *matCellDef="let element"> {{element.username}} </td>
    </ng-container>
  
    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

How can I display username from another api ?


Solution

  • You can create a new interface which combines both PostsList and UsersList like:

    export interface TableView {
      post: PostsList;
      user: UsersList;
    }
    

    After that, you need to populate data in this view. The only problem is that it depends on both API's, therefore we need result from both API's and after that, we can try to join data from both API's. One easy way to do this is through Promise.all.

    export class UsersComponent implements OnInit {
      displayedColumns: string[] = ['id', 'title', 'body', 'userId', 'username'];
      // Datasource for table
      dataSource: MatTableDataSource<TableView> =
        new MatTableDataSource<TableView>();
    
      constructor(private myService: UsersService) {}
    
      ngOnInit(): void {
        // Promise for getting results of both API's
        let dataPromise: Array<Promise<any>> = [];
        // Convert observables and push to this promise
        dataPromise.push(this.myService.getPosts().toPromise());
        dataPromise.push(this.myService.getUsers().toPromise());
    
        // Do data manipulation after we get results from both Promises
        Promise.all(dataPromise).then((responseList) => {
          // First item in responseList is from getPosts
          let posts: Posts = responseList[0];
          // Second item in responseList is from getUsers
          let users: Users = responseList[1];
          // Temporary array for storing data
          let tableData: Array<TableView> = [];
    
          posts.list.forEach((x) => {
            tableData.push({
              post: x,
              user: users.list.filter((y) => y.userId.toString() === x.userId)[0], // get the user for this userId
            });
          });
          this.dataSource = new MatTableDataSource<TableView>(tableData);
        });
      }
    }
    
    

    Now change your HTML to use new interface.

    <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
      Position Column
      <ng-container matColumnDef="id">
        <th mat-header-cell *matHeaderCellDef>id</th>
        <td mat-cell *matCellDef="let element">{{ element.post.id }}</td>
      </ng-container>
    
      <ng-container matColumnDef="title">
        <th mat-header-cell *matHeaderCellDef>title</th>
        <td mat-cell *matCellDef="let element">{{ element.post.title }}</td>
      </ng-container>
    
      <ng-container matColumnDef="body">
        <th mat-header-cell *matHeaderCellDef>body</th>
        <td mat-cell *matCellDef="let element">{{ element.post.body }}</td>
      </ng-container>
    
      <ng-container matColumnDef="userId">
        <th mat-header-cell *matHeaderCellDef>userId</th>
        <td mat-cell *matCellDef="let element">{{ element.user.userId }}</td>
      </ng-container>
    
      <ng-container matColumnDef="username">
        <th mat-header-cell *matHeaderCellDef>username</th>
        <td mat-cell *matCellDef="let element">{{ element.user.username }}</td>
      </ng-container>
    
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
    </table>
    
    

    Demo: https://stackblitz.com/edit/angular-tm2met?file=src%2Fusers%2Fusers.component.ts