Why in my Angular code I am obtaining this error? Error: InvalidPipeArgument

578 views Asked by At

I am working on an Angular project and I have the following problem.

Basically I have this HomeComponent view code:

<div class="courses-panel">

    <h3>All Courses</h3>

    <mat-tab-group>

        <mat-tab label="Beginners">
          <courses-card-list [courses]="beginnerCourses$ | async"></courses-card-list>
        </mat-tab>

        <mat-tab label="Advanced">
          <courses-card-list [courses]="advancedCourses$ | async"></courses-card-list>
        </mat-tab>

    </mat-tab-group>

</div>

And following there is the related TypeScript code:

import {Component, OnInit} from '@angular/core';
import {Course, sortCoursesBySeqNo} from '../model/course';
import {interval, noop, Observable, of, throwError, timer} from 'rxjs';
import {catchError, delay, delayWhen, filter, finalize, map, retryWhen, shareReplay, tap} from 'rxjs/operators';
import {HttpClient} from '@angular/common/http';
import {MatDialog, MatDialogConfig} from '@angular/material/dialog';
import {CourseDialogComponent} from '../course-dialog/course-dialog.component';
import { CoursesService } from '../services/courses.service';


@Component({
  selector: 'home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  beginnerCourses$: Observable<Course[]>;

  advancedCourses$: Observable<Course[]>;


  constructor(private coursesService: CoursesService) {

  }

  ngOnInit() {

    const courses$ = this.coursesService.loadedAllCourses()
                     .pipe(
                       map(courses => courses.sort(sortCoursesBySeqNo))
                     )

    this.beginnerCourses$ = courses$
                            .pipe(
                              map(courses => courses.filter(course => course.category == "BEGINNER"))
                            );

    this.advancedCourses$ = courses$
                            .pipe(
                              map(courses => courses.filter(course => course.category == "ADVANCED"))
                            );


  }

}

As you can see in the previous code into the component initialization first I retrieve an Observable with the loadedAllCourses() method of a service class (it simply perform an HTTP request toward an API), then from this Observable I create two other Observable that are beginnerCourses$ and advancedCourses$ containing two differnt type of courses filtering on the retrieved courses Observable.

Into the view of my HomeCompoent I declare the courses-card-list component that will only show the course information, something like this:

<courses-card-list [courses]="beginnerCourses$ | async"></courses-card-list>

and:

<courses-card-list [courses]="advancedCourses$ | async"></courses-card-list>

This is the view code of this courses-card-list component:

<mat-card *ngFor="let course of (courses | async)" class="course-card mat-elevation-z10">

  <mat-card-header>

    <mat-card-title>{{course.description}}</mat-card-title>

  </mat-card-header>

  <img mat-card-image [src]="course.iconUrl">

  <mat-card-content>
    <p>{{course.longDescription}}</p>
  </mat-card-content>

  <mat-card-actions class="course-actions">

    <button mat-button class="mat-raised-button mat-primary" [routerLink]="['/courses', course.id]">
      VIEW COURSE
    </button>

    <button mat-button class="mat-raised-button mat-accent"
            (click)="editCourse(course)">
      EDIT
    </button>

  </mat-card-actions>

</mat-card>

and this is the related backend code:

import { Component, OnInit, Input } from '@angular/core';
import { Course } from '../model/course';
import { MatDialogConfig, MatDialog } from '@angular/material/dialog';
import { CourseDialogComponent } from '../course-dialog/course-dialog.component';

@Component({
  selector: 'courses-card-list',
  templateUrl: './courses-card-list.component.html',
  styleUrls: ['./courses-card-list.component.scss']
})
export class CoursesCardListComponent implements OnInit {

  @Input()
  courses: Course[] = [];


  constructor(private dialog: MatDialog) { }

  ngOnInit() {
  }

  editCourse(course: Course) {

    const dialogConfig = new MatDialogConfig();

    dialogConfig.disableClose = true;
    dialogConfig.autoFocus = true;
    dialogConfig.width = "400px";

    dialogConfig.data = course;

    const dialogRef = this.dialog.open(CourseDialogComponent, dialogConfig);

  }

}

The problem is that running my application I obtain these following error message that seems related to the two async pipes:

core.js:4081 ERROR Error: InvalidPipeArgument: '[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]' for pipe 'AsyncPipe'
    at invalidPipeArgumentError (common.js:4148)
    at AsyncPipe._selectStrategy (common.js:4250)
    at AsyncPipe._subscribe (common.js:4240)
    at AsyncPipe.transform (common.js:4228)
    at Module.ɵɵpipeBind1 (core.js:24409)
    at CoursesCardListComponent_Template (courses-card-list.component.html:1)
    at executeTemplate (core.js:7329)
    at refreshView (core.js:7198)
    at refreshComponent (core.js:8335)
    at refreshChildComponents (core.js:6991)
defaultErrorLogger @ core.js:4081
handleError @ core.js:4129
(anonymous) @ core.js:28062
invoke @ zone-evergreen.js:364
run @ zone-evergreen.js:123
runOutsideAngular @ core.js:27065
tick @ core.js:28062
(anonymous) @ core.js:27941
invoke @ zone-evergreen.js:364
onInvoke @ core.js:27138
invoke @ zone-evergreen.js:363
run @ zone-evergreen.js:123
run @ core.js:27020
next @ core.js:27940
schedulerFn @ core.js:24535
__tryOrUnsub @ Subscriber.js:183
next @ Subscriber.js:122
_next @ Subscriber.js:72
next @ Subscriber.js:49
next @ Subject.js:39
emit @ core.js:24525
checkStable @ core.js:27074
onLeave @ core.js:27184
onInvokeTask @ core.js:27132
invokeTask @ zone-evergreen.js:398
runTask @ zone-evergreen.js:167
invokeTask @ zone-evergreen.js:480
invokeTask @ zone-evergreen.js:1621
globalZoneAwareCallback @ zone-evergreen.js:1658
load (async)
customScheduleGlobal @ zone-evergreen.js:1773
scheduleTask @ zone-evergreen.js:385
onScheduleTask @ zone-evergreen.js:272
scheduleTask @ zone-evergreen.js:378
scheduleTask @ zone-evergreen.js:210
scheduleEventTask @ zone-evergreen.js:236
(anonymous) @ zone-evergreen.js:1928
(anonymous) @ http.js:1764
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
subscribeToResult @ subscribeToResult.js:9
_innerSub @ mergeMap.js:59
_tryNext @ mergeMap.js:53
_next @ mergeMap.js:36
next @ Subscriber.js:49
(anonymous) @ subscribeToArray.js:3
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
call @ mergeMap.js:21
subscribe @ Observable.js:23
call @ filter.js:13
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
shareReplayOperation @ shareReplay.js:28
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
createSubscription @ common.js:4160
_subscribe @ common.js:4241
transform @ common.js:4228
ɵɵpipeBind1 @ core.js:24409
HomeComponent_Template @ home.component.html:9
executeTemplate @ core.js:7329
refreshView @ core.js:7198
refreshComponent @ core.js:8335
refreshChildComponents @ core.js:6991
refreshView @ core.js:7248
refreshEmbeddedViews @ core.js:8289
refreshView @ core.js:7222
refreshComponent @ core.js:8335
refreshChildComponents @ core.js:6991
refreshView @ core.js:7248
renderComponentOrTemplate @ core.js:7312
tickRootContext @ core.js:8507
detectChangesInRootView @ core.js:8532
detectChanges @ core.js:9918
tick @ core.js:28052
(anonymous) @ core.js:27941
invoke @ zone-evergreen.js:364
onInvoke @ core.js:27138
invoke @ zone-evergreen.js:363
run @ zone-evergreen.js:123
run @ core.js:27020
next @ core.js:27940
schedulerFn @ core.js:24535
__tryOrUnsub @ Subscriber.js:183
next @ Subscriber.js:122
_next @ Subscriber.js:72
next @ Subscriber.js:49
next @ Subject.js:39
emit @ core.js:24525
checkStable @ core.js:27074
onHasTask @ core.js:27152
hasTask @ zone-evergreen.js:419
_updateTaskCount @ zone-evergreen.js:440
_updateTaskCount @ zone-evergreen.js:263
runTask @ zone-evergreen.js:184
drainMicroTaskQueue @ zone-evergreen.js:569
Promise.then (async)
scheduleMicroTask @ zone-evergreen.js:552
scheduleTask @ zone-evergreen.js:388
scheduleTask @ zone-evergreen.js:210
scheduleMicroTask @ zone-evergreen.js:230
scheduleResolveOrReject @ zone-evergreen.js:847
then @ zone-evergreen.js:979
bootstrapModule @ core.js:27726
./src/main.ts @ main.ts:13
__webpack_require__ @ bootstrap:78
0 @ main.ts:14
__webpack_require__ @ bootstrap:78
checkDeferredModules @ bootstrap:44
webpackJsonpCallback @ bootstrap:31
(anonymous) @ main.js:1
Show 72 more frames
core.js:4081 ERROR Error: InvalidPipeArgument: '[object Object],[object Object],[object Object]' for pipe 'AsyncPipe'
    at invalidPipeArgumentError (common.js:4148)
    at AsyncPipe._selectStrategy (common.js:4250)
    at AsyncPipe._subscribe (common.js:4240)
    at AsyncPipe.transform (common.js:4228)
    at Module.ɵɵpipeBind1 (core.js:24409)
    at CoursesCardListComponent_Template (courses-card-list.component.html:1)
    at executeTemplate (core.js:7329)
    at refreshView (core.js:7198)
    at refreshComponent (core.js:8335)
    at refreshChildComponents (core.js:6991)

What is wrong? What am I missing? How can I fix this issue?

EDIT-1:* this is my service class code:

@Injectable({
  providedIn: 'root'
})
export class CoursesService {

  constructor(private http:HttpClient) {}

  loadedAllCourses(): Observable<Course[]> {
    // The obsarvable return a JSON containing the "payload" property containing the array
    //return this.http.get<Course[]>("/api/courses");
    return this.http.get<Course[]>("/api/courses")
          .pipe(
            map(res => res["payload"]),
            /* Keep the result of this HTTP request in memory avoiding that n async pipe or subscription
               will cause n call to the API. Keep the result in memory and share between subsequence subscriber */
            shareReplay()
          );


  }

}

As you can see the loadedAllCourses() method return an Observable so I "subscribe" it with the async pipe into my HomeComponent to retrieve the courses array that I am trying to pass to my subcomponent

1

There are 1 answers

0
Dennis On BEST ANSWER

<courses-card-list [courses]="beginnerCourses$ | async">

In this call you’re “unpacking” the observable with the Async pipe turning it into regular array and assigning it to the courses @Input() of courses-card-list.

Next in the HTML template of courses-card-list you are treating this regular array as an observable again by trying to unpack it.