Example and explanation


import { ReplaySubject } from 'rxjs';
const subject = new ReplaySubject(3, 500);
// 3: specify upto how much older values need to be stored.
// 500: amount of time upo which values should be stored

// Observer 1:
subject.subscribe(
    {
        next: (v) => console.log(`observerA: ${v}`),
    }
);
subject.next(1);
// 1 will be logged and stored for A

subject.next(2);
// 2 will be logged and stored for A

subject.next(3);
// 3 will be logged and stored for A.
// after this new values will be stored and
// older values will be deleted

subject.next(4);

// Observer 2:
subject.subscribe(
    {
        next: (v) => console.log(`observerB: ${v}`),
    }
);

// since now onwards B also subscribed subject
// hence previous buffered sized values [2,3,4] will also be
// logged for B and now onwards A,B both will subscribe the subject.

subject.next(5);

// Final Output:
// observerA: 1
// observerA: 2
// observerA: 3
// observerA: 4
// observerB: 2
// observerB: 3
// observerB: 4
// observerA: 5
// observerB: 5
  
Angular: Using ReplaySubject to Implement Reactive Form Input Handling

Advantages

  • History and Caching: It maintains a buffer of previously emitted items, allowing late subscribers to receive historical data.
  • Multi-casting: Events are multicast to multiple subscribers, ensuring all of them receive the same event history.
  • Error Recovery: If one subscriber encounters an error, other subscribers can still receive events from the buffer.
  • Testing: Useful for simulating and verifying event streams in unit tests.
  • Time Travel: Can implement features like "time travel" by controlling buffer size or time window.
  • Consistency: Provides a consistent event stream across subscribers.
  • Complex use case

    In this scenario, we aim to implement a dynamic drop-down with search functionality. The available options in the drop-down will depend on the user's search input. We'll explore two different methods to achieve this:

    • Method without ReplaySubject: In this approach, we will create a drop-down that includes a search input field. As the user types in the search input, the options displayed in the drop-down will be recalculated based on the entered search text. The filtering and updating of options will be handled directly using basic array manipulation and event handling.
    • Method with ReplaySubject: In contrast, the second approach utilises the power of ReplaySubject. We will still create a drop-down with a search input, but now the filtering process and option updates will be managed by a ReplaySubject. As the user interacts with the search input, the ReplaySubject will automatically handle the filtering and notify the drop-down to display the updated options. This method ensures better reactivity and a centralised data source for the filtered options.
    • Case 1:

    Index.html

    
    <mat-select [formControl]="searchControl" [multiple]="true" >
      <mat-option>
        <ngx-mat-select-search
          [(ngModel)]="ngxSearchModel"
          (ngModelChange)="updateOptionsList($event)" >
        <ngx-mat-select-search>
      </mat-option>
      <mat-option *ngFor="let option of filteredOptions">
        {{option}}
      </mat-option>
    </mat-select>
      

    Component.ts

    
    export Component{    
        public searchControl:FormControl = new FormControl();    
        ngxSearchModel="";    
        actualOptions=[    
            "one",    
            "two",    
            "three",    
            "four",    
            "five"    
      ]    
        filteredOptions=[]    
        ngOninit(){    
            this.filteredOptions = [...this.actualOptions];    
        }    
        updateOptionsList(searchItem){    
            if(!searchItem){    
                this.filteredOptions = this.actualOptions;    
            } else {    
                searchItem= searchItem.toLLowerCase();    
                this.filteredOptions = this.actualOptions.filter(option=>option.    
                    toLowerCase().indexOf(searchItem)>=0);    
            }    
      }    
    }
      

    • Case 2:

    HTML

    
    <mat-select [formControl]="searchControl" [multiple]="true" >
      <mat-option>
        <ngx-mat-select-search
        [(ngModel)]="ngxSearchModel"
        (ngModelChange)="updateOptionsList($event)" >
        <ngx-mat-select-search>
      </mat-option>
      <mat-option *ngFor="let option of filteredOptions|async">
        {{option}}
      </mat-option>
    </mat-select>
      

    TS

    
    export AppComponent{   
        public searchControl:FormControl = new FormControl();   
        ngxSearchModel="";   
        actualOptions=[   
            "one",   
            "two",   
            "three",   
            "four",   
            "five"   
      ]   
        filteredOptions:ReplaySubject<string[]> = new ReplaySubject<string[]>(1);   
        ngOninit(){}   
        updateOptionsList(searchItem){   
            if(!searchItem){   
                this.filteredOptions.next(this.actualOptions);   
            } else {   
                searchItem= searchItem.toLLowerCase();   
                this.filteredOptions.next(this.actualOptions.filter(option=>option.   
                    toLowerCase().indexOf(searchItem)>=0));   
            }   
      }   
    }
      

    Now what is the difference and why second approach is better?

    • Reactivity: Automatically notifies subscribers of changes to the filtered options, ensuring data stays up-to-date without manual handling.
    • Centralised data source: Acts as a single source of truth for filtered options, simplifying data access across multiple components.
    • Code organisation: Separates filtering logic from display concerns, leading to cleaner and more maintainable code.
    • Code scalability: Enables easy expansion of the application with coordinated data updates for new subscribers.
    • Efficiency: Avoids unnecessary re-filtration by replaying the last emitted value to new subscribers.
    • Consistency: Ensures all components receive the same filtered options, avoiding data inconsistencies.

    Useful Resources

    Documentation : https://rxjs-dev.firebaseapp.com/api/index/class/ReplaySubject

    This blog post was originally published here.

    FAQ

    Build Apps That Fit Your Business Operations

    Build Apps That Fit Your Business OperationsSign Up

    Build Apps That Fit Your Business Operations

    Close
    Build Apps That Fit Your Business Operations
    Get Started - It's free!