In this chapter, I continue building the SportsStore application by adding administration features. Relatively few users will need to access the administration features, so it would be wasteful to force all users to download the administration code and content when it is unlikely to be used. Instead, I am going to put the administration features in a separate module that will be loaded only when it is required.
Preparing the Example Application
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/pro-angular-9. See Chapter 1 for how to get help if you have problems running the examples.
Creating the Module
The process for creating the feature module follows the same pattern you have seen in earlier chapters. The key difference is that it is important that no other part of the application has dependencies on the module or the classes it contains, which would undermine the dynamic loading of the module and cause the JavaScript module to load the administration code, even if it is not used.
The Content of the auth.component.ts File in the src/app/admin Folder
The component defines properties for the username and password that will be used to authenticate the user, an errorMessage property that will be used to display messages when there are problems, and an authenticate method that will perform the authentication process (but that does nothing at the moment).
The Content of the auth.component.html File in the src/app/admin Folder
The template contains an HTML form that uses two-way data binding expressions for the component’s properties. There is a button that will submit the form, a button that navigates back to the root URL, and a div element that is visible only when there is an error message to display.
The Contents of the admin.component.ts File in the src/app/admin Folder
The Contents of the admin.component.html File in the src/app/admin Folder
The Contents of the admin.module.ts File in the src/app/admin Folder
The RouterModule.forChild method is used to define the routing configuration for the feature module, which is then included in the module’s imports property.
A dynamically loaded module must be self-contained and include all the information that Angular requires, including the routing URLs that are supported and the components they display. If any other part of the application depends on the module, then it will be included in the JavaScript bundle with the rest of the application code, which means that all users will have to download code and resources for features they won’t use.
However, a dynamically loaded module is allowed to declare dependencies on the main part of the application. This module relies on the functionality in the data model module, which has been added to the module’s imports so that components can access the model classes and the repositories.
Configuring the URL Routing System
Configuring a Dynamically Loaded Module in the app.module.ts File in the src/app Folder
The new route tells Angular that when the application navigates to the /admin URL, it should load a feature module defined by a class called AdminModule from the admin/admin.module.ts file, whose path is specified relative to the app.module.ts file. When Angular processes the admin module, it will incorporate the routing information it contains into the overall set of routes and complete the navigation.
Navigating to the Administration URL
Adding a Navigation Button in the store.component.html File in the src/app/store Folder
Implementing Authentication
The RESTful web service has been configured so that it requires authentication for the requests that the administration feature will require. In the sections that follow, I add support for authenticating the user by sending an HTTP request to the RESTful web service.
Understanding the Authentication System
The Authentication Credentials Supported by the RESTful Web Service
Username | Password |
---|---|
admin | secret |
As I noted in Chapter 7, you should not hard-code credentials in real projects, but this is the username and password that you will need for the SportsStore application.
Extending the Data Source
Adding Authentication in the rest.datasource.ts File in the src/app/model Folder
Creating the Authentication Service
The Contents of the auth.service.ts File in the src/app/model Folder
The authenticate method receives the user’s credentials and passes them on to the data source authenticate method, returning an Observable that will yield true if the authentication process has succeeded and false otherwise. The authenticated property is a getter-only property that returns true if the data source has obtained an authentication token. The clear method removes the token from the data source.
Configuring the Services in the model.module.ts File in the src/app/model Folder
Enabling Authentication
Enabling Authentication in the auth.component.ts File in the src/app/admin Folder
The Contents of the auth.guard.ts File in the src/app/admin Folder
Guarding a Route in the admin.module.ts File in the src/app/admin Folder
To test the authentication system, click the Admin button, enter some credentials, and click the Log In button. If the credentials are the ones from Table 9-1, then you will see the placeholder for the administration features. If you enter other credentials, you will see an error message. Figure 9-3 illustrates both outcomes.
The token isn’t stored persistently, so if you can, reload the application in the browser to start again and try a different set of credentials.
Extending the Data Source and Repositories
Adding New Operations in the rest.datasource.ts File in the src/app/model Folder
Adding New Operations in the product.repository.ts File in the src/app/model Folder
Adding New Operations in the order.repository.ts File in the src/app/model Folder
The order repository defines a loadOrders method that gets the orders from the repository and that ensures that the request isn’t sent to the RESTful web service until authentication has been performed.
Creating the Administration Feature Structure
The URLs for Administration Features
Name | Description |
---|---|
/admin/main/products | Navigating to this URL will display all the products in a table, along with buttons that allow an existing product to be edited or deleted and a new product to be created. |
/admin/main/products/create | Navigating to this URL will present the user with an empty editor for creating a new product. |
/admin/main/products/edit/1 | Navigating to this URL will present the user with a populated editor for editing an existing product. |
/admin/main/orders | Navigating to this URL will present the user with all the orders in a table, along with buttons to mark an order shipped and to cancel an order by deleting it. |
Creating the Placeholder Components
The Contents of the productTable.component.ts File in the src/app/admin Folder
The Contents of the productEditor.component.ts File in the src/app/admin Folder
The Contents of the orderTable.component.ts File in the src/app/admin Folder
Preparing the Common Content and the Feature Module
Replacing the Content in the admin.component.html File in the src/app/admin Folder
This template contains a router-outlet element that will be used to display the components from the previous section. There are also buttons that will navigate the application to the /admin/main/products and /admin/main/orders URLs, which will select the products or orders features. These buttons use the routerLinkActive attribute, which is used to add the element to a CSS class when the route specified by the routerLink attribute is active.
Implementing the Logout Method in the admin.component.ts File in the src/app/admin Folder
Configuring the Feature Module in the admin.module.ts File in the src/app/admin Folder
Individual routes can be extended using the children property, which is used to define routes that will target a nested router-outlet element, which I describe in Chapter 25. As you will see, components can get details of the active route from Angular so they can adapt their behavior. Routes can include route parameters, such as :mode or :id, that match any URL segment and that can be used to provide information to components that can be used to change their behavior.
Implementing the Product Feature
Replacing Content in the productTable.component.ts File in the src/app/admin Folder
The Contents of the productTable.component.html File in the src/app/admin Folder
The template contains a table that uses the ngFor directive to generate a row for each product returned by the component’s getProducts method. Each row contains a Delete button that invokes the component’s delete method and contains an Edit button that navigates to a URL that targets the editor component. The editor component is also the target of the Create New Product button, although a different URL is used.
Implementing the Product Editor
Adding Functionality in the productEditor.component.ts File in the src/app/admin Folder
Angular will provide an ActivatedRoute object as a constructor argument when it creates a new instance of the component class, and this object can be used to inspect the activated route. In this case, the component works out whether it should be editing or creating a product and, if editing, retrieves the current details from the repository. There is also a save method, which uses the repository to save changes that the user has made.
The Contents of the productEditor.component.html File in the src/app/admin Folder
The template contains a form with fields for the properties defined by the Product model class, with the exception of the id property, which is assigned automatically by the RESTful web service.
Implementing the Orders Feature
Adding Operations in the orderTable.component.ts File in the src/app/admin Folder
The Contents of the orderTable.component.html File in the src/app/admin Folder
Summary
In this chapter, I created a dynamically loaded Angular feature module that contains the administration tools required to manage the catalog of products and process orders. In the next chapter, I finish the SportsStore application and prepare it for deployment into production.