AngularJS Services for REST: Preparing the Example Project

I need a back-end service to demonstrate the different ways in which AngularJS can be used to consume a RESTful web service, so I will be using Deployd once again. If you have not downloaded and installed Deployd, then see the instructions in Chapter 1.

Caution I reuse the name of the products data collection from the SportsStore example in Part 1 of this book. If you created the SportsStore example, be sure to remove the Deployd directory before following the instructions in this chapter.

1. Creating the RESTful Service

To create the new service, I typed the following at the command prompt:

dpd create products

To start the new service, I entered the following commands to start Deployd and show the service console:

dpd -p 5500 products\app.dpd

dashboard

The Deployd dashboard will be displayed in the browser, as shown in Figure 21-1.

1.1. Creating the Data Structure

Having created the back-end service, it is time to add the data structure data. Click the green button in the Deployd dashboard and select Collection from the pop-up menu. Set the name of the collection to /products, as shown in Figure 21-2, and then click the Create button.

Deployd will prompt you to define the properties that the objects in the collection will have. Enter the properties I have listed in Table 21-2.

When you have finished, the dashboard should match Figure 21-3. Make sure you have spelled the property names correctly and that you selected the right type for each property.

1.2. Adding the Initial Data

I am going to populate Deployd with some initial data to make creating the example simple. Click the Data link in the Resources section of the dashboard screen and use the table editor to add the data items I have listed in Table 21-3.

When you have added the data, the dashboard should look like the one shown in Figure 21-4.

1.3. Testing the API

If you click the API link in the Deployd dashboard, you will be shown a table that lists the URLs and HTTP methods that can be used to manipulate the data, which is the essence of a RESTful service. My goal in this chapter is to show you the different facilities that AngularJS provides to combine these URLs and HTTP methods to drive the data from an application. In Table 21-4, I have repeated the key details from the API table.

Tip It is always worth checking the API that your RESTful service provides because there isn’t complete consistency about the way that HTTP methods are combined with URLs to manipulate data. As an example, some services support using the patch method to update individual properties for an object, whereas others, including Deployd, use the put method.

The command I used to start Deployd set the port that the server uses to 5500, which means you can manually list the products by opening a browser and navigating to the following URL (assuming you are running Deployd on the local machine):

http://localhost:5500/products

When this URL is requested, the Deployd server returns a JSON string that contains the details entered from Table 21-3. If you are using Google Chrome, then the JSON will be displayed in the browser window, but other browsers, including Internet Explorer, will ask you to save the JSON data to a file. The JSON from Deployd is similar to the JSON I manually created in Chapter 20, but with one difference: Since the data is being stored in a database, each product object is assigned a unique key on a property called id. The value of the id property is used to identify individual product objects in the RESTful URLs, as shown in Table 21-4. Here is the JSON that Deployd sent to represent just one of the product objects:

{“name”:”Apples”,

“category”:”Fruit”,

“price”:1.2,

“id”:”b5m6c8bA96ba29″

}

The id value b57776c8bd96ba29 uniquely identifies the product object whose name property is set to Apples. To delete this object via REST, I would use the HTTP DELETE method to invoke the following URL:

http://localhost:5500/products/b57776c8bd96ba29

2. Creating the AngularJS Application

Now that the RESTful API is set up and populated with data, I am going to create a skeletal AngularJS application. This application will display the content and present the user with the means to add, modify, and delete product objects.

I started by clearing the contents of the angularjs directory and reinstalling the AngularJS and Bootstrap files, as described in Chapter 1. I then created a new HTML file called products.html, the contents of which you can see in Listing 21-1.

Listing 21-1. The Contents of the products.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Products</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script src=”products.js”></script>

</head>

<body ng-controller=”defaultCtrl”>

<div class=”panel panel-primary”>

<h3 class=”panel-heading”>Products</h3>

<ng-include src=”‘tableView.html'” ng-show=”displayMode == ‘list'”></ng-include>

<ng-include src=”‘editorView.html'” ng-show=”displayMode == ‘edit'”></ng-include>

</div>

</body>

</html>

I am going to break this example into a series of smaller files, much as you would do in a real project. The products.html file contains the script element for AngularJS and the link elements for Bootstrap. The main content for this application is contained in two view files, tableView.html and editorView.html, which I will create shortly. These are imported into the products.html file using the ng-include directive, and the visibility of the elements is controlled using the ng-show directive tied to a scope variable called displayMode.

The products.html file also contains a script element for a file called products.js, which I have used to define the behaviors that the application will need. I have started by using dummy local data, which I will replace with data obtained via REST later in the chapter. Listing 21-2 shows the contents of the products.js file.

Listing 21-2. The Contents of the products.js File

angular.module(“exampleApp”, [])

.controller(“defaultCtrl”, function ($scope) {

$scope.displayMode = “list”;

$scope.currentProduct = null;

$scope.listProducts = function () {

$scope.products = [

{ id: 0, name: “Dummy1”, category: “Test”, price: 1.25 },

{ id: 1, name: “Dummy2”, category: “Test”, price: 2.45 },

{ id: 2, name: “Dummy3”, category: “Test”, price: 4.25 }];

}

$scope.deleteProduct = function (product) {

$scope.products.splice($scope.products.indexOf(product), 1);

}

$scope.createProduct = function (product) {

$scope.products.push(product);

$scope.displayMode = “list”;

}

$scope.updateProduct = function (product) {

for (var i = 0; i < $scope.products.length; i++) {

if ($scope.products[i].id == product.id) {

$scope.products[i] = product; break;

}

}

$scope.displayMode = “list”;

}

$scope.editOrCreateProduct = function (product) {

$scope.currentProduct =

product ? angular.copy(product) : {};

$scope.displayMode = “edit”;

}

$scope.saveEdit = function (product) {

if (angular.isDefined(product.id)) {

$scope.updateProduct(product);

} else {

$scope.createProduct(product);

}

}

$scope.cancelEdit = function () {

$scope.currentProduct = {};

$scope.displayMode = “list”;

}

$scope.listProducts();

});

The controller in the listing defines all the functionality I need to operate on the product data. The behaviors I have defined fall into two categories. The first category consists of behaviors that manipulate the data in the scope: the listProducts, deleteProduct, createProduct, and updateProduct functions. These behaviors correspond to the REST operations I described in Table 21-4, and most of this chapter is spent showing you different ways to implement those methods. For the moment, the application uses some dummy test data, just so I can separate showing you how the application works from showing you how to consume restful services.

The other behaviors, editOrCreateProduct, saveEdit, and cancelEdit, all support the user interface and are invoked in response to user interaction. In Listing 21-1, you will see that I used the ng-include directive to import two HTML views. The first of these is called tableView.html, and I use it to display the data and provide buttons that will allow the user to reload the data and create, delete, and edit a product. Listing 21-3 shows the contents of the tableView.html file.

Listing 21-3. The Contents of the tableView.html File

<div class=”panel-body”>

<table class=”table table-striped table-bordered”>

<thead>

<tr>

<th>Name</th>

<th>Category</th>

<th class=”text-right”>Price</th>

<th></th>

</tr>

</thead>

<tbody>

<tr ng-repeat=”item in products’^

<td>{{item.name}}</td>

<td>{{item.category}}</td>

<td class=”text-right”>{{item.price | currency}}</td>

<td class=”text-center”>

<button class=”btn btn-xs btn-primary”

ng-click=”deleteProduct(item)”>

Delete

</button>

<button class=”btn btn-xs btn-primary”

ng-click=”editOrCreateProduct(item)”>

Edit

</button>

</td>

</tr>

</tbody>

</table>

<div>

<button class=”btn btn-primary” ng-click=”listProducts()”>Refresh</button>

<button class=”btn btn-primary” ng-click=”editOrCreateProduct()”>New</button>

</div>

</div>

This view uses AngularJS features that I have described in earlier chapters. I use the ng-repeat directive to generate rows in a table for each product object, and I use the currency filter to format the price property on the product objects. Finally, I use the ng-click directive to respond when the user clicks a button, calling the behaviors defined in the controller defined in the products.js file.

The other view file is called editorView.html, and I use it to allow the user to create new product objects or edit existing ones. You can see the contents of the editorView.html file in Listing 21-4.

Listing 21-4. The Contents of the editorView.html File

<div class=”panel-body”>

<div class=”form-group”>

<label>Name:</label>

<input class=”form-control” ng-model=”currentProduct.name” />

</div>

<div class=”form-group”>

<label>Category:</label>

<input class=”form-control” ng-model=”currentProduct.category” />

</div>

<div class=”form-group”>

<label>Price:</label>

<input class=”form-control” ng-model=”currentProduct.price” />

</div>

<button class=”btn btn-primary” ng-click=”saveEdit(currentProduct)”>Save</button>

<button class=”btn btn-primary” ng-click=”cancelEdit()”>Cancel</button>

</div>

This view uses the ng-model directive to create two-way bindings with the product being edited or created, and it uses the ng-click directive to respond to the user clicking the Save or Cancel button.

2.1. Testing the Application

To test the AngularJS application, simply load the products.html file into the browser. All of the other files will be imported, and you will see the list of dummy data, as illustrated in Figure 21-5.

If you click the Delete button, the deleteProduct behavior will be invoked, and the product in the corresponding row will be removed from the data array. If you click the Refresh button, the listProducts behavior will be invoked, and the data will be reset because this is where the dummy data is defined; the data won’t be reset when I start making Ajax requests.

Clicking the Edit or New button will invoke the editOrCreateProduct behavior, which causes the contents of the editorView.html file to be displayed, as shown in Figure 21-6.

If you click the Save button, the changes made to an existing item will be saved or a new product will be created. I rely on the fact that data objects that are being edited will have an id attribute. The Cancel button returns to the list view without saving any changes, which I handle by using the angular.copy method to create a copy of the product object so that I can discard it when needed.

Note One shortcoming of the current implementation is that I don’t add an id attribute when I create new product objects. This is because the RESTful service will set the id value for me when a new product is stored in the database and will be resolved when I add support for real network requests.

Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.

Leave a Reply

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