Angular being an extensible framework is widely used for developing high responsive front end modern web applications.
These modern web applications consist of several complex workflows on the server side. To provide access of these workflows to client applications, we need REST APIs so that client applications can access these APIs over HTTP.
Client applications developed using Front-End frameworks like Angular can make use of HttpClient object from @angular/common/http to access these REST APIs and then using Observable object from RxJs Angular, can store and manage data received from the REST APIs in the front-end application.
But what if the Angular application wants to makes call to REST APIs in Parallel?
This could be tricky but an important requirement from the customer. In the following figure I have explained the scenario
Figure 1: Scenario of accessing multiple REST APIs from Angular Client Application.
If we need to find a solution on this then it is important for us to to understand the UX requirements for the Angular application to define a strategy for making parallel calls.
Advantages of making external Parallel calls are that, the UI blocking time can be reduced. As shown in the above figure, an end-user wants to show all states, cities and employees on the page when the page is loaded by making three different calls to REST APIs. In this case, if calls are made sequentially then an asynchronous HTTP operations may delay the overall loading time of the page. The parallel HTTP calls from Angular applications can be beneficial. The question here is how to make parallel HTTP calls and receive data?
As mentioned earlier, Angular makes use of RxJs Library and its Observable class to store response received from the HTTP calls.
We will be implementing an application using REST APIs created using ASP.NET core. The ASP.NET Core application is created using Visual Studio 2019 and .NET Core 2.2.
(Note: For simplicity, I have not used the database for fetching data instead, I have created collection classes for State, Cities and Employees).
Step 1: Open Visual Studio 2019 and create an ASP.NET Core WEB API Application. Name the application as DataServices. In the project, add a Models folder and in this folder, add three class files named as State.cs, City.cs and Employee.cs. Add the following code in these files:
Listing 1: State.cs
Listing 2: City.cs
Listing 3: Employee.cs
In the project, add a new folder and name it as DataStore. In this folder, add a new class file and name it as Databases.cs and add the following code in it
Listing 4: Database classes
The above code contains hard-coded data in it. (Note: You can use database access code here using EF Core)
Step 2: In the Controllers folder add three Empty API Controllers. These controllers will access Database classes to returns States, Cities and Employees. The code of the controller class is provided as in the following code
Listing 5: StateController.cs
Listing 6: CityController.cs
Listing 7: EmployeeController.cs
Step 3: Since an Angular application from a different domain will access the REST APIs, we need to configure CORS policy for the APIs created in the Step 2. Lets modify the ConfigureServices() method and Configure() method of the Startup class as shown in the following listing
Listing 8: The Startup.cs class
Step 4: In the app sub-folder of the src folder, add a new file and name it as app.model.ts. Add the following three classes in this file.
Listing 9: Model classes of name State, City and Department
These classes will be used for Databinding on HTML element
Step 5: Add a new file of name app.service.ts. In this file, lets add an Angular service. This service will contain methods to make Http GET calls to REST APIs created in earlier Steps.
Listing 10: The Angular Service
The above code contains MultiCallService class. This class is injected with HttpClient class. There are three methods to make HTTP GET requests for State, City and Employee REST APIs. All these methods returns Observable objects.
Step 6: Modify the app.component.html file as shown in Listing 11:
Listing 11: The Html for the component
Step 7: In the AppModule, import HttpClientModule so that HttpClient injection in the Angular service can be executed.
Listing 12: AppModule class
Step 8: Modify the app.component.ts file. The AppComponent class is injected with the MultiCallService. This class implements the OnInit interface and its ngOnInit() method. The AppComponent class imports forkJoin() method to make parallel calls to the REST APIs. Modify the following code in the AppComponent class.
Listing 13: AppComponent class
The ngOnInit() method makes calls to getStates(), getCities() and getEmployees() methods from service class. All these methods returns Observable objects. These Observable objects array is passed to forkJoin() method as input parameter. This method returns an Observable object. This method waits for all Observables to complete and emit results and stores the result in states, cities and employees array defined in AppComponent class.
Run the REST API application from Visual Studio and Angular application using the following command from the command prompt.
npm run start
Open browser and enter the http://localhost:4200 address in the address bar. This will show the following result
Figure 4: The Result loaded with all data
The above results shows all records fetched in Parallel calls. We can see these parallel calls from the browser developer tools as shown in the following figure:
Figure 5: Parallel calls on the browser
Conclusion: The forkJoin() method from RxJs is quite useful to make parallel HTTP Calls from Front-End applications created in frameworks like Angular.
These modern web applications consist of several complex workflows on the server side. To provide access of these workflows to client applications, we need REST APIs so that client applications can access these APIs over HTTP.
Client applications developed using Front-End frameworks like Angular can make use of HttpClient object from @angular/common/http to access these REST APIs and then using Observable object from RxJs Angular, can store and manage data received from the REST APIs in the front-end application.
But what if the Angular application wants to makes call to REST APIs in Parallel?
This could be tricky but an important requirement from the customer. In the following figure I have explained the scenario
If we need to find a solution on this then it is important for us to to understand the UX requirements for the Angular application to define a strategy for making parallel calls.
Advantages of making external Parallel calls are that, the UI blocking time can be reduced. As shown in the above figure, an end-user wants to show all states, cities and employees on the page when the page is loaded by making three different calls to REST APIs. In this case, if calls are made sequentially then an asynchronous HTTP operations may delay the overall loading time of the page. The parallel HTTP calls from Angular applications can be beneficial. The question here is how to make parallel HTTP calls and receive data?
As mentioned earlier, Angular makes use of RxJs Library and its Observable class to store response received from the HTTP calls.
Observable class
The Observable is the most basic building class of RxJs library. This call represents set of values of any types. The Observable class needs the subscriber so that values stored in the Observable can be provided to the subscriber. The following figure shows the behavior of the Observable class
Figure 2: The Observable class
forkJoin() Function
The forkJoin() function accepts an array of Observable and returns Observable. This returned Observable emits an array of values that is returned from the REST APIs. The forkJoin() functions waits for observable to complete and emits data when all observables are complete. In the following figure forkJoin() is explained.
Figure 3: The forkJoin() function
Creating REST APIs using ASP.NET Core
We will be implementing an application using REST APIs created using ASP.NET core. The ASP.NET Core application is created using Visual Studio 2019 and .NET Core 2.2.
(Note: For simplicity, I have not used the database for fetching data instead, I have created collection classes for State, Cities and Employees).
Step 1: Open Visual Studio 2019 and create an ASP.NET Core WEB API Application. Name the application as DataServices. In the project, add a Models folder and in this folder, add three class files named as State.cs, City.cs and Employee.cs. Add the following code in these files:
namespace DataServices.Models { public class State { public int StateId { get; set; } public string StateName { get; set; } } }
Listing 1: State.cs
public class City { public int CityId { get; set; } public string CityName { get; set; } public int StateId { get; set; } }
Listing 2: City.cs
public class Employee { public int EmpId { get; set; } public string EmpName { get; set; } public int CityId { get; set; } }
Listing 3: Employee.cs
In the project, add a new folder and name it as DataStore. In this folder, add a new class file and name it as Databases.cs and add the following code in it
public class StateList : List{ public StateList() { Add(new State() { StateId = 1, StateName = "Maharashtra" }); Add(new State() { StateId = 2, StateName = "Gujarat" }); Add(new State() { StateId = 3, StateName = "Rajasthan" }); Add(new State() { StateId = 4, StateName = "Karnataka" }); } } public class CityList : List { public CityList() { Add(new City() { CityId = 101, CityName = "Pune", StateId = 1 }); Add(new City() { CityId = 102, CityName = "Ahmedabad", StateId = 1 }); Add(new City() { CityId = 103, CityName = "Jaypur", StateId = 1 }); Add(new City() { CityId = 104, CityName = "Bengalure", StateId = 1 }); Add(new City() { CityId = 105, CityName = "Mulbai", StateId = 1 }); Add(new City() { CityId = 106, CityName = "Surat", StateId = 2 }); Add(new City() { CityId = 107, CityName = "Udaipur", StateId = 3 }); Add(new City() { CityId = 108, CityName = "Hubli", StateId = 4 }); Add(new City() { CityId = 109, CityName = "Nashik", StateId = 1 }); Add(new City() { CityId = 110, CityName = "Baroda", StateId = 2 }); Add(new City() { CityId = 111, CityName = "Jodhpur", StateId = 3 }); Add(new City() { CityId = 112, CityName = "Belgaum", StateId = 4 }); Add(new City() { CityId = 113, CityName = "Nagpur", StateId = 1 }); Add(new City() { CityId = 114, CityName = "Rajkot", StateId = 2 }); Add(new City() { CityId = 115, CityName = "Bikaner", StateId = 3 }); Add(new City() { CityId = 116, CityName = "Mysuru", StateId = 4 }); } } public class EmployeeList : List { public EmployeeList() { Add(new Employee() { EmpId = 10001, EmpName = "Akash", CityId = 101 }); Add(new Employee() { EmpId = 10002, EmpName = "Kumar", CityId = 102 }); Add(new Employee() { EmpId = 10003, EmpName = "Sachin", CityId = 103 }); Add(new Employee() { EmpId = 10004, EmpName = "Rahul", CityId = 104 }); Add(new Employee() { EmpId = 10005, EmpName = "Mandar", CityId = 101 }); Add(new Employee() { EmpId = 10006, EmpName = "Kiran", CityId = 102 }); Add(new Employee() { EmpId = 10007, EmpName = "Avinash", CityId = 103 }); Add(new Employee() { EmpId = 10008, EmpName = "Nishant", CityId = 104 }); Add(new Employee() { EmpId = 10009, EmpName = "Vaibhav", CityId = 101 }); Add(new Employee() { EmpId = 10010, EmpName = "Mukesk", CityId = 102 }); Add(new Employee() { EmpId = 10011, EmpName = "Vivek", CityId = 103 }); Add(new Employee() { EmpId = 10012, EmpName = "Sandeep", CityId = 104 }); Add(new Employee() { EmpId = 10013, EmpName = "Satish", CityId = 101 }); Add(new Employee() { EmpId = 10014, EmpName = "Vinay", CityId = 102 }); Add(new Employee() { EmpId = 10015, EmpName = "Sanjay", CityId = 103 }); Add(new Employee() { EmpId = 10016, EmpName = "Sharad", CityId = 104 }); Add(new Employee() { EmpId = 10017, EmpName = "Vijay", CityId = 101 }); Add(new Employee() { EmpId = 10018, EmpName = "Vilas", CityId = 102 }); Add(new Employee() { EmpId = 10019, EmpName = "Abhay", CityId = 103 }); Add(new Employee() { EmpId = 10020, EmpName = "Keshav", CityId = 104 }); Add(new Employee() { EmpId = 10021, EmpName = "Anil", CityId = 101 }); Add(new Employee() { EmpId = 10022, EmpName = "Abhay", CityId = 102 }); Add(new Employee() { EmpId = 10023, EmpName = "Jaywant", CityId = 103 }); Add(new Employee() { EmpId = 10024, EmpName = "Shyam", CityId = 104 }); Add(new Employee() { EmpId = 10025, EmpName = "Anil", CityId = 101 }); Add(new Employee() { EmpId = 10026, EmpName = "Suhas", CityId = 102 }); Add(new Employee() { EmpId = 10027, EmpName = "Kaustubh", CityId = 103 }); Add(new Employee() { EmpId = 10028, EmpName = "Naineesh", CityId = 104 }); Add(new Employee() { EmpId = 10029, EmpName = "Arav", CityId = 101 }); Add(new Employee() { EmpId = 10030, EmpName = "Sujay", CityId = 102 }); Add(new Employee() { EmpId = 10031, EmpName = "Krushna", CityId = 103 }); Add(new Employee() { EmpId = 10032, EmpName = "Tejas", CityId = 104 }); Add(new Employee() { EmpId = 10033, EmpName = "Rahul", CityId = 101 }); Add(new Employee() { EmpId = 10034, EmpName = "Sameer", CityId = 102 }); Add(new Employee() { EmpId = 10035, EmpName = "Piyush", CityId = 103 }); Add(new Employee() { EmpId = 10036, EmpName = "Abhishek", CityId = 104 }); Add(new Employee() { EmpId = 10037, EmpName = "Prashant", CityId = 101 }); Add(new Employee() { EmpId = 10038, EmpName = "Ashish", CityId = 102 }); Add(new Employee() { EmpId = 10039, EmpName = "Harshad", CityId = 103 }); Add(new Employee() { EmpId = 10040, EmpName = "Shreeniwas", CityId = 104 }); Add(new Employee() { EmpId = 10041, EmpName = "Pankaj", CityId = 101 }); Add(new Employee() { EmpId = 10042, EmpName = "Abhijit", CityId = 102 }); Add(new Employee() { EmpId = 10043, EmpName = "Ashwin", CityId = 103 }); Add(new Employee() { EmpId = 10044, EmpName = "Vaibhav", CityId = 104 }); Add(new Employee() { EmpId = 10045, EmpName = "Prashant", CityId = 101 }); Add(new Employee() { EmpId = 10046, EmpName = "Vinit", CityId = 102 }); Add(new Employee() { EmpId = 10047, EmpName = "Mahesh", CityId = 103 }); Add(new Employee() { EmpId = 10048, EmpName = "Rahul", CityId = 104 }); } }
Listing 4: Database classes
The above code contains hard-coded data in it. (Note: You can use database access code here using EF Core)
Step 2: In the Controllers folder add three Empty API Controllers. These controllers will access Database classes to returns States, Cities and Employees. The code of the controller class is provided as in the following code
[Route("api/[controller]")] [ApiController] public class StateController : ControllerBase { public IActionResult Get() { var states = new StateList(); return Ok(states) ; } }
Listing 5: StateController.cs
[Route("api/[controller]")] [ApiController] public class CityController : ControllerBase { public IActionResult Get() { var cities = new CityList(); return Ok(cities) ; } }
Listing 6: CityController.cs
[Route("api/[controller]")] [ApiController] public class EmployeeController : ControllerBase { public IActionResult Get() { var emps = new EmployeeList(); return Ok(emps) ; } }
Listing 7: EmployeeController.cs
Step 3: Since an Angular application from a different domain will access the REST APIs, we need to configure CORS policy for the APIs created in the Step 2. Lets modify the ConfigureServices() method and Configure() method of the Startup class as shown in the following listing
public void ConfigureServices(IServiceCollection services) { services.AddCors(policy=> { policy.AddPolicy("corspolicy", rules=> { rules.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver()).SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseCors("corspolicy"); app.UseMvc(); }
Listing 8: The Startup.cs class
Creating Angular Front-End Application
Lets create an Angular application. We will be creating this application using Angular CLI. Create a new project of name MultiHttpCalls using Angular CLI. This will create a folder of name MultiHttpCalls. Open this folder in Visual Studio Code.Step 4: In the app sub-folder of the src folder, add a new file and name it as app.model.ts. Add the following three classes in this file.
export class State { constructor( public StateId: number, public StateName: string ) { } } export class City { constructor( public CityId: number, public CityName: string, public StateId: number ) { } } export class Employee { constructor( public EmpId: number, public EmpName: string, public CityId: string ) { } }
Listing 9: Model classes of name State, City and Department
These classes will be used for Databinding on HTML element
Step 5: Add a new file of name app.service.ts. In this file, lets add an Angular service. This service will contain methods to make Http GET calls to REST APIs created in earlier Steps.
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { State, City, Employee } from './app.models'; @Injectable({ providedIn: 'root' }) export class MultiCallService { private url: string; constructor(private http: HttpClient) { this.url = 'http://localhost:10156/api'; } getStates(): Observable<State[]>{ let states: Observable <City[]> {= null; states = this.http.get (`${this.url}/State`); return states; } getCities(): Observable
<Employee[]> { let cities: Observable = null; cities = this.http.get (`${this.url}/City`); return cities; } getEmployees(): Observable
let employees: Observable = null; employees = this.http.get (`${this.url}/Employee`); return employees; } }
Listing 10: The Angular Service
The above code contains MultiCallService class. This class is injected with HttpClient class. There are three methods to make HTTP GET requests for State, City and Employee REST APIs. All these methods returns Observable objects.
Step 6: Modify the app.component.html file as shown in Listing 11:
<div class="container-12"> <table class="table table-bordered table-striped"> <tr> <td> <select class="form-control"> <option value="">Select State</option> <option *ngFor="let s of states" value="{{s.StateId}}">{{s.StateName}}</option> </select> </td> <td> <select class="form-control"> <option value="">Select City</option> <option *ngFor="let c of cities" value="{{c.CityId}}">{{c.CityName}}</option> </select> </td> </tr> </table> <hr> <div class="container-12"> <table class="table table-bordered table-striped"> <thead> <tr> <td>EmpId</td> <td>EmpName</td> <td>CityId</td> </tr> </thead> <tbody> <tr *ngFor="let e of employees"> <td>{{e.EmpId}}</td> <td>{{e.EmpName}}</td> <td>{{e.CityId}}</td> </tr> </tbody> </table> </div> </div>
Listing 11: The Html for the component
Step 7: In the AppModule, import HttpClientModule so that HttpClient injection in the Angular service can be executed.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Listing 12: AppModule class
Step 8: Modify the app.component.ts file. The AppComponent class is injected with the MultiCallService. This class implements the OnInit interface and its ngOnInit() method. The AppComponent class imports forkJoin() method to make parallel calls to the REST APIs. Modify the following code in the AppComponent class.
import { Component, OnInit } from '@angular/core'; import { MultiCallService } from './app.service'; import { State, Employee, City } from './app.models'; import { forkJoin } from 'rxjs'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { states: Array; cities: Array ; employees: Array ; constructor(private serv: MultiCallService) { this.states = new Array (); this.cities = new Array (); this.employees = new Array (); } ngOnInit(): void { const statePromise = this.serv.getStates(); const citiesPromise = this.serv.getCities(); const employeesPromise = this.serv.getEmployees(); // forkJoin() to make parallel calls forkJoin([statePromise, citiesPromise, employeesPromise]).subscribe(responses => { this.states = responses[0]; this.cities = responses[1]; this.employees = responses[2]; }); } }
Listing 13: AppComponent class
The ngOnInit() method makes calls to getStates(), getCities() and getEmployees() methods from service class. All these methods returns Observable objects. These Observable objects array is passed to forkJoin() method as input parameter. This method returns an Observable object. This method waits for all Observables to complete and emit results and stores the result in states, cities and employees array defined in AppComponent class.
Run the REST API application from Visual Studio and Angular application using the following command from the command prompt.
npm run start
Open browser and enter the http://localhost:4200 address in the address bar. This will show the following result
Figure 4: The Result loaded with all data
The above results shows all records fetched in Parallel calls. We can see these parallel calls from the browser developer tools as shown in the following figure:
Figure 5: Parallel calls on the browser
Conclusion: The forkJoin() method from RxJs is quite useful to make parallel HTTP Calls from Front-End applications created in frameworks like Angular.
No comments:
Post a Comment