Sunday, 19 September 2021

RXJS: Filter => These operators provide techniques for accepting values from an observable source



import { fromof } from 'rxjs';
import { filterfirstlastsingleskipskipWhile
    distinct,taketakeLast  } from 'rxjs/operators';

const source = from([12345]);
const source$ = of(12345);
//filter out non-even numbers
let example = source.pipe(filter(num => num % 2 === 0));
//output: "Even number: 2", "Even number: 4"
//no arguments, emit first value
example = source.pipe(first());
//output: "First value: 1"
example = source.pipe(last());
//output: "Last value: 5"
//emit one item that matches predicate
example = source.pipe(single(val => val === 4));
//output: 4
//skip the first 2 emitted values
example = source.pipe(skip(2));
//output: 3...4...5...
//skip emitted values from source until inner observable emits (6s)
example = source.pipe(skipWhile(a => a < 3));
//output: 3...4...5
of(1234512345).pipe(distinct()).subscribe(console.log);
// OUTPUT: 1,2,3,4,5
//take the first emitted value then complete
example = source$.pipe(take(1));
//output: 1
// take the last 2 emitted values
example = source$.pipe(takeLast(2));
//output: 4,5

Tuesday, 20 April 2021

Time ago pipe : A really simple, lightweight Angular pipe for converting a date string into a time ago

import {
  Pipe,
  PipeTransform,
  NgZone,
  ChangeDetectorRef,
  OnDestroy
from "@angular/core";
import * as moment from 'moment-timezone';
@Pipe({
  name: "timeAgo",
  pure: false
})
export class TimeAgoPipe implements PipeTransformOnDestroy {
  private timernumber;
  constructor(
    private changeDetectorRefChangeDetectorRef,
    private ngZoneNgZone
  ) {}
  transform(valuestringtimeZonestring = '') {
    this.removeTimer();
    let d = new Date(value);
    let now = new Date();
    let sign = Math.sign((now.getTime() - d.getTime()) / 1000);
    let seconds = Math.round(Math.abs((now.getTime() - d.getTime()) / 1000));
    
    if (timeZone) {
      d = moment(value).tz(timeZone);
      now = moment().tz(timeZone);
      sign = Math.sign((now.valueOf() - d.valueOf()) / 1000);
      seconds = Math.round(Math.abs((now.valueOf() - d.valueOf()) / 1000));
    }

    let timeToUpdate = Number.isNaN(seconds) ? 1000 : this.getSecondsUntilUpdate(seconds) * 1000;
    
    this.timer = this.ngZone.runOutsideAngular(() => {
      if (typeof window !== "undefined") {
        return window.setTimeout(() => {
          this.ngZone.run(() => this.changeDetectorRef.markForCheck());
        }, timeToUpdate);
      }
      return null;
    });
    
    let minutes = Math.round(Math.abs(seconds / 60));
    let hours = Math.round(Math.abs(minutes / 60));
    let days = Math.round(Math.abs(hours / 24));
    let months = Math.round(Math.abs(days / 30.416));
    let years = Math.round(Math.abs(days / 365));
    let future = false;
    
    if (sign === -1) {
      future = true;
    }
    
    if (Number.isNaN(seconds)) {
      return "";
    } else if (seconds <= 45) {
      return "just now";
    } else if (seconds <= 90) {
      return (future) ? "Starts in 1 min" : "a minute ago";
    } else if (minutes <= 45) {
      return (future) ? "Starts in " + minutes + " mins" : minutes + " minutes ago";
    } else if (minutes <= 90) {
      return (future) ? "Starts in 1 hour" : "an hour ago";
    } else if (hours <= 22) {
      return (future) ? "Starts in " + hours + " hours" : hours + " hours ago";
    } else if (hours <= 36) {
      return (future) ? "Starts in 1 day" : "a day ago";
    } else if (days <= 25) {
      return (future) ? "Starts in " + days + " days" : days + " days ago";
    } else if (days <= 45) {
      return (future) ? "Starts in 1 month" : "a month ago";
    } else if (days <= 345) {
      return (future) ? "Starts in " + months + " months" : months + " months ago";
    } else if (days <= 545) {
      return (future) ? "a year later" : "a year ago";
    } else {
      // (days > 545)
      return (future) ? years + " years later" :   years + " years ago";
    }
  }
  ngOnDestroy(): void {
    this.removeTimer();
  }
  private removeTimer() {
    if (this.timer) {
      window.clearTimeout(this.timer);
      this.timer = null;
    }
  }
  private getSecondsUntilUpdate(secondsnumber) {
    let min = 60;
    let hr = min * 60;
    let day = hr * 24;
    if (seconds < min) {
      // less than 1 min, update every 2 secs
      return 2;
    } else if (seconds < hr) {
      // less than an hour, update every 30 secs
      return 30;
    } else if (seconds < day) {
      // less then a day, update every 5 mins
      return 300;
    } else {
      // update every hour
      return 3600;
    }
  }
}

Wednesday, 3 June 2020

RXJS Handle multiple API requests in Angular using merge Map and fork Join to avoid nested subscriptions

In this article, I will introduce two techniques to handle multiple requests in Angular by using mergeMap and forkJoin.
Contents:
  1. Problem
  2. subscribe
  3. mergeMap
  4. forkJoin
  5. Combine mergeMap and forkJoin
  6. Performance comparison of subscribe vs mergeMap and forkJoin
Problem
In the real world, we frequently call more than one API in our web applications. When you enter a page, you often make multiple requests to retrieve all the required data, and the results of some API requests are required for subsequent calls.
When we make multiple requests, it’s important to handle them effectively to maintain fast performance for your users while also writing good code.
  1. Call the API to authenticate and retrieve user information
  2. Based on the user information, we call one API to get all posts created by the user.
  3. Based on the user information, we call one API to get all the albums created by the user.
subscribe is a common way to handle requests in Angular, but there are more effective methods. We will first solve our problem using subscribe and then improve on it using mergeMap and forkJoin

Subscribe

Using this technique is quite simple. First, we call one API to get user info, and then we call the other two APIs. We do this in a nested subscription so we can use the results from the first API call.
nested subscription for multiple request
This technique is fine for 2 or 3 requests, but it is hard to read for more requests as your app grows. We would be required to create a lot of nested subscription. That is why we will use RxJS to handle multiple requests.

MergeMap

This operator is best used when you wish to flatten an inner observable but want to manually control the number of inner subscriptions.
So when do we apply mergeMap?
When we need data from the first API request to make requests to the second API.
mergeMap case study
Look at the source code above, we can see that second API needs the user ID from the first API to get data.
Note:
  1. flatMap is an alias for mergeMap.
  2. mergeMap maintains multiple active inner subscriptions at once, so it’s possible to create a memory leak through long-lived inner subscriptions.

ForkJoin

This operator is best used when you have a group of observables and only care about the final emitted value of each. It means that forkJoin allows us to group multiple observables and execute them in parallel, then return only one observable.
When do we apply forkJoin?
We use it when API requests are independent. It means that they do not depend on each other to complete and can execute in parallel.
forkJoin case study

Combine mergeMap and forkJoin

In the real world, there are multiple API requests that depend on the result of another request. So let’s see how can we handle that case by using mergeMap and forkJoin.
Here is a sample to solve our problem:
combine mergeMap and forkJoin
By using these functions, we avoided nested subscriptions and can split code into many small methods.
You have to replace userId inside mergeMap by user that return from map above.

RxJS terms: Observables, operators, observer, subscription, subject, and scheduler.


In reactive programming, there are 6 popular terms are:
  • observable
  • operators
  • observer
  • subscription
  • subject
  • scheduler

In this story, I will introduce these terms as fundamental knowledge to start learning reactive programming for a newbie.

Observable

Observable is a stream or source of data that can arrive over time.
In an Angular application, you can create a data source from 2 main cases:
HTTP request:
import { ajax } from "rxjs/ajax";const URL = "https://jsonplaceholder.typicode.com/posts";
const posts$ = ajax.getJSON(URL);
UI events:
import { fromEvent } from 'rxjs';const searchBtn = document.getElementById('search-user');
const searchUser$ = fromEvent(searchBtn, 'click');
An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers.
Note: observables are unicast. It means that each subscribed Observer owns an independent execution of the Observable.

Subscription

A Subscription is an object that represents a disposable resource, usually the execution of an Observable.
It will be created whenever you  an observable. The function that you supply is an Observer where we make the decision how to react to each event that happens.
An Observer has 3 main functions: , and .
import { fromEvent } from 'rxjs';const searchBtn = document.getElementById('search-user');
const searchUser$ = fromEvent(searchBtn, 'click');const subscription = searchUser$.subscribe(event => console.log(event));
Important Note: Once calling , it will create a subscription and at the end of the component lifecycle, you need to remove it to avoid a memory leak.
Observer uses default scheduler in reactive programming which is executed before observable start emitting any notification.
You can customize your own scheduler and apply it to your observable. You can think that schedulers are centralized dispatchers to control concurrency, allowing us to coordinate when computation happens on e.g.  or  or others. This will make your web application have better performance.
To be more simple, scheduler is a mechanism to “schedule” an action to happen in the future.

Operators

Operators offer a way to manipulate values from a source, returning an observable of the transformed values.
To be simple, you can think operators are function.
There is 2 kind of operators:
  • Pipeable Operators
    A Pipeable Operator is a function that takes an Observable as its input and returns another Observable. It is a pure operation: the previous Observable stays unmodified.You can think that this kind of operators are a place that we can apply functional programming, just like pipe function or compose function.
  • Creation Operators
    Creation Operator is an operator that can be called as standalone functions to create a new Observable.You can think that this kind of operator will help you in creating a new observable.
import { of } from 'rxjs';
import { map } from 'rxjs/operators';const dataSource = of(1, 2, 3, 4, 5); // of(...) is creation operators which create new observableconst subscription = dataSource
.pipe(map(value => value + 1))
.subscribe(value => console.log(value)); // map(...) is Pipeable operator