masaj salonu masaj salonları
Home » Advertising » Developing a Web Application Using Angular (Part 5)

Developing a Web Application Using Angular (Part 5)

In the previous four parts of this series, we started with a simple goal of creating a web application to view, create, update, and delete orders from our order management web service and ultimately constructed an Angular Single Page Application (SPA) that accomplished this goal. In Part 1, we developed a User Interface (UI) design and laid out our plan of action; in Part 2, we developed an architecture for our web application and implemented the resource layer; in Part 3, we implemented our service layer and solidified our abstraction of the order management web service; in Part 4, we tied the web application together by implementing the UI layer and bootstrapping the application to run in conjunction with the order management web service.

In the final part of this series, we will start both the order management web service and our newly developed web application and demonstrate some of the debugging and inspection tools that can be used to ensure that our application functions as intended. We will also explore some of the testing frameworks that can be used to automate our manual testing processes, as well as some additional features that are common among more complex web applications. Before we delve into these supplemental topics, though, we must start the web application and web service and ensure that our web application is functioning as intended.

Table Of Contents

Starting the Application

The first step to exercising our web application is to start the order management web service. This web service can be found in the following GitHub repository:

Image title

To clone the above repository and start the web service, execute the following commands (Git and Maven must both be installed prior to running the web service):

git clone https://github.com/albanoj2/order-rest-backend.git
cd order-rest-backend
mvn spring-boot:run

Once the order management web service is started, we must start our newly developed web application. To do so, we must clone the following GitHub repository (in a separate terminal from the one running the order management web service):

Image title

To clone the above repository and start the web application, execute the following commands (Git and the Node Package Manager, NPM, must be installed):

git clone https://github.com/albanoj2/order-angular-frontend.git
cd order-angular-frontend
npm install
npm start

Once the web application has started, our web application can be accessed from http://localhost:4200.

Using the Application

With our application started, we are ready to exercise the functionality that we developed in the previous parts of this series. If we navigate to http://localhost:4200 in a browser, we notice that the URL immediately changes to http://localhost:4200/orders. This redirection is due to the route configuration we established while developing the UI layer:

const routes: Routes = [
    { path: '', redirectTo: '/orders', pathMatch: 'full' },
    // ...Other routes removed for brevity...
];

@NgModule({
    imports: [ RouterModule.forRoot(routes) ],
    exports: [ RouterModule ]
})
export class AppRoutingModule {}

Once we are redirected to this landing page, we see an empty list of our orders. Since our order management web service utilizes an in-memory database, all existing orders are lost when the web service is shut down. Therefore, to see any orders, we will have to create them.

Image title

Creating an Order

To create a new order, we simply click the create button on the bottom right of the orders list. This will open our CreateOrderComponent and display three fields for us to fill out: (1) the description, (2) the cost, and (3) the completion status.

Image title

Note that when we open the creation component, the URL changes to http://localhost:4200/orders/create. This transition occurs due to the CreateOrderComponent route we previously established, coupled with the navigate calls made to the Router object that was passed into our OrdersComponent:

const routes: Routes = [
    // ...Other routes not shown for brevity...
    { path: 'orders/create', component: CreateOrderComponent }
];

@NgModule({
    imports: [ RouterModule.forRoot(routes) ],
    exports: [ RouterModule ]
})
export class AppRoutingModule {}

Also, note that the description field displays an error message and the Create button is disabled. These error-checking features are tied to the isDescriptionValid() and canSubmit() methods of the SaveOrderComponent class, respectively. Once we enter a description into the description field, the error message goes away and the Create button becomes enabled. We can also enter a value for the cost and a completion status.

Before we create our new order, we can check to ensure our web application correctly interacts with our order management web service over HTTP by right-clicking on the screen and clicking Inspect. Under the inspection window, we can click the Network tab and see a list of network traffic that our browser is generating (for the current browser tab or window). For more information on opening the network inspection tab in Chrome, see the official Chrome documentation.

Once we are satisfied with the input values and have our network inspection tab open, we can click the Create button to create our new order. Doing so returns us to the list of orders, which now contains our created order:

Image title

If we look at the network inspection tab, we see the following:

Image title

Under the General section, we can see that our web application generated a Hypertext Transfer Protocol (HTTP) POST message to http://localhost:8080/order (the URL corresponding to our order management web service). Whatsmore, under the Request Payload section, we can see that this POST message included the following request body:

{
    "description": "A first test order", 
    "costInCents": 7234, 
    "complete": false
}

If we click on the Response tab, we can see that the response to this HTTP POST call was:

{
    "description": "A first test order",
    "costInCents": 7234,
    "complete": false,
    "_links": {
        "self": {
            "href":"http://localhost:8080/order/1"
        },
        "update": {
            "href":"http://localhost:8080/order/1"
        },
        "delete": {
            "href":"http://localhost:8080/order/1"
        }
    },
    "id": 1
}

By analyzing this network transaction, we can see that our web application has not only successfully sent a request to the order management web service to create a new order, but the web service has successfully responded with the resource corresponding to our newly created order. This newly created order is then retrieved from the web service (the list of all orders is retrieved, as was implemented in the onSave() method of our CreateOrderComponent class) and displayed in the list of existing orders. If we create another order, we see both in the list of orders:

Image title

Searching for an Order

As more orders are added, it can become difficult to see only the relevant orders. To filter out irrelevant orders, we can type the description of our desired order (or the starting portion of the description) into our Search field to reduce the number of orders we see in the list:

Image title

Editing an Order

In order to edit this order, we simply click the pencil icon to the left of the order description. This opens the EditOrderComponent, which also changes the URL to http://localhost:4200/orders/1/edit. We can now make changes to the order as desired. Note that if we remove the description, the same error-checking logic takes place as when we created a new order: an error message is displayed under the description field and the Update button is disabled. This identical error-checking logic derives from the shared SaveOrderComponent, which contains the error-checking methods seen in both the EditOrderComponents and CreateOrderComponent classes. Since both components extend this common component, they both possess the same logic.

Image title

If we click the Update button, we see that our existing order has been updated to reflect the changes we made in our editing component. We also see the URL return to http://localhost:4200/orders, denoting that we have returned to the OrdersComponent. (Note that if instead, we clicked the Cancel button, we would have been returned to the list of existing orders, but the changes we made to our order through the EditOrderComponent would have been dropped.) During the update process, we can see in the network inspection tab that our HTTP PUT request was sent to http://localhost:8080/order/1 with the following request body:

{
    "id": 1,
    "description": "A new name for first order",
    "costInCents": 4538,
    "complete": true
}

This request is, in turn, met with the following response body:

{
    "description": "A new name for first order",
    "costInCents": 4538,
    "complete": true,
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1"
        },
        "update": {
            "href": "http://localhost:8080/order/1"
        },
        "delete": {
            "href": "http://localhost:8080/order/1"
        }
    },
    "id": 1
}

Deleting an Order

The last feature to test is the deletion of an existing order. To do this, simply click the X icon to the left of an order. Note that there is no confirmation window that pops-up to request that the user confirm the deletion. Although such a confirmation is standard practice in real-world web applications, we have foregone this feature for simplicity. To implement this confirmation feature, we can alter the deleteOrder(order: Order) method of the OrdersComponent class to provide a confirmation prior to calling the deleteOrder method on our injected OrderService object.

Further Information

While we have fully exercised the main functionality we implemented in our web application, we would be remiss if we did not at least touch on some supplemental topics, such as automating the acceptance and usability testing processes and more advanced concepts for our orders.

Automated Testing Frameworks

Although we have now successfully tested each of the main features of our web application, it is a painstakingly manual process to do so, especially if we have to conduct this series of tests each time a change is made to the web application source code. While not all acceptance or usability tests can be automated, it is often worthwhile to automate the most crucial tests to ensure that core features are continuously functioning throughout the life of the application.

The most common automated testing framework currently available is Selenium, which automates the execution of browser-based actions. While Selenium by itself is powerful enough for a wide array of automated acceptance tests, the Angular team has developed a special-purpose framework that includes Selenium support targeted for Angular applications: Protractor.

While we could dedicate an entire series of articles to the testing frameworks and examples of their use, for our purposes, the following list of resources can help in developing a basic set of automated acceptance and usability tests:

More Advanced Concepts

Although our web application contains the basic functionality necessary to interact with our order management web service, there are a few features that are commonly present in web applications that we have not touched on. Due to the brevity of this tutorial, we will not go into detail on how to implement these features, but rather provide guidance on the need for the feature and where to find more information on how to include these features in an Angular SPA:

  • Pagination: As the number of orders grows, so too does the size of the list of orders. At some threshold, the length of this list will become cumbersome and inhibit the usability of our web application. For example, if there are 10,000 orders, the user would be required to scroll through a very large list that takes up more vertical space than is offered by the browser (or the user’s monitor). In order to mitigate this issue, we can divide the list of orders into pages and provide page navigation buttons underneath the list of users. For more information, including Bootstrap pagination in an Angular application, see the Pagination section of the Angular-UI Bootstrap documentation.
  • Details Component: Due to the simplicity of an order in our web application, we have decided to forego a dedicated component to display the details of an order. Such as a details component would display the extensive details of an order if such details existed. Likewise, the list of orders would contain only the more general information about an order, such as the description and cost, while the details page would contain information such as the when the order was initiated, when it was completed, any dependent orders, etc. (assuming such details exist), along with links or buttons to edit or delete the order. We would then route the URL http://localhost:4200/orders/{id} to this details component, where {id} is the ID of the order whose details are displayed.
  • Identity and Permissions: Had our order management system supported it, we would have implemented the concept of identities (such as users) with associated permissions. Assuming that there were read, write, and delete permissions, we would alter the orders and actions displayed in our web application based on the identity currently logged in. For example, if the current identity did not have permission to read an order, it would be omitted from the list. Likewise, if the current identity did not have permission to edit or delete an order, the edit and delete action icons, respectively, would have been omitted from the order entry in the list of orders. While this permission scheme allows us to restrict the access that users have to certain features, it can be a difficult task to keep such a system secure. For more information on implementing an authorization scheme in a web service using Spring, see the Spring Security Authorization Documentation.

The above web application features only scratch the surface for many of the large web applications that exist today; only through experience and by studying successful web applications will these patterns become more evident and their application become useful in our future web applications.

Conclusion

Starting with a hand-drawn UI design, we gradually worked our way towards the creation of our architecture, design of our three web application layers, and the implementation of the classes and templates within each layer. With our implementation complete, we then exercised the main features of our application, enjoying the fruits of our labor (in bringing our simple sketch to life).

While this tutorial covered many of the fundamental facets of creating a web application in Angular, there are countless others that were not covered. While great guides, such as the official Angular Fundamentals reference guide, exist for your benefit, only experience and practice in creating more complex and challenging web applications will help our skills grow and aid in becoming better web developers.

Leave a Reply

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

*
*

cover letter