angularrxjsobservablerxjs-observablesbehaviorsubject

How to change/cast Observable<String> to Observable<String[]>


I am trying to use the async pipe with an observable in my receiving component so that I don't need to use OnDestroy (b/c the async pipe automatically subscribes and unsubscribes for me)

In order to do this I need to change/cast/"push" an Observable<String> to an Observable<String[]>.

Here is the original implementation, which works fine (note I haven't implemented the OnDestroy method yet)

imports...
@Component({
  selector: 'app-console-container',
  template: `
  <p>Console</p>
  <p *ngFor="let message of msgsArr" >{{message}}</p>
  `
})
export class ConsoleContainerComponent implements OnInit{
  public msgsArr: string[] = []; 
  constructor(private msgsvc: MessageService){}

  ngOnInit(): void {
    this.msgsvc.getMsg() //get initial msg
    this.msgsvc.getMsg().subscribe({next: msg => {this.msgsArr.push(msg)}}) 
  }
}

So I've got the class member declaration and the template modified:

imports...
@Component({
  selector: 'app-console-container',
  template: `
  <p>Console</p>
  <p *ngFor="let message of messages$ | async" >{{message}}</p>
  `,
})
export class ConsoleContainerComponent implements OnInit{
  public messages$: Observable<string[]> = new Observable<string[]>();
  constructor(private msgsvc: MessageService){}

  ngOnInit(): void {
//need to do the cast/type conversion/"push" here 
//or add another method to the service such as getMsgs which returns Observable<string[]>
  }
  
}

but I still need to figure out how to do the cast/type conversion/"push".
I suppose it doesn't technically matter whether I do this:

FYI - Here is my MessageService:

imports...
@Injectable({
    providedIn: 'root'
})
export class MessageService{
    private moveEmitter$ = new BehaviorSubject<string>('Start Game')

    public pushMsg(msg: string ): void {
        console.log('svc pushMsg ->', msg)
        this.moveEmitter$.next(msg)
    }

    public getMsg(): Observable<string> {
        console.log('svc getMsg -> ', this.moveEmitter$)
       return this.moveEmitter$;
    }


Solution

  • You can use scan rxjs operator to perform the accumulation for the messages.

    import { scan } from 'rxjs';
    
    this.messages$ = this.msgsvc
        .getMsg()
        .pipe(scan((acc: string[], x: string) => [...acc, x], []));
    

    Demo @ StackBlitz