loading...
Cover image for C is for combineLatest

C is for combineLatest

juliandierkes profile image Julian Dierkes ・3 min read

Let's check out the first function in this series: combineLatest.

Use this function whenever you are interested in the latest output of two or more Observables.

One can understand the behavior pretty good when looking at the marble diagram from the official documentation.
Marble diagram of combineLatest
The upper observables emit the values in the marbles and are combined with combineLatest. As you can see it emits tha latest values of all its Observables whenever on of them emits. These values are then returned as array.

Enough theory! Show us the example!

Ok. I first used this operator in the following scenario:

We had a relational database with a many-to-many relationship and two different models that had to be consolidated and displayed in the UI written in Angular. Lets use Course and Student as examples here:

export interface Course {
  id: string;
  name: string;
  studentIds: string[];
}
export interface Student {
  id: string;
  name: string;
}

However the model we want to display should look like this:

interface CourseWithStudents {
  id: string;
  name: string;
  students: Student[];
}

So we need to merge both models into one view model.

Lets have a look on how to achieve this with the combineLatest operator.

We have two Angular services providing the data in form of an Observable, the CourseService and the StudentService.
I'm also simulating some http delay in the services using setTimeout().

export class CourseService {

  private _courses: Course[] = [
    {id: '1', name: 'German', studentIds: ['1', '3', '4']},
    {id: '2', name: 'Math', studentIds: ['2', '3', '5']},
    {id: '3', name: 'Biology', studentIds: ['1', '2']}
  ];
  courses: BehaviorSubject<Course[]> = new BehaviorSubject<Course[]>([]);

  constructor() {
    setTimeout(() => {
      this.courses.next(this._courses);
    }, 1000);
  }
}
export class StudentService {

  private _students: Student[] = [
    {id: '1', name: 'Johnny Rakete'},
    {id: '2', name: 'Melissa Woods'},
    {id: '3', name: 'Gordon Thorner'},
    {id: '4', name: 'Jamy Dormer'},
    {id: '5', name: 'Will Higgs'},
    {id: '6', name: 'Smantha Claire Restful'},
  ];
  students: BehaviorSubject<Student[]> = new BehaviorSubject<Student[]>([]);

  constructor() {
    setTimeout(() => {
      this.students.next(this._students);
    }, 2000);
  }
}

This is the code that combines both Observable results once any of them emits:

this.coursesWithStudents = combineLatest([this.courseService.courses, this.studentService.students])
  .pipe(
    tap(latestResults => {
      console.log('combineLatest emitted', latestResults);
    }),
    map(latestResults => {
      const [latestCourses, latestStudents] = latestResults;
      return latestCourses.map(course => ({
        id: course.id,
        name: course.name,
        students: latestStudents.filter(student => course.studentIds.includes(student.id))
      }));
    }));

This is basically just the combineLatest operator with some mapping logic attached. The whole code returns an Observable which we can consume e.g. with the async pipe directly in the template.

Lets see the output of the above code in three tables, one for courses, one for students and one for the combined result.

resulting tables

I've also integrated some console logging which pops up after the service timeouts emit. You can see that our merged Observable emits three times.

  1. When the initial empty arrays are emitted by the BehaviorSubjects
  2. When the courses array is emitted
  3. When the students array is emitted

console output

So we achieved what was desired. Every time the courses or students are updated the resulting table gets a fresh merged view model.

Thanks for reading! Feel free to check out the other articles of this series and stay tuned for the next ones.

Posted on by:

juliandierkes profile

Julian Dierkes

@juliandierkes

I am a young full stack web developer at bosch connected industry and mainly working with Spring and Angular. In my free time I love making music and enjoy doing all sorts of craftsmanship.

Discussion

markdown guide