Using AngularJS and Bootstrap for a Front-End Framework

In my previous posts we created a RESTful API for a hotel guest registration application. Our fictional client at the Rayburn House is going to want a functional user interface to enter in their guest information. AngularJS, an open source JavaScript library written and maintained by Google makes it easy to create single page applications, it enforces good structured MVC coding, and eliminates much of the code you would have to write otherwise. We will also use Twitter’s BootStrap CSS framework to add a bit of style to your web site.

Add BootStrap to Your Application

Edit your Maven pom file to tell your application that you want to include Bootstrap in its configuration.

<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>bootstrap</artifactId>
	<version>4.1.3</version>
	<scope>runtime</scope>
</dependency>
Figure 1.

After you restart your webservice, the Bootstrap jar will be found in the maven libraries folder.

STS Import Wizard Settings
Figure 2.

Inside your static folder, create the following index.html file so that we can test out the bootstrap implementation.

<!DOCTYPE html>
<html lang="en">
<head>
<title>Rayburn House Guest Registration</title>
</head>
<body>
   <div class="page-header text-center">
       <h2>Rayburn House Guest Registration</h2>
   </div>
   <link rel="stylesheet" href="/webjars/bootstrap/4.1.3/css/bootstrap.css">
</body>
</html>
Figure 3.

In a browser, navigate to localhost:8080 and see your new web page. It should have the heading centered in Helvetica typeface. Now let’s add to it.

Add AngularJS Library To Your Application

Edit your Maven pom file to tell your application that you want to include AngularJS in its configuration.

<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>angularjs</artifactId>
	<version>1.7.2</version>
	<scope>runtime</scope>
</dependency>
Figure 4.
Let’s Create a View

Because we are creating a single-page application, we want to create html templates for Angular to pull into the index.html. Let’s start off with a view used to register a new guest. Create a new folder called template in the src/main/resources/ folder. We will store our views here as html partial files. This view will contain a form used to enter new guest information. Create an html file in the templates folder called guestRegistration.html.

<div class="container">
<span class="lead">Guest Registration</span>
  <div class="panel panel-default">
    <div class="panel-body">
      <div class="container">
	<form class="form-horizontal" name="guestRegistrationForm">
	  <div class="row">
	    <div class="form-group col-md-12">
	       <label class="col-md-2 control-label" for="firstName">
	         First Name</label>				
	       <div class="col-md-7">
	         <input type="text" ng-model="guest.firstName"
		   id="firstName" class="form-control input-sm"
		   placeholder="Enter Guest's First Name" />
	       </div>
	    </div>
	  </div>
	  <div class="row">
	    <div class="form-group col-md-12">
	      <label class="col-md-2 control-label" for="lastName">
		Last Name</label>					
	      <div class="col-md-7">
		<input type="text" ng-model="guest.lastName"
		  id="lastName" class="form-control input-sm"
		  placeholder="Enter Guest's Last Name" />
	      </div>
	    </div>
	  </div>
	  <div class="row">
	    <div class="form-group col-md-12">
              <label class="col-md-2 control-label" for="address">
		Address</label>
	      <div class="col-md-7">
	        <input type="text" ng-model="guest.address"
		  id="address" class="form-control input-sm"
		  placeholder="Enter Guest's Address" />
              </div>
	    </div>
	  </div>
	  <div class="row">
	    <div class="form-group col-md-12">
	      <label class="col-md-2 control-label" for="city">
		City</label>					
	      <div class="col-md-7">
		<input type="text" ng-model="guest.city"
		  id="city" class="form-control input-sm"
		  placeholder="Enter Guest's City" />
	      </div>
	    </div>
	  </div>
	  <div class="row">
	    <div class="form-group col-md-12">
	      <label class="col-md-2 control-label" for="state">
		State</label>
	      <div class="col-md-7">
                <input type="text" ng-model="guest.state"
		  id="state" class="form-control input-sm"
		  placeholder="Enter Guest's State" />
	      </div>
	    </div>
	  </div>
	  <div class="row">
	    <div class="form-group col-md-12">
	      <label class="col-md-2 control-label" for="zip">
	        Zip Code</label>
	      <div class="col-md-7">
		<input type="text" ng-model="guest.zip"
		  id="zip" class="form-control input-sm"
		  placeholder="Enter Guest's Zip Code" />
	      </div>
	     </div>
           </div>
	   <div class="row">
	     <div class="form-group col-md-12">
               <label class="col-md-2 control-label" for="email">
	         Email Address</label>
               <div class="col-md-7">
	         <input type="text" ng-model="guest.email"
		   id="email" class="form-control input-sm"
		   placeholder="Enter Guest's Email" />
	       </div>
	     </div>
	   </div>
	   <div class="row">
	     <div class="form-group col-md-12">
	       <label class="col-md-2 control-label" for="bungalowNum">
	         Assigned Bungalow</label>					
               <div class="col-md-7">
	         <input type="text" ng-model="guest.bungalowNum"
	           id="bungalowNum" class="form-control input-sm"
	           placeholder="Enter Guest's Bungalow" />
	       </div>
	     </div>
	   </div>
	   <div class="row">
	     <div class="form-actions floatRight">
	       <input type="submit" value="Register Guest"
	         class="btn btn-info btn-sm">			
	     </div>
	   </div>
	 </form>
       </div>
     </div>
   </div>
</div>
Figure 5.

Now we need a button on the index.html page that will be used to route us to our new registration template. We will add a navigation element in the html to put the button in. We also need to an ngView directive to tell AngularJS where to render our view, and finally, we must include the AngularJS library.

<html lang="en">
<head>
   <title>Rayburn House Guest Registration</title>
</head>
<body>

<div class="page-header text-center">
   <h2>Rayburn House Guest Registration</h2>
</div>

<nav class="navbar navbar-expand-lg navbar-light bg-light">
   <div class="btn-group" role="group">
      <a href="#/" class="btn btn-info navbar-btn" role="button">Home</a>
      <a href="#/guestRegistration" class="btn btn-info navbar-btn" role="button">Guest Registration</a>
   </div>
</nav>

<div ng-view></div>

<script src="/webjars/angularjs/1.7.2/angular.js"></script>
<link rel="stylesheet" href="/webjars/bootstrap/4.1.3/css/bootstrap.css">
</body>

</html>
Figure 6.

 

To get the view to render inside the ng-view div, we need set up a route for it using AngularJS’s $routeProvider API. Inside the src/main/resources/js folder, create a file called app.js. This file will be our main entry point into AngularJS. See Figure 7.

var app = angular.module('GuestRegistrationSystem', ['ngRoute', 'ngResource']);

app.config(function($routeProvider) {
	$routeProvider.when('/guestRegistration', {
		templateUrl = '/template/guestRegistration.html'
	});
});

Figure 7.

Now we must go back to the index.html and add two things to allow our new JavaScript to run. First, in the html tag, we must add the name of the JavaScript app that we want to include. This should be the same name assigned in the app variable from app.js.

<html lang="en" ng-app="GuestRegistrationSystem">
...
</html>
Figure 8.

Secondly, we need to include the app.js as a resource in the index.html. Add the following to the list of scripts at the bottom of the file.

<script src="/webjars/angularjs/1.7.2/angular-route.js"></script>
<script src="/webjars/angularjs/1.7.2/angular-resource.js"></script>
<script src="/js/app.js"></script>
Figure 9.

If you restart your application, navigate to localhost:8080/, and press the “Register a Guest” button, your form should display. See figure 10. The home button should clear the screen because a route has not yet been configured for the home screen.

Guest Registration Form
Figure 10.
Add a AngularJS Controller

Now let’s make the “Register Guest” button on the Guest Registration Screen actually do something.  We will need an AngularJS controller to tie our form to our web service.  Inside your src/main/resources/static/js folder, create a JavaScript file called controller.js. Inside your new file, create a controller called “guestRegistrationController” with a submitGuestForm function.

app.controller.('guestRegistrationController', function($scope, $http, $location, route){
	
	$scope.submitGuestForm = function() {
		$http({
			method : "POST",
			url : 'http://localhost:8080/api/guest/',
			data : $scope.guest,
		}).then(function(response) {
			$location.path("/guestRegistration");
			$route.reload();
		}, function (errorResponse) {
			$scope.errorMessage = errorResponse.data.errorMessage;
		});
	}
});
Figure 11.

As you can see, the controller’s submitGuestForm function interacts with our web service. The CRUD operation we want to preform (a POST in this case) and the URL for adding a new guest are configured in the function. In order for the guest registration form to find its controller, we need to add an ng-submit directive to the form tag in our guestRegistration.html. See below:

<form class="form-horizontal" name="guestRegistrationForm" ng-submit="submitGuestForm()">
Figure 12.

Also, in index.html, we need to include the new JavaScript file:

<script src="/js/controller.js"></script>
Figure 13.

Next, assign the new controller to the route in our app.js.

app.config(function($routeProvider, $locationProvider) {
	$locationProvider.hashPrefix('');
	
	$routeProvider.when('/guestRegistration', {
		templateUrl : '/template/guestRegistration.html',
		controller : 'guestRegistrationController'
	});
});
Figure 14.

If you were to restart your application now, you would be able to register a guest, but you wouldn’t know if it was successful without using an http tool like Postman because the application is not doing anything with the error response that is sent back to the client. Since the Rayburn’s won’t be happy with this, let’s fix the situation.  In the guestRegistration.html template, we want to add an ng-if directive to display the error response if one is present. Add a div for the errors just inside the first div of the file:

<div class=”container">
<div class="alert alert-danger" role="alert" ng-if="errorMessage">
{{errorMessage}}
</div>

…

</div>
Figure 15.

Now, going forward, you will see any error that exits in the red area. Notice that it is an error that we created in the <ENTER POST NAME HERE> from my earlier blog.

Error Message
Figure 16.
Add a Second View

While this page will make our clients very happy, the Rayburn’s are going to want to see that the guest was actually added. Currently, if everything goes well, there is no indication of success. Let’s say for demonstration purposes that the clients have requested that when the guest is successfully added, that the application load a list of all the current guests staying in Rayburn House. To do that, we need another html template added to the src/main/resources/static/template directory. Let’s call it guestBook.html:

<div class="container">

	<span class="lead">Guest Book</span>
		<div class="panel panel-default">
			<div class="panel-body">
			</div>
		</div>
	<div class="panel-body">
		<div class="table-responsive">
			<table class="table table-hover table-bordered">
				<thead>
					<tr>
						<th>First Name</th>
						<th>Last Name</th>
						<th>Address</th>
						<th>City</th>
						<th>State</th>
						<th>Zip Code</th>
						<th>Email Address</th>
						<th>Bungalow</th>
					</tr>
				</thead>
				<tbody>
					<tr ng-repeat="guest in guests">
						<td>{{guest.firstName}}</td>
						<td>{{guest.lastName}}</td>
						<td>{{guest.address}}</td>
						<td>{{guest.city}}</td>
						<td>{{guest.state}}</td>
						<td>{{guest.zip}}</td>
						<td>{{guest.email}}</td>
						<td>{{guest.bungalowNum}}</td>		
					</tr>
				</tbody>
			</table>
			
		</div>
	</div>
</div>
Figure 17.

To fill our guestBook template with data, we must create an AngularJS controller for it. Inside controller.js, append the following controller to the existing code:

app.controller('guestBookController', function($scope, $http, $location, $route)
{
						
	$http({
		method : 'GET',
		url : 'http://localhost:8080/api/guest/',
				
	}).then(function(response) {
		$scope.guests = response.data;

	});

});
Figure 18.

This new controller calls our Restful web service and grabs the response data for use. We assign the data to the users variable in our $scope so that we can iterate through it in the browser. But first, we need to hook up a new route so AngularJS knows where you want to send the user. Edit guestRegistrationController in controller.js to make it so the user is routed to the guestBook after a new guest is posted.

app.controller.('guestRegistrationController', function($scope, $http, $location, route){
	
	$scope.submitGuestForm = function() {
		$http({
			method : "POST",
			url : 'http://localhost:8080/api/guest/',
			data : $scope.guest,
		}).then(function(response) {
			$location.path("/guestBook");
			$route.reload();
		}, function (errorResponse) {
			$scope.errorMessage = errorResponse.data.errorMessage;
		});
	}
});
Figure 19.

Also, in our app config in app.js, we need to tell Angular what template to load and what controller to use, when the path is “/guestBook”.

	
	$routeProvider.when('/guestRegistration', {
		templateUrl : '/template/guestRegistration.html',
		controller : 'guestRegistrationController'
        }).when('/guestBook', {
		templateUrl : '/template/guestBook.html',
		controller : 'guestBookController'
	});

Figure 20.

Now, after restarting your web service and registering a guest or two, the Guest Book page should load:

Error Message
Figure 21.

With the above changes, our clients will be able to see who is registered to stay at the Rayburn House. Let’s make it easier on them by adding a button to the navigation bar so that they can go directly to the Guest Book without having to first register a guest. To do so, open index.html. After the Register a Guest anchor tag, add :

<nav class="navbar navbar-expand-lg navbar-light bg-light">
	<div class="btn-group" role="group">
		<a href="#/" class="btn btn-info navbar-btn" role="button">Home</a>
		<a href="#/guestRegistration" class="btn btn-info navbar-btn" role="button">Register a Guest</a>
		<a href="#/guestBook" class="btn btn-info navbar-btn" role="button">Guest Book</a>
       </div>	
</nav>

Figure 22.

Our client is also going to want to delete a guest when they check out. To do that, let’s add a delete button to the guest book table. In guestBook.html, add a new column to your table:

<table class="table table-hover table-bordered">
	<thead>
		<tr>
	        	<th>First Name</th>
		        <th>Last Name</th>
			<th>Address</th>
			<th>City</th>
			<th>State</th>
			<th>Zip Code</th>
			<th>Email Address</th>
			<th>Bungalow</th>
			<th>Delete</th>
		</tr>
	</thead>
	<tbody>
	        <tr ng-repeat="guest in guests">
			<td>{{guest.firstName}}</td>
			<td>{{guest.lastName}}</td>
			<td>{{guest.address}}</td>
			<td>{{guest.city}}</td>
			<td>{{guest.state}}</td>
			<td>{{guest.zip}}</td>
			<td>{{guest.email}}</td>
			<td>{{guest.bungalowNum}}</td>		
			<td><button type="button" ng-click="deleteGuest(guest.id)"
				class="btn btn-danger custom-width">
				<span class="glyphicon glyphicon-remove"></span>
		         		Delete
			    </button></td>
		</tr>
	</tbody>
</table>
Figure 23.

 

Restarting your web server and navigating to the Guest Book will add the delete button to the table. See Figure 24:

Delete Button
Figure 24.

In the code in Figure 23, In the above code, our ng-click directive is setting deleteGuest in the $scope. We need to modify our guestBookController in controller.js so that our controller knows to call our DELETE method on our RESTful web service. See below:

app.controller('guestBookController', function($scope, $http, $location, $route)
{
						
	$http({
		method : 'GET',
		url : 'http://localhost:8080/api/guest/',
				
	}).then(function(response) {
		$scope.guests = response.data;

	});
			
	$scope.deleteGuest = function(guestId) {
		$http({
			method : 'DELETE',
			url : 'http://localhost:8080/api/guest/' + guestId
		}).then(function(response) {
			$location.path("/guestBook");
			$route.reload();
		});
	}

});
Figure 25.

Thanks to the above controller code, the user will be routed back to the guestbook page after the CRUD operation is completed. They will then see that the guest has been deleted.

Let’s add another button to guestBook for updating guests. If Rayburn House staff types in a guest’s registration incorrectly, they are going to want to be able to update the information. Follow the steps in Figure 23, only adding an Edit button this time. See the required button tag in figure 26 bellow.

<button type="button" ng-click="deleteGuest(guest.id)"	class="btn btn-danger custom-width">
   <span class="glyphicon glyphicon-remove"></span>
   Delete
</button>
Figure 26.

Now we need to update the guestBookController in controller.js to handle our new $scope item. Add the following:

			
		$scope.editGuest = function(guestId) {
			$location.path("/updateGuest/" + guestId);
		}

Figure 27.

A few things need to happen before the above edit button will function. First, lets make an html template for AngularJS to route users to when they click on the edit button. In src/main/resources/static/template, create the following updateGuest.html. It is almost identical to our guestRegistration.html, but has a different form name and form action.

<div class="container">

<div class="alert alert-danger" role="alert" ng-if="errorMessage">
{{errorMessage}}
</div>
<span class="lead">Update Guest</span>
  <div class="panel panel-default">
     <div class="panel-body">
	<div class="container">
		<form class="form-horizontal" name="guestUpdateForm" ng-submit="submitGuestForm()">
	        	<div class="row">
				<div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="firstName">
						First Name
					</label>				
					<div class="col-md-7">
		                		<input type="text" ng-model="guest.firstName"
							id="firstName" class="form-control input-sm"
							placeholder="Enter Guest's First Name" />
					</div>
				</div>
			</div>
			<div class="row">
				<div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="lastName">
						Last Name
					</label>					
				        <div class="col-md-7">
					        <input type="text" ng-model="guest.lastName"
						        id="lastName" class="form-control input-sm"
						        placeholder="Enter Guest's Last Name" />
				        </div>
			         </div>
			</div>
			<div class="row">
				<div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="address">
						Address
					</label>
					<div class="col-md-7">
          					<input type="text" ng-model="guest.address"
							id="address" class="form-control input-sm"
							placeholder="Enter Guest's Address" />
	         			</div>
				</div>
			</div>
			<div class="row">
				<div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="city">
						City
					</label>					
		        		<div class="col-md-7">
			                	<input type="text" ng-model="guest.city"
				                 	id="city" class="form-control input-sm"
					                placeholder="Enter Guest's City" />
			                </div>
			         </div>
			</div>
			<div class="row">
			         <div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="state">
						State
					</label>
					<div class="col-md-7">
						<input type="text" ng-model="guest.state"
							id="state" class="form-control input-sm"
							placeholder="Enter Guest's State" />
					</div>
				</div>
			</div>
			<div class="row">
				<div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="zip">
						Zip Code
					</label>
                                        <div class="col-md-7">
						<input type="text" ng-model="guest.zip"
							id="zip" class="form-control input-sm"
							placeholder="Enter Guest's Zip Code" />
					</div>
				</div>
			</div>
			<div class="row">
			        <div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="email">
						Email Address
					</label>
                                	<div class="col-md-7">
						<input type="text" ng-model="guest.email"
							id="email" class="form-control input-sm"
							placeholder="Enter Guest's Email" />
					</div>
				</div>
			</div>
			<div class="row">
				<div class="form-group col-md-12">
					<label class="col-md-2 control-label" for="bungalowNum">
						Assigned Bungalow
					</label>					
				        <div class="col-md-7">
						<input type="text" ng-model="guest.bungalowNum"
							id="bungalowNum" class="form-control input-sm"
							placeholder="Enter Guest's Bungalow" />
					</div>
				</div>
			</div>
			<div class="row">
				<div class="form-actions floatRight">
					<input type="submit" value="Update Guest"
						class="btn btn-info btn-sm">			
				</div>
			</div>
		  </form>
	     </div>
          </div>
      </div>
</div>


Figure 28.

The modification that was done to the guestBookController will set the path to updateGuest. To route the user to our new updateGuest.html, we need to include this route in app.js:

 

	
        $routeProvider.when('/guestRegistration', {
		templateUrl : '/template/guestRegistration.html',
		controller : 'guestRegistrationController'
	}).when('/guestBook', {
		templateUrl : '/template/guestBook.html',
		controller : 'guestBookController'
	}).when('/updateGuest/:id', {
		templateUrl : '/template/updateGuest.html',
		controller : 'updateGuestController'
	});

Figure 29.

 Next, we need to provide an updateGuestController in controller.js so that we can interact with rest. Add the following to controller.js:

app.controller('updateGuestController', function($scope, $http, $location, $routeParams, $route)
{
	
		$scope.guestId = $routeParams.id;
					
		$http({
			method : 'GET',
			url : 'http://localhost:8080/api/guest/' + $scope.guestId,
			
		}).then(function(response) {
			$scope.guest = response.data;

		});
		$scope.submitGuestForm = function() {
			$http({
				method : 'PUT',
				url : 'http://localhost:8080/api/guest/' +  $scope.guestId,
				data : $scope.guest,
			}).then(function(response) {
				$location.path("/guestBook");
				$route.reload();
			}, function(errResponse) {
				$scope.errorMessage = "Error while updating guest - Error Message: '" + errResponse.data.errorMessage;
			});
		}
	
});


Figure 30.

After restarting your web service, you should now be able to update guests and have the changes show on the guest list. You should now have a grasp of what AngularJS can do for you and a foundation to build on to!

Leave a Reply

Your email address will not be published. Required fields are marked *