De­vel­op­ing an An­gu­lar Ap­pli­ca­tion Us­ing Re­dux for State Man­age­ment

This ar­ti­cle ex­plains the de­vel­op­ment of an An­gu­lar ap­pli­ca­tion, us­ing Re­dux for state man­age­ment, in­ter­fac­ing with the Ex­press frame­work for data and us­ing An­gu­lar CLI as the front-end build pipe­line.

OpenSource For You - - Devel­op­ers -

An­gu­lar is a JavaScript frame­work for build­ing user in­ter­faces and can be used to build sin­gle page ap­pli­ca­tions. There is a large amount of state that needs to be man­aged by sin­gle page ap­pli­ca­tions. The state in­cludes server re­sponses, cached and lo­cally cre­ated data, and var­i­ous UI states. Re­dux is a pop­u­lar frame­work for man­ag­ing the state. The data dis­played by the client side UI can be pro­vided from a back-end server. Ex­press is a com­monly used Web frame­work based on Node.js. This ar­ti­cle ex­plains the de­vel­op­ment of an An­gu­lar ap­pli­ca­tion us­ing Re­dux for state man­age­ment and in­ter­fac­ing with the Ex­press frame­work for data along with us­ing An­gu­lar CLI for set­ting up the de­vel­op­ment en­vi­ron­ment. An­gu­lar CLI seam­lessly han­dles the front-end build pipe­line, en­abling us to fo­cus on writ­ing the ap­pli­ca­tion logic.

To get an un­der­stand­ing of this ar­ti­cle, read­ers should have a knowl­edge of the ba­sics of An­gu­lar, Re­dux, Ex­press.js, and Node.js con­cepts.

Con­tacts ap­pli­ca­tion

The con­cepts in de­vel­op­ing an ap­pli­ca­tion us­ing An­gu­lar, Re­dux and Ex­press will be il­lus­trated through a Con­tacts ap­pli­ca­tion. We will keep the ap­pli­ca­tion sim­ple, as the main ob­jec­tive is to il­lus­trate the var­i­ous con­cepts in an easy-to-un­der­stand man­ner. Us­ing the app, we will be able to view the Con­tacts list, add a new con­tact and delete a con­tact. We will also use mid­dle­ware called re­dux-thunk to per­form the ac­tion asyn­chronously.

Set­ting up the de­vel­op­ment en­vi­ron­ment

Let’s de­velop the app us­ing Win­dows as the de­vel­op­ment ma­chine, as fol­lows:

1. In­stall Node.js from https://nodejs.org. Down­load the 32-bit or 64-bit bi­nary de­pend­ing upon the ar­chi­tec­ture of the sys­tem.

2. In­stall An­gu­lar CLI, us­ing which we will start build­ing the Con­tacts ap­pli­ca­tion:

npm in­stall –g @an­gu­lar/cli

3. Gen­er­ate the ba­sic code tem­plate, which we will mod­ify to add the fea­tures re­quired for the Con­tacts ap­pli­ca­tion.

ng new con­tacts

This will cre­ate a di­rec­tory called con­tacts in the cur­rent folder. In­side that folder, you will see an ini­tial project struc­ture gen­er­ated, and the re­quired de­pen­den­cies are au­to­mat­i­cally in­stalled.

4. Change to the con­tacts di­rec­tory. Check if the setup is proper, by run­ning the ba­sic ap­pli­ca­tion, as fol­lows:

npm start

This will start the ap­pli­ca­tion in de­vel­op­ment mode and ‘Wel­come to the app’ will be dis­played.

5. In­stall the mod­ules for Re­dux in­te­gra­tion, as fol­lows:

npm in­stall –S re­dux npm in­stall –S re­dux-thunk npm in­stall –S @an­gu­lar-re­dux/store

6. Cre­ate the di­rec­tory, client, un­der con­tacts and move the en­tire folder struc­ture from con­tacts to client so that the client folder con­tains the code for the client-side An­gu­lar app. 7. To proxy API re­quests dur­ing de­vel­op­ment and avoid CORS is­sues, mod­ify the ‘start’ script in pack­age.json present in the client folder with the fol­low­ing com­mand:

“start”: “ng serve ­­proxy­con­fig proxy.conf.json”

8. Cre­ate the file proxy.conf.json un­der the client folder with the fol­low­ing con­tent: { “/api”: {

“tar­get”: “http://lo­cal­host:3000”, “se­cure”: false

} }

9. Cre­ate a di­rec­tory server un­der con­tacts. From this folder, ex­e­cute npm init, and in­stall the ex­press and body­parser mod­ule to serve the per­sis­tent data re­quired for the Con­tacts ap­pli­ca­tion.

npm init –y npm in­stall –S ex­press npm in­stall –S body-parser

10. Add the ‘start’ set­ting un­der ‘scripts’ in pack­age.json present in the server folder with the fol­low­ing com­mand so that we can run the Ex­press server ap­pli­ca­tion us­ing ‘npm start’.

“start”: “node server.js”

Now we can start work­ing on adding the fea­tures re­quired for the Con­tacts ap­pli­ca­tion. While ex­plain­ing the im­ple­men­ta­tion, the main code snip­pets have been pro­vided. For the com­plete code, please re­fer to the GitHub repos­i­tory at https://github.com/srini-wip/an­gu­lar-re­dux-con­tacts.git.

Serv­ing the data re­quired by the Con­tacts app us­ing Ex­press

Let’s now im­ple­ment the code to serve the data re­quired by the Con­tacts ap­pli­ca­tion us­ing the Ex­press Web frame­work.

Cre­ate a file server.js un­der the folder con­tacts. To pro­vide the list of con­tacts, im­ple­ment the API end­point ‘/ api/con­tacts’ us­ing the GET method. This will read the JSON file, con­tacts.json, for the list of avail­able con­tacts and send the re­sponse as JSON data.

app.get(‘/api/con­tacts’, func­tion(req, res) {

// read from JSON file and send the re­sponse as JSON data });

To add a con­tact to the con­tact list, im­ple­ment the API end­point ‘api/con­tacts’ us­ing the POST method. To re­trieve the data sent by the browser, use body-parser mid­dle­ware, which in­serts the re­quired data in the re­quest ob­ject, from which we can eas­ily ex­tract and save it to the JSON file.

app.post(‘/api/con­tacts’, func­tion(req, res) {

// ex­tract the data and save to JSON file });

To delete a con­tact from the con­tact list, im­ple­ment the API end­point ‘api/con­tacts/:id’ us­ing the DELETE method. This will delete the con­tact and up­date the JSON file.

app.delete(‘/api/con­tacts/:id’, func­tion(req, res) {

// delete the con­tact and up­date the JSON file });

Im­ple­ment­ing the user in­ter­face us­ing An­gu­lar and Re­dux

Let’s now im­ple­ment the set of fea­tures on the client side us­ing An­gu­lar and Re­dux.

When gen­er­at­ing the new project, An­gu­lar CLI would have cre­ated a file app.mod­ule.ts, which is the root mod­ule and con­tains the dec­la­ra­tion of li­braries and com­po­nents.

Con­fig­ur­ing the store

In the con­struc­tor of the class Ap­pMo­d­ule, we need to con­fig­ure the store. We need to pro­vide the rootRe­ducer, ini­tial state (if any), mid­dle­wares and then a store en­hancer. Since we will be us­ing the re­dux-thunk mid­dle­wares to im­ple­ment asyn­chro­nous ac­tions, the third pa­ram­e­ter will be the thunk mid­dle­wares. We need to in­clude the nec­es­sary im­ports for Re­dux, re­dux-thunk and also spec­ify the NgRe­duxMo­d­ule in the im­ports sec­tion of the @NgMo­d­ule dec­o­ra­tor for Ap­pMo­d­ule.

con­struc­tor (ngRe­dux: NgRe­dux<any>) {

ngRe­dux.con­fig­ureS­tore(rootRe­ducer, {}, [ thunk ], []); }

Im­ple­ment­ing the re­duc­ers

Cre­ate a file con­tac­tRe­ducer.ts un­der the folder re­duc­ers. When the store dis­patches an ac­tion, it passes to rootRe­ducer, the state main­tained in the store and the ac­tion. While cre­at­ing the rootRe­ducer, we would have com­bined all the in­di­vid­ual re­duc­ers of the ap­pli­ca­tion us­ing the com­bineRe­duc­ers API. In our cur­rent ap­pli­ca­tion, we have only one re­ducer, i.e., for con­tacts, which we will be cre­at­ing shortly. Although we have only one re­ducer, it will be use­ful to use the com­bineRe­duc­ers API, as we can ex­tend it to add more re­duc­ers as our ap­pli­ca­tion ex­pands in the fu­ture.

const rootRe­ducer = com­bineRe­duc­ers({ con­tacts });

The rootRe­ducer will pass the ac­tion and the re­spec­tive state to each re­ducer. Let’s then im­ple­ment the re­ducer func­tion con­tac­tRe­ducer ac­cept­ing two pa­ram­e­ters — state and ac­tion. The re­ducer is sup­posed to han­dle the ac­tion it is in­ter­ested in and re­turn the new state. An im­por­tant point to un­der­stand is that the re­ducer is a pure func­tion, and it should not mu­tate the pa­ram­e­ters and re­turn a new state in an im­mutable way, us­ing only the val­ues passed in the pa­ram­e­ters. In the con­tac­tRe­ducer, we will han­dle the ac­tions – suc­cess­ful load­ing of con­tacts, suc­cess­ful ad­di­tion of a con­tact and suc­cess­ful dele­tion of a con­tact.

const con­tacts = (state, ac­tion) => { switch (ac­tion.type) { case ‘LOAD­ED_­CON­TACTS’: // Re­turn con­tacts

case ‘ADDED_­CON­TACT’:

// Add new con­tact to state and re­turn

case ‘DELET­ED_­CON­TACT’:

// Delete con­tact from state and re­turn

de­fault:

// Re­turn state passed as pa­ram­e­ter } }

Im­ple­ment­ing the ac­tions

Cre­ate a file ac­tionTypes.ts un­der the folder ac­tions to store all the ac­tion names as a con­stant. Hav­ing the ac­tion types as a con­stant will help in bet­ter main­te­nance of code rather than us­ing them di­rectly as a string.

Next, cre­ate a file con­tac­tAc­tions.ts, in which we will im­ple­ment var­i­ous ac­tions re­lated to Con­tacts. We will im­ple­ment the ac­tions—load­ing of con­tacts, adding a con­tact and delet­ing a con­tact. As we will be com­mu­ni­cat­ing us­ing REST API calls with a server in the back­end, we will be mak­ing asyn­chro­nous calls and hence will use the re­dux­thunk mid­dle­ware to per­form asyn­chro­nous dis­patch. The thunk func­tion will in­voke the Con­tact Ser­vice meth­ods (dis­cussed be­low), which will com­mu­ni­cate with the server and fetch or add/mod­ify the data based on the ac­tion. The Con­tact Ser­vice makes REST API calls to the Ex­press server, per­forms the nec­es­sary task and re­turns ei­ther suc­cess or er­ror. If suc­cess is re­turned, then the cor­re­spond­ing suc­cess ac­tion–load, add or delete—will be dis­patched, after which the re­ducer code dis­cussed above will get ex­e­cuted.

ex­port class Con­tac­tAc­tions { load­Con­tacts() { re­turn dis­patch => {

// In­voke Con­tact Ser­vice method to load con­tacts and process

// suc­cess­ful re­sult or er­ror

}

} ad­dCon­tact(con­tact) { re­turn dis­patch => {

// In­voke Con­tact Ser­vice method to add con­tact and process

// suc­cess­ful re­sult or er­ror

} } deleteCon­tact(id) { re­turn dis­patch => {

// In­voke Con­tact Ser­vice method to delete con­tact and process

// suc­cess­ful re­sult or er­ror

}

}

}

Im­ple­ment­ing the Con­tact Ser­vice

To cre­ate the Con­tact Ser­vice, run the An­gu­lar CLI gen­er­ate ser­vice com­mand from the folder ‘app’.

ng gen­er­ate ser­vice con­tact

The above com­mand will gen­er­ate the stan­dard ser­vice blue­print con­tain­ing the ser­vice type­script file and a ser­vice test spec­i­fi­ca­tion file.

Let’s im­ple­ment the ser­vice meth­ods – getCon­tacts, ad­dCon­tact and deleteCon­tact. To im­ple­ment these meth­ods, is­sue REST API calls to the Ex­press server us­ing the An­gu­lar HTTP mod­ule.

@In­jectable() ex­port class Con­tac­tSer­vice { getCon­tacts() {

// In­voke REST end­point ‘/api/con­tacts’ us­ing http.get }

ad­dCon­tact(con­tact) {

// In­voke REST end­point ‘/api/con­tacts’ us­ing http.post } deleteCon­tact(id) {

// In­voke REST end­point ‘/api/con­tacts/<id>’ us­ing http. delete

}

}

Im­ple­ment­ing the UI com­po­nents

Let’s now use An­gu­lar to im­ple­ment the UI com­po­nents. Also, let’s use the @an­gu­lar-re­dux/store mod­ule to in­ter­face An­gu­lar with Re­dux.

When gen­er­at­ing the new project, An­gu­lar CLI would have cre­ated a com­po­nent called ‘App’. Mod­ify the HTML tem­plate app.com­po­nent.html to ren­der the Con­tacts com­po­nent (<app-con­tacts>), which we will be im­ple­ment­ing shortly.

To cre­ate the Con­tacts and the Con­tact Form com­po­nent, run the An­gu­lar CLI gen­er­ate com­po­nent com­mand from the folder ‘app’.

ng gen­er­ate com­po­nent con­tacts ng gen­er­ate com­po­nent con­tact­form

The above com­mands will cre­ate the fold­ers, con­tacts and con­tact­form un­der app, and gen­er­ate the stan­dard com­po­nent blue­print con­tain­ing the HTML tem­plate file, CSS file, com­po­nent type­script file, and a com­po­nent test spec­i­fi­ca­tion file.

The Con­tact­sCom­po­nent needs to ac­cess the Re­dux store to dis­patch ac­tions and to re­ceive the state changes. For this, we in­ject NgRe­dux into the con­struc­tor of Con­tact­sCom­po­nent, and us­ing the NgRe­dux ob­ject, we can dis­patch ac­tions.

con­struc­tor(pri­vate ngRe­dux: NgRe­dux<any>, pri­vate _ con­tac­tAc­tions: Con­tac­tAc­tions) { }

In the ngOnInit life cy­cle method of Con­tact­sCom­po­nent, we can then dis­patch the ac­tion to load the con­tacts, which is im­ple­mented in con­tac­tAc­tions.ts as ex­plained in the ‘Im­ple­ment­ing the ac­tions’ sec­tion above.

ngOnInit() { this.ngRe­dux.dis­patch<any>(this._con­tac­tAc­tions. load­Con­tacts());

}

Ad­di­tion­ally, in the deleteCon­tact method of Con­tact­sCom­po­nent, dis­patch the ac­tion to delete the se­lected con­tact.

deleteCon­tact(id: any) { this.ngRe­dux.dis­patch<any>(this._con­tac­tAc­tions. deleteCon­tact(id));

}

To re­ceive the state changes from the store, we de­clare the @se­lect dec­o­ra­tor, and spec­ify the por­tion of the state that the Con­tact­sCom­po­nent is in­ter­ested in, which is ‘con­tacts’.

@se­lect() con­tacts:any;

When­ever there is a state change, the Re­dux store will pro­vide the lat­est state to the Con­tact­sCom­po­nent, and due to data bind­ing, the changes will re­flect in the view.

Next, we will im­ple­ment the Con­tact­for­mCom­po­nent. This com­po­nent also needs ac­cess to the store to dis­patch an ac­tion, and hence we need to in­ject NgRe­dux in the con­struc­tor of Con­tact­for­mCom­po­nent.

con­struc­tor(pri­vate ngRe­dux: NgRe­dux<any>, pri­vate _ con­tac­tAc­tions: Con­tac­tAc­tions) { }

The Con­tact­for­mCom­po­nent will have the nec­es­sary HTML tem­plate to ac­cept the user in­put to cre­ate the con­tact. When the user sub­mits the con­tact in­for­ma­tion to be added, we will dis­patch the ac­tion to add the con­tact. onSub­mit(for­mValue: any) { this.ngRe­dux.dis­patch<any>(this._con­tac­tAc­tions. ad­dCon­tact(newCon­tact));

}

Up­dat­ing styles.css to in­clude Boot­strap

In the styles.css file, un­der the src folder, add the link to the Boot­strap CSS for a pre­sentable UI:

@im­port “https://cd­njs.cloud­flare.com/ajax/libs/twit­ter­boot­strap/3.3.7/css/boot­strap.min.css”

Run­ning the ap­pli­ca­tion

1. Go to the Node.js com­mand prompt.

2. Change di­rec­tory to con­tacts/server and start the server:

npm start

3. Change the di­rec­tory to con­tacts/client. Start the An­gu­lar Con­tacts app:

npm start

This will start the ap­pli­ca­tion and dis­play the Con­tacts in­for­ma­tion. We can now add and delete con­tacts.

Newspapers in English

Newspapers from India

© PressReader. All rights reserved.