2

I am trying to use Angular MatTable with an async pipe. I get the data from RESTAPI as Observable. However, when I use ([dataSource] = "dataSource | async") in this way, I get the error I mentioned above.

repository.service.ts:

public GetList(controller: string): Observable<T[]> {
return this.httpclient.get<T[]>(this.apiURL + '/' + controller + '/getList', { headers: this.headers });}

contact.component.ts:

ngOnInit() {
this.contactService.GetList("OfferContact").subscribe(res => {
  this.ContactList = res
  this.setFunctions(this.ContactList)
})} 
setFunctions(list) {
  this.dataSource.data = list;
  this.spinner.hide()
  this.dataSource.paginator = this.paginator;
  this.dataSource.sort = this.sort;
  }

contact.component.html:

           <table mat-table [dataSource]="dataSource|async" matSort>

                <ng-container matColumnDef="company">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header> Firma Adı </th>
                    <td mat-cell *matCellDef="let element"> {{element.company}} </td>
                </ng-container>

                <ng-container matColumnDef="name">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header> Yetkili Adı </th>
                    <td mat-cell *matCellDef="let element"> {{element.name}} </td>
                </ng-container> 
                   ...
                 </table>
            <mat-paginator [pageSizeOptions]="[20, 30, 50, 70, 100]"></mat-paginator>

the error

ERROR Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'

this is the screenshot. As you can see at the bottom right, the data is fetched but not processed into the table.

Can anyone help with this issue?

Akif
  • 7,098
  • 7
  • 27
  • 53
Bilal GÖZET
  • 63
  • 1
  • 9

2 Answers2

3

As the error says, async pipe could only be used in conjunction with an observable. You're trying with the response from the observable instead.

Option 1: without async pipe

You could simply simply remove the async pipe. This might throw an error in the console at component initialization when the dataSource variable is undefined.

<table mat-table [dataSource]="dataSource" matSort>
    <ng-container matColumnDef="company">
        <th mat-header-cell *matHeaderCellDef mat-sort-header> Firma Adı </th>
        <td mat-cell *matCellDef="let element"> {{element.company}} </td>
    </ng-container>

    <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef mat-sort-header> Yetkili Adı </th>
        <td mat-cell *matCellDef="let element"> {{element.name}} </td>
    </ng-container> 
        ...
</table>
<mat-paginator [pageSizeOptions]="[20, 30, 50, 70, 100]"></mat-paginator>

Option 2: with async pipe

You could assign the HTTP request to a variable in the controller. You could then use map operator to set data, paginator and sort properties and finalize operator to hide the spinner.

Try the following

Controller

dataSource$: Observable<any>;  // <-- define it as an observable ('$' at end is convention)
ngOnInit() {
  this.dataSource$ = this.contactService.GetList("OfferContact").pipe(
    tap(res => {
      this.ContactList = res   // <-- is `this.ContactList` even required?
    }),
    map(dataSource => ({
      ...dataSource, 
      dataSource['data']: res,
      dataSource['paginator']: this.paginator,
      dataSource['sort']: this.sort
    })),
    finalize(() => this.spinner.hide())
  );
}

Template

<ng-container *ngIf="(dataSource$ | async) as dataSource">   <!-- wrap it in *ngIf to reuse the response -->
    <table mat-table [dataSource]="dataSource" matSort>
        <ng-container matColumnDef="company">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Firma Adı </th>
            <td mat-cell *matCellDef="let element"> {{element.company}} </td>
        </ng-container>

        <ng-container matColumnDef="name">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Yetkili Adı </th>
            <td mat-cell *matCellDef="let element"> {{element.name}} </td>
        </ng-container> 
            ...
    </table>
    <mat-paginator [pageSizeOptions]="[20, 30, 50, 70, 100]"></mat-paginator>
</ng-container>

Edit: Change res to dataSource inside map operator

ruth
  • 29,535
  • 4
  • 30
  • 57
  • Frankly, I don't know how to use the operations inside the map (...) function. For example `... dataSource,` => `Cannot find name 'dataSource'. Did you mean the instance member 'this.dataSource'?` gives a warning. `dataSource.data: res,` => `An object literal cannot have multiple properties with the same name in strict mode.` gives a warning. I don't know if `... dataSource` is an acronym or a usage. – Bilal GÖZET Jan 18 '21 at 11:49
  • That was my mistake. I assigned the response to variable `res` instead of `dataSource`. I've updated the answer. Also I've replaced dot notation with bracket notation to avoid potential `undefined` errors by TS Linter. Please try the solution again. – ruth Jan 18 '21 at 11:58
  • thank you and Akif for your effort. It didn't worked. I will use first solution – Bilal GÖZET Jan 18 '21 at 12:22
1

You can try to delete the pipe "async" like this:

 [dataSource]="dataSource"

As mentioned in the official documentation, you can use an async pipe with observable or promise. But in your case, your data source is not an observable or a promise itself. Your getList method is observable. So, you can't use an async pipe with your data source as you tried.

Akif
  • 7,098
  • 7
  • 27
  • 53
  • 1
    I know this usage. But when i use this, it takes 10-15 seconds to load page. So I wanted to use async pipe. It loads immediately but not data rows – Bilal GÖZET Jan 18 '21 at 11:20
  • 1
    I edited the answer. @Michael D has a good explanation for your requirements. – Akif Jan 18 '21 at 11:29