skip to Main Content

Given two sources, one of which might be undefined how can I combineLatest of the two?

const observable1 = interval(100);
const observable2 = this.ref?.observable; // this is a reference to an angular viewchild that will not exist until some unknown point in the future

combineLatest([observable1, observable2]).subscribe(...)

I need the output observable that I’m subscribing to subscribe to this.ref.observable as soon as this.ref is defined (but I don’t know when that will be).

What I tried…

observable1.pipe(
   startWith(of(true)),
   switchMap((_) => interval(10)),
   skipWhile((_) => !this.ref),
   switchMap((_) => observable1),
   combineLatestWith(this.ref?.observable ?? EMPTY),
)
.subscribe((val1, val2]) => {...})

But a) I couldn’t figure out what the TS errors I was getting around the types returned from the combineLatestWith and b) that seems overly complicated for what seems like a simple issue!

Is there a simpler way of getting emissions from a source observable if the object the source observable is on may be undefined at the time I want to start observing?

2

Answers


  1. Create an BehaviorSubject that initially has null

    subject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
    

    Then on ngOnInit or constructor subscribe for the changes!

    ngOnInit() {
        this.subject.pipe(
           startWith(of(true)),
           switchMap((_) => interval(10)),
           skipWhile((_) => !this.ref),
           switchMap((_) => observable1),
           combineLatestWith(this.ref?.observable ?? EMPTY),
        )
        .subscribe((val1, val2]) => {...})
    }
    

    The view child generally becomes visible on ngAfterViewInit.

    ngAfterViewInit() {
        if (this.viewChild?.nativeElement) {
            this.subject.next(this.viewChild.nativeElement);
        }
    }
    
    Login or Signup to reply.
  2. My Solution for your Problem

    import { CommonModule } from '@angular/common';
    import {
      AfterViewInit,
      Component,
      OnInit,
      QueryList,
      ViewChildren,
    } from '@angular/core';
    import { combineLatest, interval } from 'rxjs';
    import { Site3Child1Component } from './site-3-child-1/site-3-child-1.component';
    
    @Component({
      selector: 'app-site-3',
      standalone: true,
      templateUrl: './site-3.component.html',
      styleUrl: './site-3.component.css',
      imports: [Site3Child1Component, CommonModule],
    })
    export class Site3Component implements OnInit, AfterViewInit {
      @ViewChildren('child1') children?: QueryList<Site3Child1Component>;
    
      protected showChild1 = false;
    
      private observable1 = interval(1000);
    
      ngOnInit(): void {
        setTimeout(() => {
          this.showChild1 = true;
        }, 2000);
      }
    
      ngAfterViewInit(): void {
        this.children?.changes.subscribe(
          (children: QueryList<Site3Child1Component>) => {
            const child1 = children.first;
            if (child1) {
              console.log('child1 is now available');
              combineLatest([this.observable1, child1.observable]).subscribe(
                ([observable1, child1Value]) => {
                  console.log(observable1, child1Value);
                }
              );
            }
          }
        );
      }
    }
    
    //child
    
    import { Component } from '@angular/core';
    import { interval } from 'rxjs';
    
    @Component({
      selector: 'app-site-3-child-1',
      standalone: true,
      imports: [],
      templateUrl: './site-3-child-1.component.html',
      styleUrl: './site-3-child-1.component.css',
    })
    export class Site3Child1Component {
      public observable = interval(3000);
    }
    <p>site-3 works!</p>
    <ng-container *ngIf="showChild1">
      <app-site-3-child-1 #child1 />
    </ng-container>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search