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
<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.