Showing posts with label rxjs. Show all posts
Showing posts with label rxjs. Show all posts

6/6/19

When You Should Use Subject

Subject - Who are you?

Yes, lot of us (angular people) have heard about subjects. Yes, it is something that acts like observable (you can subscribe to it):


const subject = new Subject();
...
subject.subscribe(x => console.log(x));

And simultaneously you also can call "next" method, like in observer:

 ...
 const button = document.querySelector('button')
 button.addEventListener("click", () => subject.next('click')); 
 ...

Ok, great, but... when it is good practice to use this Subject thing? you may ask...

Lets look on what Angular guys are Doing

Everybody who starts with "get started" section of angular - knows about "heroes tour" tutorial.
Quick search shows the use of "Subject" in the HeroSearchComponent.
Here you have a pretty common scenario: a search input which have to trigger a search request and populate with response results a list below the input...
So in "input" event handler you expect to see something like this:


  search(term: string): void {
    this.heroService.searchHeroes(term).subscribe(results => this.heroes = results);
  }

Or, when displaying the data using "Async" pipe, something like:

/*    
   <li *ngFor="let hero of heroes$ | async" >
      <a routerLink="/detail/{{hero.id}}">
        {{hero.name}}
      </a>
    </li>
*/
  search(term: string): void {
    this.heroes$ = this.heroService.searchHeroes(term);
  }

The problem with this code (although it will work) - is that the subscription to "searchHeroes" method is done each time when search terms changes, (not mention is already done at "ngOnInit" lifehook of the component):

 ngOnInit(): void {
    this.heroes$ = this.searchTerms.pipe( // pipe is the equivalent of "subscribe"
      // wait 300ms after each keystroke before considering the term
      debounceTime(300),

      // ignore new term if same as previous term
      distinctUntilChanged(),

      // switch to new search observable each time the term changes
      switchMap((term: string) => this.heroService.searchHeroes(term)),
    );
  }

And it is bad to subscribe multiple times, because of memory leaks potential. Instead - by using searchTerms Subject(like Angular guys did), you can call the "next" and trigger the service again, which is much more accurate:

  // Push a search term into the observable stream.
  search(term: string): void {
    this.searchTerms.next(term);
  }
 

2/11/19

Handle nested requests with rxjs

In the previous post i manage to get data using nested requests - by vanilla javascript.
In this post I will try to explain how to do the same thing, but using angular + rxjs library.
Firstly - lets write here the usual angular service which has "getUsers" and "getRepos" methods:

import { HttpClient,  HttpErrorResponse } from '@angular/common/http';
import { Observable} from 'rxjs';
import { from } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class DataService {
  token = 'PUT-YOUR-TOKEN-HERE';
  constructor(private http: HttpClient) { }
  
  getUsers(since=0) : Observable {
    return this.http.get(`https://api.github.com/users?per_page=10&since=${since}&access_token=${this.token}`)
      .pipe(
        catchError(this.handleError('getUsers'))
      ) as Observable;
  }

  getReposCount(user) : Observable {   
    return this.http.get(`https://api.github.com/users/${user}/repos?per_page=10000&access_token=${this.token}`)
      .pipe(
        map(repos => repos.length || 0 ),
        catchError(this.handleError('getReposCount'))
      ) as Observable;
  }
  ...
  ...
}

But how to combine those two requests to work together as nested requests?
After googling around I came up with following code:

  getData(since) {
     return this.getUsers(since)
       .pipe(
         concatMap(users => from(users)),
         mergeMap((user) => this.getReposCount(user.login)
            .pipe(
              map(count => ({login: user.login, avatar: user.avatar_url, count}))
            )         
         ),
         toArray()
       )
  }
What a mess! - What is "form"? What are those "concatMap" and "mergeMap"? and why this "toArray" needed?
I will try to understand myself

Big Picture (as i understood so far)

The big picture of what is need to be done here - is:
* We need to convert the stream which brings as a collection to stream which brings us single user (one by one)
* For each user we need to send the request to get repos count and update the user object
* Stream of single users need to be converted back to stream of arrays


concatMap

according to the docs: Map values to inner observable, subscribe and emit in order (same as mergeMap but preserves the order)

from

according to this article - converts an array to an observable. - need for converting the observable of users array to observable of single users

mergeMap

Map to observable, emit values

why not switchMap?

In many examples, demonstrating how to do some operation on the stream - the "switchMap" method is widely used:


  getQuote(): Observable {
    return Observable.create(observer => observer.next(this.nextId++)).pipe(
      // here
      switchMap((id: number) => this.http.get(`api/quotes/${id}`)),
      map((q: Quote) => q.quote),
      ...
    )
SwitchMap is also maps the observable to different one , however - the main feature of switchMap is that it "kills" the previous observable, thus it cannot fit for our case...

2/13/18

What Is This "Tap" Thing In Rxjs?

In the bank of Jordan river, in the Moab land, Moses wished to explain his testament to Sons of Israel and that`s what is he said:
I'm looking at great angular-ngrx-material-starter repo of great guy named Tom Trajan
And here is the code of "auth.effects" file, where you can clear see the usage of "tap" operator:

  @Effect({ dispatch: false })
  login(): Observable {
    return this.actions$
      .ofType(AuthActionTypes.LOGIN)
      .pipe(
        tap(action => // <-- code="" here="" isauthenticated:="" see="" tap="" the="" thing="" this.localstorageservice.setitem="" true="">
So what is this "tap"(lettable operator) thing doing???

Documentation

When looking into rxjs docs you can see that "tap" is newer version of "do" operator.
Also you can see following sentence: invokes an action upon graceful or exceptional termination of the observable sequence.
That means - if you want something to be done regardless of outcome of observable you can do it with "tap"

Example

All the places in he code which only taking care of storing the payload(and not trying to modify the outcome of observable ) in the localstorage making usage if "tap":

  @Effect({ dispatch: false })
  persistTodos(): Observable {
    return this.actions$
      .ofType(TodosActionTypes.PERSIST)
      .pipe(
        tap((action: ActionTodosPersist) =>
          this.localStorageService.setItem(TODOS_KEY, action.payload.todos)
        )
      );
  }

When observable outcome is somehow modified - the "map" operator comes to the stage:

  @Effect()
  loadPizzas$ = this.actions$.ofType(pizzaActions.LOAD_PIZZAS).pipe(
    switchMap(() => {
      return this.pizzaService
        .getPizzas()
        .pipe(
          map(pizzas => new pizzaActions.LoadPizzasSuccess(pizzas)),
          catchError(error => of(new pizzaActions.LoadPizzasFail(error)))
        );
    })
  );

10/28/17

Rxjs - Discovering Subject and Subject Behavior

In frameworks like angular and react we have this behavior that tracks every change of model (a javascript variable which stores state of some ui component) and immediately projects all the model changes to ui.
lets say we want to mimic this behavior without using any framewok.
For example: we have person object in our application that has a boolean "isMale" field (among other fields).

let person = {
  name:"Holden",
  lname:"Colfield",
  isMale:"true"
}

Lets say we need UI to react to change of this "isMale" field, and change checkbox caption accordingly
here is when rxjs can be useful:
One of the options to achieve the desired behavior with rxjs - is to define person object as Behavior Subject

let bSubject = new Rx.BehaviorSubject(person); 

Behavior subject is an observable sequence which makes you able to notify everyone of its subscribers when some change occurs.
Only thing you do is call "next" method of the subject:
(In our app we will trigger change when somebody cliks on the checkbox)

Rx.Observable.fromEvent(checkBox,'change').subscribe(x=> {
   person.isMale = x.target.checked;
   bSubject.next(person);
})

Now our app changes its ui according to changes of observed "person" object...
see running code

10/17/17

Post About Rxjs

What can i say about rxjs?

Rxjs it is a different way of writing the code
For instance, instead of doing this:


document.querySelector('h1').addEventListener('click',(e)=>{
  alert()
}) 

With Rxjs you can write the same thing using this code:

Rx.Observable.fromEvent(document.querySelector('h1'),'click').subscribe((x)=>{
  alert()
})

So whats the advantage of using rxjs?

One of the advantages - is that you can use plenty of various utils which helps you:
For example if you want you code to respond not more than one events per second you can simply write this code:


Rx.Observable.fromEvent(window,'resize')
.throttleTime(1000)
.subscribe((x)=>{
  document.querySelector('h1').innerText =window.outerHeight
})

Which is nice
link to running code

7/11/16

3 Different Ways To Use ngFor

Introduction

Like ng-repeat directive of angular 1. there is ngFor directive in angular2. it uses for display lists of items.
For example sake - lets take situation we need to display table of site users.
In old angular1 the template should look like this:
angular1 template


    <div>
      <h2>{{name}}</h2>
      <div  ng-if="userList.length===0">No Users Yet</div>
      <table ng-if="userList.length" >
        <tr><th>First Name</th><th>Last Name</th></tr>
        <!-- Look here -->
        <tr ng-repeat="user in userList">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>
    </div>

And here is controller & service code:
angular1 code

   //service 
   app.factory('usersSrv', function($http, $q) {
     function get(){
       var defer = $q.defer();
       $http.get('./users.json').success(function(data){
         defer.resolve(data)
       });
       return defer.promise;
     }
  
     var service = { 
        getUsers: get
     }

     return service;
   });
    //controller:
   $scope.userList = [];
   usersSrv.getUsers().then(function(users){
    $scope.userList = users; 
   })

Code: plunkr


The same scenario could be written in angular2 like this
template of app.component.ts

      <table *ngIf="userList.length">
        <tr>
            <th>First Name</th>
            <th>Last Name</th>
        </tr>
        <!-- Look here -->
        <tr *ngFor="let user of userList">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>

If there are some users in our db - they will be shown in table.
Currently the data taken from static array:

export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:'
    this.userList =  [{firstName:'Vasia', lastName:'kozlov'}]
  }
}

we can see users table and everything is fine.

Code: plunk

Promise

The things starting to be more complicate when users collection is loaded from http request. It is complicate because users data now is asyncronous.
So, the first way to handle this thing is to use good old Prmoise known to us well from angular1.
Unlike angular1 we need to import toPromise operator of rxjs library.
users.servise.ts:


import { Injectable }    from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';//import of "topromise" method from rxjs lib

@Injectable()
export class UserService {
  private url:string = './users.json';
  constructor(private http: Http) {}
  getUsers(): Promise {//method must return promise
    return this.http.get(this.url)
    .toPromise()
    .then(response => response.json());
  }
}

Now in the component code we able to use the "then" method for getting the callback of asyncronous method.
app.component.ts:

import {Component} from '@angular/core';
import {UserService} from './user.servise';
@Component({
  selector: 'my-app',
  providers: [],
  template: `
      ...
      ...
      ...
  `,
  providers: [UserService]
})
export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:'
    userService.getUsers().then(//Here we can use "then"
      users => this.userList=users,
      error => console.log(error)
    );
  }
}

The good side here (for me) is that it very similar to angular1 way.
Code: plunk

Subscribe

The second attitude is to use map method of rxjs library.
users.servise.ts:


import { Injectable }    from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/Rx';

@Injectable()
export class UserService {
  private url:string = './users.json';
  constructor(private http: Http) {}
  getUsers() {
    return this.http.get(this.url).map(response => response.json());
  }
}

This map method returns Rx.Observable object, thus in component code we will need to use subscribe for handle the async data:
app.component.ts:

import {Component} from '@angular/core';
import {UserService} from './user.servise';
@Component({
      ...
      ...
      ...
  providers: [UserService]
})

export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:';
    userService.getUsers().subscribe(
      users => this.userList=users,
      error=>console.log(error)
    );
  }
}

I like this way because it looks more shorter (no need to use conversion to promise like in previous way).
Code: plunk

Using Async Pipe

Unlike in angular1 there is a way in angular2 to pass observable collection directly to ngFor directive.
We can do this using angular2 async pipe.
template of app.component.ts


    <div>
      <h2>{{name}}</h2>
      <div  *ngIf="userList.length===0">No Users Yet</div>
      <table >
        <tr><th>First Name</th><th>Last Name</th></tr>
        <!-- Look here -->
        <tr *ngFor="let user of userList | async">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>
    </div>

Usinp async pipe makes the component code even more shorter: app.component.ts:

import {Component} from '@angular/core';
import {UserService} from './user.servise';
@Component({
      ...
      ...
      ...
  providers: [UserService]
})

export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:';
    //no subscribe needed
    this.userList=userService.getUsers();
  }
}

Little More About Async Pipe

Another cool thing about this pipe is - that it can be used not only in ngFor
For example - if you need to show "No Users Yet" message when no users found in DB, you may use async pipe too:
template of app.component.ts


    <div>
      <h2>{{name}}</h2>
      
      <!-- HERE -->
      <div  *ngIf="(userList | async)?.length===0">No Users Yet</div>
      <table *ngIf="(userList | async)?.length>0">
        <tr><th>First Name</th><th>Last Name</th></tr>
        <tr *ngFor="let user of userList | async">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>
    </div>


Code: plunk

Hope you have fun reading...

Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...