Angular Basics - Rxjs II: distinct, distinct utilchanged, filter

Posted by Ryaan on Mon, 28 Feb 2022 06:31:45 +0100

Introduction to RxJS

Reactive Extensions for JavaScript

RxJS is a library that uses observable sequences to write asynchronous and event based programs. It provides a core type Observable , dependent types (observers, Schedulers, Subjects) and operators inspired by [Array#extras] (map, filter, reduce, every, etc.) can handle asynchronous events as a collection.

RxJS core concept

  • Observable: represents a concept that is a collection of callable future values or events.
  • Observer: a collection of callback functions that know how to listen for values provided by Observable.
  • Subscription: indicates the execution of Observable. It is mainly used to cancel the execution of Observable.
  • Operators: pure function in functional programming style, using operators such as map, filter, concat and flatMap to process sets.
  • Subject: equivalent to EventEmitter, and is the only way to push values or events to multiple observers.
  • Schedulers: a centralized dispatcher used to control concurrency, allowing us to coordinate when calculations occur, such as setTimeout or requestAnimationFrame or others.

Distinct (filter operator)

Used to select the value of a key to check whether it is different.

Returns Observable, which emits all items issued by the source Observable that are different from the previous items.

If the keySelector function is provided, it will project each value of the source Observable into a new value, which will be used to check whether it is equal to the previously projected value. If the keySelector function is not provided, it will directly use each value of the source Observable to check whether it is equal to the previous value.

In JavaScript runtimes that support {Set}, this operator uses {Set} to improve the performance of different value checks.

In other runtimes, this operator will use the minimization implementation of , Set , and the bottom layer of this implementation depends on , Array , and , indexOf. Because more values need to be checked to distinguish, the performance will be reduced.

Even in new browsers, long-running "distinct" operations can lead to memory leaks. In order to alleviate this problem in a certain scenario, you can provide an optional , flushes , parameter, so that the internal , Set , can be "cleared", basically clearing all its values.

Sample code

import { of, Subscription } from 'rxjs';
import { distinct } from 'rxjs/operators';
import { UntilDestroy } from '@ngneat/until-destroy';

interface Person {
    age: number,
    name: string
}
@UntilDestroy({ arrayName: 'subscriptionSet' })
export class TestDistinct {

    private subscriptionSet: Subscription[] = [];

    constructor() {

        this.subscriptionSet.push(of(1, 1, 2, 2, 2, 1, 2, 3, 4, 3, 2, 1)
            .pipe(
                distinct(),
            )
            .subscribe(x => console.log(x)));
        // result:
        // 1, 2, 3, 4

        this.subscriptionSet.push(of<Person>(
            { age: 4, name: 'Foo'},
            { age: 7, name: 'Bar'},
            { age: 5, name: 'Foo'},
        ).pipe(
            distinct((p: Person) => p.name),
        )
            .subscribe(x => console.log(x)));
        // result:
        // { age: 4, name: 'Foo' }
        // { age: 7, name: 'Bar' }
    }
}

Distinguishuntilchanged (filter operator)

Used to check whether the current item is the same as the previous item in the source.

Returns Observable, which emits all items issued by the source Observable that are different from the previous item.

If the compare function is provided, it will be called by each item to check whether the value should be issued.

If the compare function is not provided, the equality check is used by default.

Sample code

In the example, InputDebounceDirective is to solve two problems

  • Each character entered by the user will trigger a search request, wasting resources;
  • Two keyup events may produce the same value;

For example, if the user enters 123, the search request will be triggered. If the user enters 1234 again and deletes 4 quickly, it will still trigger the same search request as the last time);

ts code

import { Directive, ElementRef, Output, EventEmitter } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { UntilDestroy } from '@ngneat/until-destroy';
import * as _ from 'lodash';

@UntilDestroy({ arrayName: 'subscriptionSet' })
@Directive({
    selector: '[inputDebounce]'
})
export class InputDebounceDirective {

    private subscriptionSet: Subscription[] = [];

    constructor(el: ElementRef) {
        this.subscriptionSet.push(fromEvent(el.nativeElement, 'input')
            .pipe(debounceTime(500), distinctUntilChanged())
            .subscribe(this.getInputValue));
    }

    getInputValue = (value: string) => {
        this.inputDebounceChange.emit(value);
    };
}

Html code

<input type="text"
inputDebounce
      [(ngModel)]="searchText"
      [ngModelOptions]="{ standalone: true }"
      (inputDebounceChange)="handleSearchText()"
      [placeholder]="'Search'">

Filter (filter operator)

Filter by sending only the items in the source Observable that meet the specified predicate function.

A function that evaluates each value emitted by the source Observable. If it returns "true", it will issue a value. If it is "false", it will not be passed to the output Observable. The index parameter is the index of the sending sequence since the start of subscription, which starts from # 0 #.

Code example

In the example, the form outputs content only after verification.

Html code

<form [formGroup]="userForm">
    <div class="form-group">
        <label>Name *</label>
        <input name="testName" formControlName="user.name"/>
        <label>Age *</label>
        <input name="testAge" formControlName="user.age"/>
        <label>Gender *</label>
        <input name="testGender" formControlName="user.gender"/>
    </div>
</form>

Ts code

this.userForm = new FormGroup({
            'user.name': new FormControl(this.user.name, [Validators.required]),
            'user.age': new FormControl(this.user.age, [Validators.required]),
            'user.gender': new FormControl(this.user.gender, [Validators.required]),
       });

this.userForm.valueChanges
        .pipe(
          filter(() => this. userForm.valid)
        )
        .subscribe(res => console.log(res));

 

Topics: angular