Building SPA With Angular And Redux

In the current application development era, Single Page Application (SPA) is a great feature to develop modern web based applications. In these SPA applications we tend to move the data level dependency from the server side to browser level as much as possible to improve the application performance. So as much as application level functionality moves to the browser level the amount of the data and way to manage these data is also increased. Modern SPA application frameworks like Angular and React use component based architecture that divides our application into small-small sections. Each component contains its own HTML page, stylesheet and its own state(data). This approach is very efficient because it divides our application into small sections that we can manage very easily and also we can reuse these components in our application.

Angular
In the current application development era, Single Page Application (SPA) is a great feature to develop modern web-based applications. In these SPA applications, we tend to move the data level dependency from the server side to browser level as much as possible to improve the application performance. So as much as the application-level functionality moves to the browser level, the amount of the data and ways to manage the data are also increased. Modern SPA application frameworks like Angular, React use component-based architecture that divides our application into small sections. Each component contains its own HTML page, stylesheet, and its own state (data). This approach is very efficient because it divides our application into small sections that we can manage very easily and also, we can reuse these components into our application.

The component-based architecture is very feasible if the size of our application is not very large. When the size of our application is increased, it becomes very difficult to maintain the state of each component. Let’s focus on some downsides of the component-based architecture.

 

  • Data Passing Approach using Properties

 

In Angular, we use the input properties to pass the data in component tree. Suppose, we have a component in component tree and we want to pass some data to its grand children component; First of all, we have to pass this data to its child component that is parent component of the “grand children” components, then further, the “parent” component passes that data to its own children components (grandchildren) using the “input” property.

Angular

It is clear that in the above process, the parent component doesn’t use this data but if we want to pass the data to the grandchildren components, we must have to use this intermediate component. In a component-based architecture, we have many intermediate components for passing the data but not actually using that data. So, this approach is not very efficient.

 

  • Inflexible Components

 

Using this property of passing the data b/w components, make the components inflexible and we can’t reuse them because we will have to pass several input values, which makes it difficult to understand the assignment of a component.

 

  • Maintenance of data Synchronously

 

If multiple components are using the same states and state changes within one component, then it is necessary to notify all the other components to update their state too. That is very difficult and expensive task.

 

  • State of Application

 

If each component contains its own state, then it becomes very difficult to take a screenshot of the state of whole application because we have divided the state at the component level.

 

  • Redundant Data

 

If we have multiple components and each component contains its own copy of data (state), then there are huge chances that some duplicate data is present in multiple components.

All the above shortcoming can lead an application to inconsistent state application and makes it difficult to manage the state of the whole application. To overcome all these shortcomings, we need a new way, using which, we can manage the state of our application.

Here, Redux comes into the picture.

Redux is a predictable state container for the JavaScript apps. It is an open-source JavaScript library developed to maintain the state of the application. Redux works on the centralized data mechanism that means instead of storing the state at the component level, we store the state in a centralized location and all the components can access this store. Sometimes, the data architecture becomes a complex topic for the application but the advantage of Redux is that it makes the data architecture very easy.

Redux Architecture

Angular

Redux works on following key points.

  1. Maintain the whole application data into a single state and we can access the state from the store of application.
  2. This store doesn't mutate directly.
  3. Action is used to trigger the change in the state of the application.
  4. Action calls the dispatcher.
  5. Dispatcher calls the Reducer that takes the previous state and new data as input and generates a new state and now, all the components use this new state.

Building Blocks of Redux

Before starting the development with Redux, we must have the basic knowledge of the building blocks or working blocks of the Redux architecture. Let’s understand them.

Store

Store is a simple JavaScript object that contains the state of the whole application. We can update the state of the application using the “dispatch(action, payload)” method. Dispatcher updates the previous state o the application and generates a new state every time.

Actions

Actions are payloads of the information that are sent from our application to our store. Actions are the source to send the data to the store using the “Store.dispatch()” method. We can compare the action to events that indicate the reducer what to perform with this new data and the previous state.

Reducers

Action tells the Reducer to what to perform but doesn’t tell how to perform the task. Reducers are the functions that specify how the state will change. Reducer is a pure function; that means, we get the same output with same input in all the conditions and circumstances. Reducer takes the action name and previous state and always returns a new state to contain the data with modification included.

API

APIs are the ways to interact with the external environment. API is used to get the data from server side and also update the data to server side.

I think the above intro is enough to start working with Redux and we will cover the remaining topic later in this article. If you want to read more about Redux, then go to the official website of Redux here.

Create Bug Todo List Application with Angular and Redux

Now, let us start developing the bug todo list application using the Angular 4 and use the Redux to maintain the state of our application. Actually the “bug todo” app will be a demo of the bug management system, where we can add the news bugs, check the status of the bugs and also add the functionality to change the status of the bugs.

To create a new Angular 4 application, we will use the Angular CLI. Open a new command line terminal and run the “ng new Bug-TodoApp” this command create a new Angular 4 template for the project.

Now open this project into any Code Editor here I am using the “Visual Studio Code”.  Move to the root directory of the project and run the “ng serve” command this command build and run our project at 4200 port with live reloading features. If your using the “Visual Studio Code” then you can also use the “integrated Terminal” from View menu to run the commands.

Angular

Now open the given URL into any browser and you will find the below screen.

Angular

Install Redux Packages

After successfully creating the Angular template now we need to install the “redux” packages for our application. So run the “npm install redux @angular-redux/store --save” command to install the required packages. Now you go to the “package.json” file you will find both packages have been added to our project. After installing the Redux package now we create building blocks like Store, Action and Reducer for our application.

Create IBugModel Model

Now we create a model for our bugtodo project. For this we create an Interface with some property. First of all create a “model” folder into “src” directory and add “BugModel.ts” file into this directory. After adding the TypeScript file now paste the following code into this file.

  1. export interface IBugModel{  
  2.     bugId:number;  
  3.     description:string;  
  4.     project:string;  
  5.     priority:string;  
  6.     status:string;  
  7. }  
Angular

Create Store

Add a folder into “Src” directory and named this folder as “store”, now in this folder add a typescript file and named this field as “BugStore.ts”.  After creating this file now paste following code into this file.

  1. import { IBugModel } from "../model/BugModel";  
  2.   
  3. export interface IBugState {  
  4.     bugList:IBugModel[],  
  5.     totalBug:number,  
  6.     unassigned:number,  
  7.     assignedBug:number,  
  8.     pendingBug:number,  
  9.     completed:number,  
  10.     reopenBug:number,  
  11.     bugId:number    
  12.     
  13. }  
  14.   
  15. export const INITIAL_STATE:IBugState={  
  16.     bugList:[],  
  17.     totalBug:0,  
  18.     unassigned:0,  
  19.     assignedBug:0,  
  20.     pendingBug:0,  
  21.     completed:0,  
  22.     reopenBug:0,  
  23.     bugId:0  
  24.     
  25. }  

In the above lines of code we create an “IBugStore” interface  and this interface will work as the store of the our application state .  

The “bugList” property will contain the list of all bug and “unassigned”, “assigned”, “pending”, “completed” and “reopenBug” property indicate the numbers of unassigned, assigned, pending, completed and reopen bugs.

We also create an “INITIAL_STATE” constant of type “IBugState” this variable indicate the initial state of the application whenever our application run the first time. If we are creating  a state for the application then we also need to define the initial state of the application that will load when application runs the first time.

Create Action

As Redux document describes that “Actions” are used to indicate to the reducer what type of task it will perform with the payload. Now we create a file that will define all possible “Action” types. So create an “action” folder into “src” directory, after creating the folder now create a “BugAction.ts” file into this folder and paste the following code into this file.

  1. export class bugTodoAction{    
  2.     public static   Add_NewBug='Add';  
  3.     public static   Assign_Bug='Assign';  
  4.     public static   Reopen_Bug='Reopen';  
  5.     public static   Close_Bug='Close';  
  6.     public static   Pending_Bug='Pending';  
  7.     public static   Remove_All='Remove_All';  
  8.     public static   Open_Model='Open_Model';  
  9.     }  
Angular

Create Reducer

Reducer is the heart of the “Redux” pattern, each reducer function takes two parameters, the first parameter contains the previous state of the application and the second parameter contains the action type and new data payload for the state change. Now  create a “reducer” folder into “src” directory and create a “Reducer.ts” file and paste the following code into this file.

  1. import {bugTodoAction} from "../action/BugAction";  
  2.   
  3. export function rootReducre(state,action){  
  4.     switch(action.type){  
  5.           
  6.         case bugTodoAction.Add_NewBug:  
  7.                 action.todo.bugId=state.bugList.length+1;  
  8.                 action.todo.status="Unassigned";  
  9.   
  10.                 return Object.assign({},state,{  
  11.                     bugList:state.bugList.concat(Object.assign({},action.todo)),  
  12.                     totalBug:state.bugList.length+1,  
  13.                     unassigned:state.unassigned+1,  
  14.                     assignedBug:state.assignedBug,  
  15.                     pendingBug:state.pendingBug,  
  16.                     completed:state.completed,  
  17.                     reopenBug:state.completed  
  18.                 });  
  19.   
  20.         case bugTodoAction.Assign_Bug:  
  21.             var bug=state.bugList.find(x=>x.bugId==action.bugNo);  
  22.             var currentStatus=bug.status;  
  23.             var index =state.bugList.indexOf(bug);  
  24.               
  25.           
  26.             if(bug.status=="Unassigned"){  
  27.                 state.unassigned--;                 
  28.             }  
  29.             else if(bug.status=="Reopen"){  
  30.                 state.reopenBug--;                 
  31.             }  
  32.             else if(bug.status=="Close"){  
  33.                 state.completed--;                 
  34.             }  
  35.             else if(bug.status=="Pending"){  
  36.                 state.pendingBug--;                 
  37.             }  
  38.             if(bug.status!="Assign"){  
  39.                 state.assignedBug++;  
  40.             }  
  41.               
  42.             bug.status=bugTodoAction.Assign_Bug;  
  43.   
  44.             return Object.assign({},state,{  
  45.                 bugList:[  
  46.                     ...state.bugList.slice(0,index),  
  47.                     Object.assign({},bug),  
  48.                     ...state.bugList.slice(index+1)  
  49.                 ]  
  50.             });  
  51.   
  52.          
  53.               
  54.         case bugTodoAction.Close_Bug:  
  55.         var bug=state.bugList.find(x=>x.bugId==action.bugNo);  
  56.         var currentStatus=bug.status;  
  57.         var index =state.bugList.indexOf(bug);  
  58.           
  59.         if(bug.status=='Assign'){  
  60.             state.assignedBug--;                 
  61.         }  
  62.         else if(bug.status=="Unassigned"){  
  63.             state.unassigned--;                 
  64.         }  
  65.         else if(bug.status=="Reopen"){  
  66.             state.reopenBug--;                 
  67.         }  
  68.         else if(bug.status=="Pending"){  
  69.             state.pendingBug--;                 
  70.         }  
  71.           
  72.         if(bug.status!="Close"){  
  73.             state.completed++;  
  74.         }  
  75.           
  76.         bug.status=bugTodoAction.Close_Bug;  
  77.   
  78.         return Object.assign({},state,{  
  79.             bugList:[  
  80.                 ...state.bugList.slice(0,index),  
  81.                 Object.assign({},bug),  
  82.                 ...state.bugList.slice(index+1)  
  83.             ],  
  84.             lastUpdate:new Date()  
  85.         });  
  86.           
  87.         case bugTodoAction.Pending_Bug:  
  88.         var bug=state.bugList.find(x=>x.bugId==action.bugNo);  
  89.         var currentStatus=bug.status;  
  90.         var index =state.bugList.indexOf(bug);  
  91.           
  92.         if(bug.status=='Assign'){  
  93.             state.assignedBug--;                 
  94.         }  
  95.         else if(bug.status=="Unassigned"){  
  96.             state.unassigned--;                 
  97.         }  
  98.         else if(bug.status=="Reopen"){  
  99.             state.reopenBug--;                 
  100.         }  
  101.         else if(bug.status=="Close"){  
  102.             state.completed--;                 
  103.         }  
  104.         if(bug.status!="Pending"){  
  105.             state.pendingBug++;  
  106.         }  
  107.           
  108.         bug.status=bugTodoAction.Pending_Bug;  
  109.   
  110.         return Object.assign({},state,{  
  111.             bugList:[  
  112.                 ...state.bugList.slice(0,index),  
  113.                 Object.assign({},bug),  
  114.                 ...state.bugList.slice(index+1)  
  115.             ],  
  116.             lastUpdate:new Date()  
  117.         });  
  118.   
  119.         case bugTodoAction.Remove_All:  
  120.         return Object.assign({},state,{  
  121.             bugList:[],  
  122.             totalBug:0,  
  123.             unassigned:0,  
  124.             assignedBug:0,  
  125.             pendingBug:0,  
  126.             completed:0,  
  127.             reopenBug:0  
  128.         });  
  129.   
  130.         case bugTodoAction.Reopen_Bug:  
  131.         var bug=state.bugList.find(x=>x.bugId==action.bugNo);  
  132.         var currentStatus=bug.status;  
  133.         var index =state.bugList.indexOf(bug);  
  134.           
  135.         if(bug.status=='Assign'){  
  136.             state.assignedBug--;                 
  137.         }  
  138.         else if(bug.status=="Unassigned"){  
  139.             state.unassigned--;                 
  140.         }  
  141.         else if(bug.status=="Pending"){  
  142.             state.pendingBug--;                 
  143.         }  
  144.         else if(bug.status=="Close"){  
  145.             state.completed--;                 
  146.         }  
  147.         if(bug.status!="Reopen"){  
  148.             state.reopenBug++;  
  149.         }  
  150.         bug.status=bugTodoAction.Reopen_Bug;  
  151.   
  152.         return Object.assign({},state,{  
  153.             bugList:[  
  154.                 ...state.bugList.slice(0,index),  
  155.                 Object.assign({},bug),  
  156.                 ...state.bugList.slice(index+1)  
  157.             ],  
  158.             lastUpdate:new Date()  
  159.         });  
  160.   
  161.         case bugTodoAction.Open_Model:  
  162.             return Object.assign({},state,{  
  163.                 bugId:action.bugId  
  164.             });  
  165.     }      
  166.     return state;  
  167. }  

We create a “rootRedcuer” function that takes the two parameter and using the switch statement we define all the action blocks that are defined into “bugTodoAction.ts” file. I know it is a little bit difficult to understand the above code but don’t worry we will cover all these methods and their functionalities into upcoming part of the article. If your application is small then you can create a single reducer and define all the action into this single reducer function but if application is very large then you can create multiple reducer function and later combine all the reducer function into a single unit. Here we will use a single reducer method.

So far we have configured all the building block(Action, Store, Reducer) of the “Redux” pattern let’s implement this Redux pattern into application.

Open the “app.module.ts” file and replace the code with below lines of code.

  1. import { BrowserModule } from '@angular/platform-browser';  
  2. import { NgModule } from '@angular/core';  
  3. import { AppComponent } from './app.component';  
  4. import {FormsModule} from "@angular/forms";  
  5. import {NgRedux,NgReduxModule} from "@angular-redux/store";  
  6. import { IBugState, INITIAL_STATE } from '../store/BugStore';  
  7. import { rootReducre } from '../reducer/Reducer';  
  8.   
  9. @NgModule({  
  10.   declarations: [  
  11.     AppComponent  
  12.   ],  
  13.   imports: [  
  14.     BrowserModule,  
  15.     FormsModule,  
  16.     NgReduxModule  
  17.   ],  
  18.   providers: [],  
  19.   bootstrap: [AppComponent]  
  20. })  
  21. export class AppModule {  
  22.   constructor(ngRedux:NgRedux<IBugState>){  
  23.     ngRedux.configureStore(rootReducre,INITIAL_STATE);  
  24.   }  
  25.  }  

In the above line of code we import some “modules” and add the “NgReduxModule” into imports array. In constructor function we configure the state for the application using the “NgRedux” and also configure the “reducer” function that will handle all the actions. In “configureStore” function of “ngRedux” class we pass the reducer function name and the initial state of the application.

Define the Layout for application

So far we have configured all the Redux configuration blocks, define the state and reducer function for the state into “AppModule.ts” file, I think all major tasks have been completed, now we only need to define the view of our application and perform the required action.

Add Bootstrap 4 Configuration

We will use the “Bootstrap 4” for design purposes so open the “index.html” file and paste the following links into title section.

  1. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">  
  2.   <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>  
  3.   <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>  
  4.   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>  

If you are new to Bootstrap 4 or want to read more about the Bootstrap 4 then you can go to the official website of the bootstrap, following is the link here.

Add Required Component

Open the command line terminal and run the “ng g c bugTodo” command; this command adds a new component in your project. In this component we will write the code to add a new bug or clear all bug lists. After adding this component now we add an another component, so run the “ng g c bugStatus” command, this command adds a new component and name this component as “bug-status.component.ts”. We will use this component to show the status of the bugs. We also show the list of all bugs that are generated so far. To add another component run the “ng g c bugList” command. This command adds a component as “bug-list.component.ts” in our project. After generating all three components following will be the structure of the project.

Angular

Design Layout of the application

After creating the all the required components now we design the layout of the application.  Open the “app.component.html” file replace the code with following code.

App.component.html

  1. <main role="main" class="container">   
  2.         <div class="row row-offcanvas row-offcanvas-right">   
  3.           <div class="col-12 col-md-9">  
  4.            <app-bug-todo></app-bug-todo>  
  5.            <app-bug-list></app-bug-list>  
  6.           </div>   
  7.           <div class="col-6 col-md-3 sidebar-offcanvas" id="sidebar">  
  8.             <app-bug-status></app-bug-status>         
  9.           </div>         
  10.         </div>  
  11.         <hr>   
  12.       </main>  

In the same way replace the code of all the remaining components,

bug-todo.component.html

  1. <div class="card">  
  2.   <h4 class="card-header">Bug Dashboard</h4>  
  3.   <div class="card-body">  
  4.     <h4 class="card-title">Add new bug</h4>  
  5.   
  6.     <form >  
  7.       <div class="form-row">  
  8.         <div class="col-auto">  
  9.           <input  
  10.            type="text" class="form-control"  
  11.            placeholder="Description" id="description"  
  12.            name="description"  
  13.             />  
  14.         </div>  
  15.         <div class="col-auto">  
  16.           <select  
  17.           type="text" class="form-control"  
  18.           placeholder="Priority" id="reprioritysponsible"  
  19.           name="priority"  
  20.           >  
  21.           <option value="Bike">Bike</option>  
  22.           <option value="Car">Car</option>  
  23.           <option value="Health">Health</option>  
  24.           <option value="Home">Home</option>  
  25.           <option value="ProHealth">ProHealth</option>  
  26.           </select>  
  27.         </div>  
  28.         <div class="col-auto">  
  29.               <select  
  30.                type="text" class="form-control"  
  31.                placeholder="Priority" id="reprioritysponsible"  
  32.                name="priority"  
  33.                >  
  34.                <option value="Low">Low</option>  
  35.                <option value="Medium">Medium</option>  
  36.                <option value="High">High</option>  
  37.                </select>  
  38.         </div>  
  39.         <div class="col-auto">  
  40.           <button type="submit" class="btn btn-info">Add Bug</button>  
  41.         </div>  
  42.       </div>  
  43.     </form>  
  44.     <br/>  
  45.     <a href="#" class="btn btn-danger">Clear List</a>  
  46.   </div>  
  47. </div>  

bug-status.component.html

  1. <ul class="list-group">  
  2.     <li class="list-group-item active">Bug Status</li>  
  3.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  4.       Total Bugs  
  5.       <span class="badge badge-primary badge-pill">14</span>  
  6.     </li>  
  7.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  8.         Assigned Bugs  
  9.       <span class="badge badge-secondary badge-pill">2</span>  
  10.     </li>  
  11.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  12.         Unassigned Bugs  
  13.       <span class="badge badge-primary badge-pill">2</span>  
  14.     </li>  
  15.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  16.         Pending  Bugs  
  17.       <span class="badge badge-warning badge-pill">2</span>  
  18.     </li>  
  19.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  20.       Reopen Bug  
  21.       <span class="badge badge-danger badge-pill">1</span>  
  22.     </li>  
  23.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  24.         Completed Bug  
  25.         <span class="badge badge-success badge-pill">1</span>  
  26.       </li>  
  27.   </ul>  

bug-list.component.html

  1. <h4>Bug List</h4>  
  2. <table class="table table-bordered">  
  3.     <thead>  
  4.       <tr>  
  5.         <th scope="col">BugId</th>  
  6.         <th scope="col">Project</th>  
  7.         <th scope="col">Description</th>  
  8.         <th scope="col">Status</th>  
  9.         <th scope="col">Priority</th>  
  10.       </tr>  
  11.     </thead>  
  12.     <tbody>  
  13.       <tr>  
  14.         <th scope="row">1</th>  
  15.         <td>Health</td>  
  16.         <td>Mark</td>  
  17.         <td>Otto</td>  
  18.         <td>@mdo</td>  
  19.       </tr>  
  20.       <tr>  
  21.         <th scope="row">2</th>  
  22.         <td>Health</td>  
  23.         <td>Mark</td>  
  24.         <td>Otto</td>  
  25.         <td>@TwBootstrap</td>  
  26.       </tr>  
  27.       <tr>  
  28.         <th scope="row">3</th>  
  29.         <td>Health</td>  
  30.         <td>Jacob</td>  
  31.         <td>Thornton</td>  
  32.         <td>@fat</td>  
  33.       </tr>  
  34.       <tr>  
  35.         <th scope="row">4</th>  
  36.         <td>Health</td>  
  37.         <td >Larry the Bird</td>  
  38.         <td>Thornton</td>  
  39.         <td>@twitter</td>  
  40.       </tr>  
  41.     </tbody>  
  42.   </table>  

Now save all the changes and refresh your browser,  after making all the changes the following will be the design of our application.

Angular
If you get the above screen without any error message into console that means  every thing is configured very well. Ok now our Redux structure is ready, design is also ready so let’s use the “Redux” mechanism and bind the components with application state.

Add New Bug

Now we write the program to add a new bug into bug list, so open the “bug-todo.component.ts”  file and paste the following code into this file.

  1. import { Component, OnInit } from '@angular/core';  
  2. import {NgRedux,select} from "@angular-redux/store";  
  3. import {bugTodoAction} from "../../action/BugAction";  
  4. import {IBugState} from "../../store/BugStore";  
  5. import {IBugModel} from "../../model/BugModel";  
  6. @Component({  
  7.   selector: 'app-bug-todo',  
  8.   templateUrl: './bug-todo.component.html',  
  9.   styleUrls: ['./bug-todo.component.css']  
  10. })  
  11. export class BugTodoComponent implements OnInit {  
  12.   
  13.   bugObject:IBugModel={  
  14.     bugId:0,  
  15.     description:"",  
  16.     project:"Car",  
  17.     priority:"Low",  
  18.     status:""  
  19.   }  
  20.   
  21.   constructor(private ngRedux:NgRedux<IBugState>) {  
  22.    }  
  23.   
  24.   ngOnInit() {  
  25.   }  
  26.   
  27.   submitForm=()=>{  
  28.     this.ngRedux.dispatch({type:bugTodoAction.Add_NewBug,todo:this.bugObject});  
  29.   }  
  30.   
  31.   clearList=()=>{  
  32.     this.ngRedux.dispatch({type:bugTodoAction.Remove_All,todo:this.bugObject});  
  33.   }  
  34.   
  35. }  

In above line of code first of all we imported all the required modules into our project like “IBugState”, ”IBugModel” and “bugTodoAction”. We also imported the “ngRedux” and “select” modules.  We created an object “bugObject” of  IBugModel type. We will use this object as model for the form and also pass this object along with action payload to update the state for “submitForm” and “clearForm” method.  We create the “submitForm” method  that will be called when form will submit. In this method we call the “dispatch” method of the Store instance in this method we pass the payload and this payload contains the action type and data for creating a new bug. We also create the “clearList” method, we will use this method to clear the bug list.

bug-todo.component.html

We will use the template driven approach to create the html from so replace the code of this file with the following code.

  1. <div class="card">  
  2.   <h4 class="card-header">Bug Dashboard</h4>  
  3.   <div class="card-body">  
  4.     <h4 class="card-title">Add new bug</h4>  
  5.   
  6.     <form (ngSubmit)="submitForm()" #form="ngForm">  
  7.       <div class="form-row">  
  8.         <div class="col-md-6 col-sm-12">  
  9.           <textarea  
  10.            type="text" class="form-control"  
  11.            rows="3"  
  12.            placeholder="Description" id="description"  
  13.            [(ngModel)]="bugObject.description"  
  14.            name="description"  
  15.             ></textarea>  
  16.   
  17.         </div>  
  18.           
  19.       </div>  
  20.       <br/>  
  21.       <div class="form-row">  
  22.           <div class="col-auto">  
  23.               <select  
  24.               type="text" class="form-control"  
  25.               placeholder="Project" id="project"  
  26.               name="project"  
  27.               [(ngModel)]="bugObject.project"  
  28.               >  
  29.               <option value="Bike">Bike</option>  
  30.               <option value="Car">Car</option>  
  31.               <option value="Health">Health</option>  
  32.               <option value="Home">Home</option>  
  33.               <option value="ProHealth">ProHealth</option>  
  34.               </select>  
  35.             </div>  
  36.             <div class="col-auto">  
  37.                   <select  
  38.                    type="text" class="form-control"  
  39.                    placeholder="Priority" id="priority"  
  40.                    name="priority"  
  41.                    [(ngModel)]="bugObject.priority"  
  42.                    >  
  43.                    <option value="Low">Low</option>  
  44.                    <option value="Medium">Medium</option>  
  45.                    <option value="High">High</option>  
  46.                    </select>  
  47.             </div>  
  48.             <div class="col-auto">  
  49.               <button type="submit" class="btn btn-info">Add Bug</button>  
  50.             </div>  
  51.       </div>  
  52.     </form>  
  53.     <br/>  
  54.     <button type="button" class="btn btn-danger" (click)="clearList()">Clear List</button>  
  55.   </div>  
  56. </div>  

When we click on the “Add Bug” button the “submitForm” method will call, in this method we invoke the “dispatch” method of store. This method calls the reducer that we configured into “app.modulet.ts” class’s constructor function.

Angular

Point to notice that in dispatch method we assigned the action type parameter of “bugTodoAction” so reducer will identify the method type and execute the code that's written for this action type.  In the same way for   “clearList” function we call the reducer function of the store and assign the type parameter  of  “Remove_All” type .

Angular
Angular

The “rootReducer” function takes two parameters; the first parameter define the previous state of the application and second parameter contains the payload of the action.  If action type is the “Add_NewBug” then we take the previous state and add a new bug into “bugList” property and also update the remaining property and return this new state to the application.

Angular

If action type is “Remove_All” then we empty the “bugList” and set all remaining property to 0 and return this new state to the application.

Bug-status.component.ts

In Bug-status component  we will show the status of the bugs,  the property of “IBugStore” contain these required information. So we need to access these property into our component and display into html page.

Angular

Bug-status.component.html

  1. <ul class="list-group">  
  2.     <li class="list-group-item active">Bug Status</li>  
  3.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  4.       Total Bugs  
  5.       <span class="badge badge-primary badge-pill">{{totalBug | async}}</span>  
  6.     </li>  
  7.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  8.         Assigned Bugs  
  9.       <span class="badge badge-secondary badge-pill">{{assignBug | async}}</span>  
  10.     </li>  
  11.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  12.         Unassigned Bugs  
  13.       <span class="badge badge-primary badge-pill">{{unassignedBug | async}}</span>  
  14.     </li>  
  15.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  16.         Pending  Bugs  
  17.       <span class="badge badge-warning badge-pill">{{pendingBug | async}}</span>  
  18.     </li>  
  19.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  20.       Reopen Bug  
  21.       <span class="badge badge-danger badge-pill">{{reopenBug | async}}</span>  
  22.     </li>  
  23.     <li class="list-group-item d-flex justify-content-between align-items-center">  
  24.         Completed Bug  
  25.         <span class="badge badge-success badge-pill">{{completedBug | async}}</span>  
  26.       </li>  
  27.   </ul>  

Bug-status.component.ts

  1. import { Component, OnInit } from '@angular/core';  
  2. import {NgRedux,select} from "@angular-redux/store";  
  3. import {bugTodoAction} from "../../action/BugAction";  
  4. import {IBugState} from "../../store/BugStore";  
  5. import {IBugModel} from "../../model/BugModel";  
  6. import { Observable } from 'rxjs/Observable';  
  7.   
  8. @Component({  
  9.   selector: 'app-bug-status',  
  10.   templateUrl: './bug-status.component.html',  
  11.   styleUrls: ['./bug-status.component.css']  
  12. })  
  13. export class BugStatusComponent implements OnInit {  
  14.   
  15.   @select('totalBug') totalBug;  
  16.   @select('assignedBug') assignBug;  
  17.   @select('unassigned') unassignedBug;  
  18.   @select('pendingBug') pendingBug;  
  19.   @select('reopenBug') reopenBug;  
  20.   @select('completed') completedBug;  
  21.   constructor(private ngRedux:NgRedux<IBugState>) {  
  22.     
  23.    }  
  24.   
  25.   ngOnInit() {  
  26.   }  
  27.   
  28. }  

In this component we only need to access the flag properties of the “IBugStore” state, so we use the “@select”  decorator.  The “@select” decorators are the observable types that bind a  variable to the property of the state.  So “@select('totalBug') totalBug” line of code bind the “totalBug” variable to the “totalBug” property of the “IBugStore”. If the value of state property changes then this variable also changes. In the same way using the “@select” decorator we access the all remaining property of the “IBugStore” state and display into the “.html” page.

Now save all the changes and try to make some entry into form. In starting we don’t  have any bug into “bugList” property so all the bug statuses are zero.

Angular

Now add some entry and see the changes. As you click on “Add Bug” button a new entry will insert into “bugList” and Bug Status flags will update. When you add a new bug, you  will find that “Total Bug” and “Unassigned Bug” flags value are updated.

Angular

Add another bug and you get that “Total Bug” and “Unassigned Bug” status has been updated again.

Angular

Display the Bug List

Up to now we successfully completed the work of adding the new bugs into buglist and displayed the count of all bug status. Our next task will be to show all these added bugs into a list and add a popup in which we can see the all details of a bug and can change the status of the particular bug. We open this popup model on click of the bug id.

 Angular

First of all we need to add an another component in which we will show the details of the bug and add the functionality to change the status of the bug. We will show this component into bootstrap popup model.

Angular

Run “ng g c BugInfo” command into your command line terminal, this command adds a new component and we named this component as “BugInfo”.

After successfully adding the component now paste the following code into “bug-list.component.html” file.

Bug-list.component.html

  1. <h4>Bug List</h4>  
  2. <table class="table table-bordered">  
  3.     <thead>  
  4.       <tr>  
  5.         <th scope="col">BugId</th>  
  6.         <th scope="col">Project</th>  
  7.         <th scope="col">Description</th>  
  8.         <th scope="col">Current Status</th>  
  9.         <th scope="col">Priority</th>  
  10.       </tr>  
  11.     </thead>  
  12.     <tbody>  
  13.       <tr *ngFor="let data of bugList | async">  
  14.         <th scope="row">  
  15.           <a href=""  data-toggle="modal" (click)="openPopupModel(data.bugId)"  
  16.            data-target="#exampleModal"> {{data.bugId}}</a>  
  17.            
  18.         </th>  
  19.         <td >{{data.project}}</td>  
  20.         <td>{{data.description}}</td>  
  21.         <td>{{data.status}}</td>  
  22.         <td>{{data.priority}}</td>  
  23.       </tr>  
  24.     </tbody>  
  25.   </table>  
  26.   
  27.   <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">  
  28.       <div class="modal-dialog" role="document">  
  29.         <div class="modal-content">  
  30.           <div class="modal-header">  
  31.             <h5 class="modal-title" id="exampleModalLabel">Bug Description</h5>  
  32.             <button type="button" class="close" data-dismiss="modal" aria-label="Close">  
  33.               <span aria-hidden="true">×</span>  
  34.             </button>  
  35.           </div>  
  36.           <div class="modal-body">  
  37.            <app-bug-info></app-bug-info>  
  38.           </div>  
  39.           <div class="modal-footer">  
  40.             <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>  
  41.           </div>  
  42.         </div>  
  43.       </div>  
  44.     </div>  

Now open the bug-list.component.ts” file and paste the following code into this file.

Bug-list.component.ts

  1. import { Component, OnInit } from '@angular/core';  
  2. import { NgRedux, select } from '@angular-redux/store';  
  3. import { IBugState } from '../../store/BugStore';  
  4. import { bugTodoAction } from '../../action/BugAction';  
  5.   
  6. @Component({  
  7.   selector: 'app-bug-list',  
  8.   templateUrl: './bug-list.component.html',  
  9.   styleUrls: ['./bug-list.component.css']  
  10. })  
  11. export class BugListComponent implements OnInit {  
  12.   
  13.   @select('bugList') bugList;  
  14.   constructor(private ngRedux:NgRedux<IBugState>) {  
  15.   
  16.    }  
  17.   
  18.   ngOnInit() {  
  19.   }  
  20.   
  21.   openPopupModel=(bugId)=>{  
  22.       this.ngRedux.dispatch({type:bugTodoAction.Open_Model,bugId:bugId})  
  23.   }  
  24. }  

In the above line of code we inject the “NgRedux” dependency into constructor function and access the “bugList” property of our application state using the “@select” decorator, this property contain the list of all bugs. As I earlier explained that “@select” decorator is observable type and allows use access to the property of application state. To get the bug list we need to subscribe this variable into our html page that we will perform using the “async” pipe and show the list. For each bug we also add the functionality to open the popup model. When we click on any bug id a popup model will open and in this popup model we will show the “bugInfo” component.

Angular

If you go through the code of anchor tag you will find that on the click action we perform two tasks. On click action we call the “openPopupModel” method of component and open a popup  model.

Angular

In “openPopupModel” method we execute the “dispatch” method of reducer. In form of payload for the reducer method we pass the “Open_Model” as action type and bugId.

Angular

In reducer function if action type is “Open_Model” then we fetch out the “bugId” from the payload and update the “budId” property of the state. We update this “bugId” property because we needs this “bugId” when we open the “popup” model to access the information about a particular bug and perform the further action for that bug. So we will use this “bugId” property in our “bugInfo” component.

Bug-info.component.ts

  1. import { Component, OnInit } from '@angular/core';  
  2. import { select, NgRedux } from '@angular-redux/store';  
  3. import { IBugState } from '../../store/BugStore';  
  4. import { IBugModel } from '../../model/BugModel';  
  5. import { bugTodoAction } from '../../action/BugAction';  
  6.   
  7. @Component({  
  8.   selector: 'app-bug-info',  
  9.   templateUrl: './bug-info.component.html',  
  10.   styleUrls: ['./bug-info.component.css']  
  11. })  
  12. export class BugInfoComponent implements OnInit {  
  13.   @select('bugId') bugId;  
  14.   @select('bugList') bugList;  
  15.   bugNumber:number;  
  16.   bugInfo:IBugModel;  
  17.   status:string="Assign";  
  18.   constructor(private ngRedux:NgRedux<IBugState>) {  
  19.   
  20.     this.bugId.subscribe(data=>{  
  21.       this.bugList.subscribe(data1=>{  
  22.         this.bugNumber=data;  
  23.         this.bugInfo=data1.filter(x=>x.bugId==data)[0]   
  24.       });  
  25.     });  
  26.       
  27.    }  
  28.   
  29.   ngOnInit() {  
  30.         
  31.   }  
  32.   submit=()=>{  
  33.     this.ngRedux.dispatch({type:this.status,bugNo:this.bugNumber});  
  34.   }  
  35.   ChangeStatus=()=>{  
  36.   
  37.   }  
  38.   
  39. }  

In above line of code we inject the “NgRedux” dependency into constructor function.  We also create two observable “bugId” and “bugList” using the “@select” decorator. In constructor function we subscribe both observable to get the “bugId” and bug data from the “bugList” property of the store.

At the last line of the code we create “submit” method. We call this method from our html page and in this function we pass the new status of bug and “bugId” as payload to the reducer function and at reducer end we update the status of bug that matches this particular “bugId” send in payload.

Bug-info.component.html

  1. <div class="card" style="width:400px">  
  2.     <div class="card-body">  
  3.       <h4 class="card-title">Bug Number: {{(bugNumber)}}</h4>  
  4.       <div class="card-blockquote">  
  5.         <span style="font-weight: bold;">Priority: {{bugInfo?.priority}}</span>  
  6.         <span style="float: right;font-weight: bold;">Status: {{bugInfo?.status}}</span>  
  7.         <br/>  
  8.         <span style="font-weight: bold;">Project: {{bugInfo?.project}}</span>  
  9.       </div>  
  10.       <br/>  
  11.       <p class="card-text">{{bugInfo?.description}}</p>  
  12.       <div>  
  13.           <select  
  14.           type="text" class="form-control"  
  15.           [(ngModel)]="status"  
  16.           (change)="ChangeStatus()"  
  17.           >  
  18.           <option value="Assign">Assign</option>  
  19.           <option value="Pending">Pending</option>  
  20.           <option value="Close">Close</option>  
  21.           <option value="Reopen">Reopen</option>  
  22.           </select>  
  23.       </div>  
  24.       <br/>  
  25.         
  26.       <input type="button" value="Submit" (click)="submit()" class="btn btn-primary"/>  
  27.     </div>  
  28.   </div>  
  29.   <br>  

In the following lines of the code we simply show the current information of the bug and we also create a select list to change the bug status. On submit button click we send the “changed” status code to the reducer and at reducer end we update the status of code. After making all these changes now our project is completely ready to run.

Let’s take a complete scenario of our project.

Angular

In current we have three bugs in our “bugList” and all these bugs go into “unassigned;” now click on any “BugId” here I clicked on bugId 2. When we click on any bugId a popup model we open that which contains the display the bug information and provide the functionality to update the status of the bug.

Angular

Now change the status of bug to “Assign”.

Angular

When you click on “submit” button you will find that status of bug has been changed and count of “unassigned” and “assigned” bug has been changed. When you close the popup model you will find that bug list is also changed.

Angular

Let’s understand the “reducer” function and how it updates the status of bug.

Angular

If the new status type for the bug is “Assigned” then we get that particular bug from the “bugList” and update the status of this bug. We also check the previous status of the bug and reduce the “-1” from the previous status count and update the state. For current bug status we increase the “assigned” counts by 1 for the state properties. For example if previous state of the bug is “unassigned” then we reduce the “unassigned” count by 1 and if new status is “assigned” then update the “assignedBug” count by 1. We performed similar functionality for all other status codes.

Conclusion

When you run this project and go through the various steps of the project like add a new bug, clear the list of bugs and change the status of bug, you will find that “bug-list” component, “bug-status” and “bug-info” components remains in synchronized state. A change in any components reflects the changes into all three components and we are not passing any data b/w all these components. We achieve this functionality because our application state is centralized and all components of the application are using this state. If any components make any change into state then this update also listens to other components and these components can also make their changes.

Final conclusion is that if we are going to create a bigger application in Angular then it will be better to use a state management architecture that can manage the state of our application. This state management technique will definitely increase the performance of our application. But if our application is not large then it is not a good idea to use any state management technique and the implementation of any such kind of functionality can reduce the performance of our application. If you have any ideas regarding this article then you can mention it in the comments section.

You can download this article from the below github repository. 

Thanks for reading this article.