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
error
object insidecatchError
to 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
-
endpointService
my HTTP service -
formBuilder
Angular reactive form builder -
snackBar
Angular Material Snack Bar
My component has also two other properties:
-
subject
RxJS Subject which allows me to pass data to the service -
enpointToggle
true 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 }))
-
asObservable
allows me to use the subject as observable and treat it as a stream -
pipe
method is for working with data from the stream -
switchMap
operator for switching from one observable (with form value) to another (HTTP call) -
iif
function 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!
iif
evaluates both expressions in any case, but service will fire HTTP call only in one of them (depending on boolean return)
-
subscribe
callsopen
method onsnackBar
to 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! 👍🏻