This article explains the development of an Angular application, using Redux for state management, interfacing with the Express framework for data and using Angular CLI as the front-end build pipeline.
Angular is a JavaScript framework for building user interfaces and can be used to build single page applications. There is a large amount of state that needs to be managed by single page applications. The state includes server responses, cached and locally created data, and various UI states. Redux is a popular framework for managing the state. The data displayed by the client side UI can be provided from a back-end server. Express is a commonly used Web framework based on Node.js.
This article explains the development of an Angular application using Redux for state management and interfacing with the Express framework for data along with using Angular CLI for setting up the development environment. Angular CLI seamlessly handles the front-end build pipeline, enabling us to focus on writing the application logic.
To get an understanding of this article, readers should have a knowledge of the basics of Angular, Redux, Express.js, and Node.js concepts.
Contacts application
The concepts in developing an application using Angular, Redux and Express will be illustrated through a Contacts application. We will keep the application simple, as the main objective is to illustrate the various concepts in an easy-to-understand manner. Using the app, we will be able to view the Contacts list, add a new contact and delete a contact. We will also use middleware called redux-thunk to perform the action asynchronously.
Setting up the development environment
Let’s develop the app using Windows as the development machine, as follows:
1. Install Node.js from https://nodejs.org. Download the 32-bit or 64-bit binary depending upon the architecture of the system.
2. Install Angular CLI, using which we will start building the Contacts application:
npm install –g @angular/cli
3. Generate the basic code template, which we will modify to add the features required for the Contacts application.
ng new contacts
This will create a directory called contacts in the current folder. Inside that folder, you will see an initial project structure generated, and the required dependencies are automatically installed.
4. Change to the contacts directory. Check if the setup is proper, by running the basic application, as follows:
npm start
This will start the application in development mode and ‘Welcome to the app’ will be displayed.
5. Install the modules for Redux integration, as follows:
npm install –S redux npm install –S redux-thunk npm install –S @angular-redux/store
6. Create the directory, client, under contacts and move the entire folder structure from contacts to client so that the client folder contains the code for the client-side Angular app.
7. To proxy API requests during development and avoid CORS issues, modify the ‘start’ script in package.json present in the client folder with the following command:
“start”: “ng serve --proxy-config proxy.conf.json”
8. Create the file proxy.conf.json under the client folder with the following content:
{ “/api”: { “target”: “http://localhost:3000”, “secure”: false } }
9. Create a directory server under contacts. From this folder, execute npm init, and install the express and body-parser module to serve the persistent data required for the Contacts application.
npm init –y npm install –S express npm install –S body-parser
10. Add the ‘start’ setting under ‘scripts’ in package.json present in the server folder with the following command so that we can run the Express server application using ‘npm start’.
“start”: “node server.js”
Now we can start working on adding the features required for the Contacts application. While explaining the implementation, the main code snippets have been provided. For the complete code, please refer to the GitHub repository at https://github.com/srini-wip/angular-redux-contacts.git.
Serving the data required by the Contacts app using Express
Let’s now implement the code to serve the data required by the Contacts application using the Express Web framework.
Create a file server.js under the folder contacts. To provide the list of contacts, implement the API endpoint ‘/api/contacts’ using the GET method. This will read the JSON file, contacts.json, for the list of available contacts and send the response as JSON data.
app.get(‘/api/contacts’, function(req, res) { // read from JSON file and send the response as JSON data });
To add a contact to the contact list, implement the API endpoint ‘api/contacts’ using the POST method. To retrieve the data sent by the browser, use body-parser middleware, which inserts the required data in the request object, from which we can easily extract and save it to the JSON file.
app.post(‘/api/contacts’, function(req, res) { // extract the data and save to JSON file });
To delete a contact from the contact list, implement the API endpoint ‘api/contacts/:id’ using the DELETE method. This will delete the contact and update the JSON file.
app.delete(‘/api/contacts/:id’, function(req, res) { // delete the contact and update the JSON file });
Implementing the user interface using Angular and Redux
Let’s now implement the set of features on the client side using Angular and Redux.
When generating the new project, Angular CLI would have created a file app.module.ts, which is the root module and contains the declaration of libraries and components.
Configuring the store
In the constructor of the class AppModule, we need to configure the store. We need to provide the rootReducer, initial state (if any), middlewares and then a store enhancer. Since we will be using the redux-thunk middlewares to implement asynchronous actions, the third parameter will be the thunk middlewares. We need to include the necessary imports for Redux, redux-thunk and also specify the NgReduxModule in the imports section of the @NgModule decorator for AppModule.
constructor (ngRedux: NgRedux<any>) { ngRedux.configureStore(rootReducer, {}, [ thunk ], []); }
Implementing the reducers
Create a file contactReducer.ts under the folder reducers. When the store dispatches an action, it passes to rootReducer, the state maintained in the store and the action. While creating the rootReducer, we would have combined all the individual reducers of the application using the combineReducers API. In our current application, we have only one reducer, i.e., for contacts, which we will be creating shortly. Although we have only one reducer, it will be useful to use the combineReducers API, as we can extend it to add more reducers as our application expands in the future.
const rootReducer = combineReducers({ contacts });
The rootReducer will pass the action and the respective state to each reducer. Let’s then implement the reducer function contactReducer accepting two parameters — state and action. The reducer is supposed to handle the action it is interested in and return the new state. An important point to understand is that the reducer is a pure function, and it should not mutate the parameters and return a new state in an immutable way, using only the values passed in the parameters. In the contactReducer, we will handle the actions – successful loading of contacts, successful addition of a contact and successful deletion of a contact.
const contacts = (state, action) => { switch (action.type) { case ‘LOADED_CONTACTS’: // Return contacts case ‘ADDED_CONTACT’: // Add new contact to state and return case ‘DELETED_CONTACT’: // Delete contact from state and return default: // Return state passed as parameter } }
Implementing the actions
Create a file actionTypes.ts under the folder actions to store all the action names as a constant. Having the action types as a constant will help in better maintenance of code rather than using them directly as a string.
Next, create a file contactActions.ts, in which we will implement various actions related to Contacts. We will implement the actions—loading of contacts, adding a contact and deleting a contact. As we will be communicating using REST API calls with a server in the backend, we will be making asynchronous calls and hence will use the redux-thunk middleware to perform asynchronous dispatch. The thunk function will invoke the Contact Service methods (discussed below), which will communicate with the server and fetch or add/modify the data based on the action. The Contact Service makes REST API calls to the Express server, performs the necessary task and returns either success or error. If success is returned, then the corresponding success action–load, add or delete—will be dispatched, after which the reducer code discussed above will get executed.
export class ContactActions { loadContacts() { return dispatch => { // Invoke Contact Service method to load contacts and process // successful result or error } } addContact(contact) { return dispatch => { // Invoke Contact Service method to add contact and process // successful result or error } } deleteContact(id) { return dispatch => { // Invoke Contact Service method to delete contact and process // successful result or error } } }
Implementing the Contact Service
To create the Contact Service, run the Angular CLI generate service command from the folder ‘app’.
ng generate service contact
The above command will generate the standard service blueprint containing the service typescript file and a service test specification file.
Let’s implement the service methods – getContacts, addContact and deleteContact. To implement these methods, issue REST API calls to the Express server using the Angular HTTP module.
@Injectable() export class ContactService { getContacts() { // Invoke REST endpoint ‘/api/contacts’ using http.get } addContact(contact) { // Invoke REST endpoint ‘/api/contacts’ using http.post } deleteContact(id) { // Invoke REST endpoint ‘/api/contacts/<id>’ using http.delete } }
Implementing the UI components
Let’s now use Angular to implement the UI components. Also, let’s use the @angular-redux/store module to interface Angular with Redux.
When generating the new project, Angular CLI would have created a component called ‘App’. Modify the HTML template app.component.html to render the Contacts component (<app-contacts>), which we will be implementing shortly.
To create the Contacts and the Contact Form component, run the Angular CLI generate component command from the folder ‘app’.
ng generate component contacts ng generate component contactform
The above commands will create the folders, contacts and contactform under app, and generate the standard component blueprint containing the HTML template file, CSS file, component typescript file, and a component test specification file.
The ContactsComponent needs to access the Redux store to dispatch actions and to receive the state changes. For this, we inject NgRedux into the constructor of ContactsComponent, and using the NgRedux object, we can dispatch actions.
constructor(private ngRedux: NgRedux<any>, private _contactActions: ContactActions) { }
In the ngOnInit life cycle method of ContactsComponent, we can then dispatch the action to load the contacts, which is implemented in contactActions.ts as explained in the ‘Implementing the actions’ section above.
ngOnInit() { this.ngRedux.dispatch<any>(this._contactActions.loadContacts()); }
Additionally, in the deleteContact method of ContactsComponent, dispatch the action to delete the selected contact.
deleteContact(id: any) { this.ngRedux.dispatch<any>(this._contactActions.deleteContact(id)); }
To receive the state changes from the store, we declare the @select decorator, and specify the portion of the state that the ContactsComponent is interested in, which is ‘contacts’.
@select() contacts:any;
Whenever there is a state change, the Redux store will provide the latest state to the ContactsComponent, and due to data binding, the changes will reflect in the view.
Next, we will implement the ContactformComponent. This component also needs access to the store to dispatch an action, and hence we need to inject NgRedux in the constructor of ContactformComponent.
constructor(private ngRedux: NgRedux<any>, private _contactActions: ContactActions) { }
The ContactformComponent will have the necessary HTML template to accept the user input to create the contact. When the user submits the contact information to be added, we will dispatch the action to add the contact.
onSubmit(formValue: any) { this.ngRedux.dispatch<any>(this._contactActions.addContact(newContact)); }
Updating styles.css to include Bootstrap
In the styles.css file, under the src folder, add the link to the Bootstrap CSS for a presentable UI:
@import “https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css”
Running the application
1. Go to the Node.js command prompt.
2. Change directory to contacts/server and start the server:
npm start
3. Change the directory to contacts/client. Start the Angular Contacts app:
npm start
This will start the application and display the Contacts information. We can now add and delete contacts.