[Angular RxJS] Single data observable pattern (combineLatest with startWith)

For example we have multi API calls for one single page. 

this.course$ = this.coursesService.loadCourseById(courseId)
this.essons$ = this.coursesService.loadAllCourseLessons(courseId)

This results multi <ng-container> in html:

  <ng-container *ngIf="(course$ | async) as data">
      <ng-container *ngIf="(lessons$ | async) as data">
        ...
      </ng-container>
   </ng-container>
 

To avoid using multi ng-container, we can use single data observable pattern:

        const courseId = parseInt(this.route.snapshot.paramMap.get("courseId"));

        const course$ = this.coursesService.loadCourseById(courseId)
        const lessons$ = this.coursesService.loadAllCourseLessons(courseId)

        this.data$ = combineLatest([course$, lessons$])
            .pipe(
                map(([course, lessons]) => {
                    return {
                        course,
                        lessons
                    }
                }),
                tap(console.log)
            );

We use 'combineLatest' to merge two observable, and results in one single observable.

But this brings one performance problem. Because 'combineLatest' need to wait both inner-obervables emit value then emits the final value. We end up wait all the data coming back from server. 

Let's say, course$ should be resolved faster then lessons$, and we want to display the content related to coure$, then when the lessons$ data coming back, we display the content related to lesons$.

To solve the problem, we can let both observable emit a init value, then when the data for inner-observable coming back, it will be merged one by one.

  ngOnInit() {

        const courseId = parseInt(this.route.snapshot.paramMap.get("courseId"));

        const course$ = this.coursesService.loadCourseById(courseId)
            .pipe(
                startWith(null)
            );

        const lessons$ = this.coursesService.loadAllCourseLessons(courseId)
            .pipe(
                startWith([])
            );

        this.data$ = combineLatest([course$, lessons$])
            .pipe(
                map(([course, lessons]) => {
                    return {
                        course,
                        lessons
                    }
                }),
                tap(console.log)
            );


  }
  
<ng-container *ngIf="(data$ | async) as data">

    <div class="course">

        <h2>{{data.course?.description}}</h2>

        <img class="course-thumbnail" [src]="data.course?.iconUrl" *ngIf="data.course">

        <table class="lessons-table mat-elevation-z7" *ngIf="data.lessons.length">

            <thead>

            <th>#</th>
            <th>Description</th>
            <th>Duration</th>

            </thead>

            <tr class="lesson-row" *ngFor="let lesson of data.lessons">
                <td class="seqno-cell">{{lesson.seqNo}}</td>
                <td class="description-cell">{{lesson.description}}</td>
                <td class="duration-cell">{{lesson.duration}}</td>
            </tr>

        </table>

    </div>

</ng-container>

猜你喜欢

转载自www.cnblogs.com/Answer1215/p/12729393.html