Topic
RxJs is a powerful tool to play with streams, make operations on them. This demo project is an example of sending one form value to two different APIs (with a toggle button deciding which one should be used).
Setup project
I created an angular project by typing (answered CLI question by default):
ng new store-data-example
I love using Angular Material, so in the root directory, I typed (default answers):
ng add @angular/material
Now my project is ready to code.
Service
To be able to make HTTP calls I added HttpClientModule to imports inside AppModule file (from @angular/common/http).
I made a service to make HTTP calls to two endpoints separatly.
The first one is firing to Httpbin endpoint.
The second one is firing to JSONPlaceholder.
Here is full code of service:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class EndpointService {
constructor(private http: HttpClient) {}
sendBin(data: any): Observable<boolean> {
console.info('HTTP BIN sent');
return this.http.post('https://httpbin.org/post', { data }).pipe(
map(_ => true),
catchError(error => of(false))
);
}
sendJsonPlaceholder(data: any): Observable<boolean> {
console.info('JSON Placeholder sent');
return this.http.post('https://jsonplaceholder.typicode.com/posts', { data }).pipe(
map(_ => true),
catchError(error => of(false))
);
}
}
I simplified it, and all success I am treating as positives and all errors as negatives.
- map operator converts one value to another
-
catchError operator is listening for any errors on stream
Of course, I could use the
errorobject insidecatchErrorto check response status. Both methods return observable with the boolean results depending on the HTTP response.
The form
To be able to use some Angular Material elements and angular forms I imported some modules into AppModule:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { MatButtonModule } from '@angular/material/button';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
MatButtonModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatSnackBarModule,
FormsModule,
MatSlideToggleModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
My form is really simple, with two fields:
form = this.formBuilder.group({
firstName: [null],
lastName: [null],
});
In constructor, I added three classes:
private endpointService: EndpointService,
private formBuilder: FormBuilder,
private snackBar: MatSnackBar
-
endpointServicemy HTTP service -
formBuilderAngular reactive form builder -
snackBarAngular Material Snack Bar
My component has also two other properties:
-
subjectRxJS Subject which allows me to pass data to the service -
enpointToggletrue for JSONPlaceholder, false for HttpBin
I am sending form value to the subject by using the next method:
onSubmit(): void {
this.subject.next(this.form.value);
}
Depending on the enpointToggle value I am sending data to one of the endpoints:
this.subject
.asObservable()
.pipe(
switchMap(value => iif(
() => this.enpointToggle,
this.endpointService.sendJsonPlaceholder(value),
this.endpointService.sendBin(value),
))
)
.subscribe(result => this.snackBar.open(result ? `Send to ${this.endpoint}` : 'Error', '', { duration: 3000 }))
-
asObservableallows me to use the subject as observable and treat it as a stream -
pipemethod is for working with data from the stream -
switchMapoperator for switching from one observable (with form value) to another (HTTP call) -
iiffunction takes three arguments (and returns observable):- first takes a function which result decides which observable should be subscribed
- second takes observable which is subscribed when the first function returns true
- third takes observable which is subscribed when the first function returns false
IMPORTANT!
iifevaluates both expressions in any case, but service will fire HTTP call only in one of them (depending on boolean return)
-
subscribecallsopenmethod onsnackBarto show notification
The HTML code is also simple:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field appearance="fill">
<mat-label>First name</mat-label>
<input matInput formControlName="firstName">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Last name</mat-label>
<input matInput formControlName="lastName">
</mat-form-field>
<button type="submit" mat-flat-button color="primary">Send</button>
</form>
<mat-slide-toggle [(ngModel)]="enpointToggle">HttpBin / JSONPlaceholder</mat-slide-toggle>
Link to repo.
Top comments (1)
This is very useful to me, thank you for sharing! 👍🏻