The TypeScript Compiler Options Used in This Chapter
Name | Description |
---|---|
baseUrl | This option specifies the root location used to resolve module dependencies. |
declaration | This option produces type declaration files when enabled, which describe the types for use in other projects. |
emitDecoratorMetadata | This option determines whether decorator metadata is produced in the JavaScript code emitted by the compiler. |
experimentalDecorators | This option determines whether decorators are enabled. |
importHelpers | This option determines whether helper code is added to the JavaScript to reduce the amount of code that is produced overall. |
lib | This option selects the type declaration files the compiler uses. |
module | This option determines the style of module that is used. |
moduleResolution | This option specifies how modules are resolved. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
sourceMap | This option determines whether the compiler generates source maps for debugging. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
typeRoots | This option specifies the root location that the compiler uses to look for declaration files. |
Preparing for This Chapter
Angular projects are most easily created using the angular-cli package. Open a command prompt and run the command shown in Listing 17-1 to install the angular-cli package.
Tip
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript .
Installing the Project Creation Package
Creating a New Project
The Project Setup Questions and Answers
Question | Answer |
---|---|
Would you like to add Angular routing? | Yes |
Which stylesheet format would you like to use? | CSS |
It can take a few minutes for the project to be created because a large number of JavaScript packages must be downloaded.
Configuring the Web Service
Adding Packages to the Project
The Contents of the data.js File in the angularapp Folder
Configuring Tools in the package.json File in the angularapp Folder
These entries allow both the web service that will provide the data and the Angular development tools to be started with a single command.
Configuring the Bootstrap CSS Package
Adding the CSS Package
Adding a Stylesheet in the angular.json File in the angularapp Folder
Caution
There are two styles settings in the angular.json file, and you must take care to change the one in the build section and not the test section. If you don’t see styled content when you run the example application, the likely cause is that you have edited the wrong section.
Starting the Example Application
Starting the Development Tools
Understanding TypeScript in Angular Development
This is the Component decorator, which describes a class that will generate HTML content, similar in purpose to the JSX classes I created in the stand-alone web app in Chapters 15 and 16.
Understanding the TypeScript Angular Toolchain
The configuration writes the compiled JavaScript files to the dist/out-tsc folder, although you won’t see that folder in the project because webpack is used to create a bundle automatically.
The most important settings are experimentalDecorators and emitDecoratorMetadata, which enable decorators and decorator metadata in the JavaScript files produced by the compiler. This feature—more than any other feature provided by TypeScript—is essential for Angular development.
Caution
Care is required when making changes to the tsconfig.json file because they can break the rest of the Angular toolchain. Most changes in an Angular project are applied through the angular.json File.
Understanding the Two Angular Compilers
Making a Change in the app.component.ts File in the src/app Folder
Replacing the Contents of the app.component.html File in the src/app Folder
The second compiler is included in the bundle and doesn’t rely on TypeScript or the TypeScript compiler.
Understanding Ahead-of-time Compilation
The second Angular compilation stage is performed by the browser every time the application starts, which can introduce a delay before the user is presented with content, especially when the browser is running on a slow device. An alternative approach is ahead-of-time (AOT) compilation, which performs the second compilation phase during the bundling process.
With AOT enabled, both compilers are used to create the contents of the bundle, which means that both the TypeScript and HTML files are compiled into pure JavaScript and no further compilation is required when the bundle is received by the browser.
The advantages of AOT compilation are that the application startup is quicker and the bundle file can be smaller because the code for the compiler is not required. But AOT is not a good choice for all projects because it places restrictions on the TypeScript/JavaScript that can be used, requiring adherence to a subset of language features. See https://angular.io/guide/aot-compiler for details of the restrictions.
Once you have changed the package.json file, use Control+C to stop the development tools, and run the npm start command to launch them again.
Creating the Data Model
The Contents of the entities.ts File in the src/app/data Folder
This is the same code used in Chapter 15 and requires no changes because Angular uses regular TypeScript classes for its data model entities.
Creating the Data Source
The Contents of the dataSource.ts File in the src/app/data Folder
When a new DataSource object is needed, Angular will inspect the constructor, create a DataSourceImpl object, and use it to invoke the constructor to create the new object, a process known as injection. The Injectable decorator tells Angular that other classes can declare dependencies on the DataSource class. The DataSourceImpl class is abstract, and the DataSource class has no idea which concrete implementation class will be used to resolve its constructor dependency. The selection of the implementation class is made in the application’s configuration, as shown in Listing 17-14.
In this situation, I am using the Observable class as a direct replacement for the standard JavaScript Promise. The Observable class provides sophisticated features for dealing with complex sequences, but the advantage here is that Angular will update the content presented to the user when the Observable produces a result, which means that the rest of the DataSource class can be written without needing to deal with asynchronous tasks.
Creating the Data Source Implementation Class
The Contents of the remoteDataSource.ts File in the src/app/data Folder
The type argument is used for the result from the get method, which is an Observable that will generate a sequence of the specified type, which is Product[] in this case.
Tip
The generic type arguments for the HttpClient methods are standard TypeScript. There is no Angular magic happening behind the scenes, and the developer remains responsible for specifying a type that will correspond to the data received from the server.
The pipe method is used with the map function to create an Observable that generates values based on those from another Observable. This allows me to receive the result from the HTTP POST request and extract just the id property from the result.
Note
In the stand-alone web application, I created an abstract data source class and created subclasses that provided local or web service data, which was loaded by a method called in the abstract class constructor. This is an approach that doesn’t work well in Angular because the HttpClient is not assigned to an instance property until after the abstract class constructor is invoked with the super keyword, which means the subclass is asked to get data before it has been properly set up. To avoid this problem, I separated just the part of the data source that deals with the data into the abstract class.
Configuring the Data Source
The Contents of the data.module.ts File in the src/app/data Folder
The DataModelModule class is defined just so that the NgModule decorator can be applied. The decorator’s imports property defines the dependencies that the data model classes require, and the providers property defines the classes in the Angular module that can be injected into the constructors of other classes in the application. For this module, the imports property tells Angular that the module that contains the HttpClient class is required, and the providers property tells Angular that the DataSource class can be used for dependency injection and that dependencies on the DataSourceImpl class should be resolved using the RemoteDataSource class.
Displaying a Filtered List of Products
Angular splits the generation of HTML content into two files: a TypeScript class to which the Component decorator is applied and an HTML template that is annotated with directives that direct the generation of dynamic content. When the application is executed, the HTML template is compiled, and the directives are executed using the methods and properties provided by the TypeScript class.
The Contents of the productItem.component.ts File in the src/app Folder
The Component decorator configures the component. The selector property specifies the CSS selector that Angular will use to apply the component to the application’s HTML, and the templateUrl property specifies the component’s HTML template. For the ProductItem class, the selector property tells Angular to apply this component when it encounters the product-item element and that the component’s HTML template can be found in a file called productItem.component.html in the same directory as the TypeScript file.
Angular uses the Input decorator to denote the properties that allow components to receive data values through HTML element attributes. The Output decorator is used to denote the flow of data out from the component through a custom event. The ProductItem class receives a Product object, whose details it displays to the user, and triggers a custom event when the user clicks a button, accessible through the addToCart property.
The Contents of the productItem.component.html File in the src/app Folder
Expressions are evaluated in the context of the component, so this fragment reads the value of the product.price property, invokes the toFixed method, and inserts the result into the enclosing span element.
The ngModel directly is applied with square brackets and parentheses and creates a two-way binding between the select element and the component’s quantity property. Changes to the quantity property will be reflected by the select element, and values picked using the select element are used to update the quantity property.
Displaying the Category Buttons
The Contents of the categoryList.component.ts File in the src/app Folder
The Contents of the categoryList.component.html File in the src/app Folder
This template uses the ngFor directive to generate a button element for each of the values returned by the categories property. The asterisk (the * character) that prefixes ngFor indicates a concise syntax that allows the ngFor directive to be applied directly to the element that will be generated.
The square brackets allow the value of the class attribute to be set using a JavaScript expression, which is the result of calling the component’s getBtnClass method.
Creating the Header Display
The Contents of the header.component.ts File in the src/app Folder
The Contents of the header.component.html File in the src/app Folder
Combining the Product, Category, and Header Components
The Contents of the productList.component.ts File in the src/app Folder
The ProductList class declares a dependency on the DataSource class and defines products and categories methods that return data from the DataSource. There are three methods that will respond to user interaction: handleCategorySelect will be invoked when the user clicks a category button, handleAdd will be invoked when the user adds a product to the order, and handleSubmit will be called when the user wants to move on to the order summary. The handleSubmit method writes out a message to the console and will be fully implemented in Chapter 18.
The Contents of the productList.component.html File in the src/app Folder
The header tag corresponds to the selector setting for the Component decorator applied to the Header class in Listing 17-19. The order attribute is used to provide a value for the Input property of the same name defined by the Header class and allows ProductList to provide Header with the data it requires. The submit attribute corresponds to the Output property defined by the Header class and allows ProductList to receive notifications. The ProductList template uses header, category-list, and product-item elements to display the Header, CategoryList, and ProductItem components.
Configuring the Application
Configuring the Module in the app.module.ts File in the src/app Folder
The NgModule decorator’s declarations property is used to declare the components that the application requires and is used to add the classes defined in the previous sections. The imports property is used to list the other modules the application requires and has been updated to include the data model module defined in Listing 17-14.
Replacing the Contents of the app.component.html File in the src/app Folder
The user can filter the list of products and add products to the order. Clicking Submit Order only writes a message to the browser’s JavaScript console, but I’ll add support for the rest of the application’s workflow in the next chapter.
Summary
In this chapter, I explained the role that TypeScript has in Angular development. I explained that TypeScript decorators are used to describe the different building blocks that can be used in an Angular application. I also explained that Angular HTML templates are compiled when the browser executes the application, which means that TypeScript features have already been removed and cannot be used in templates. In the next chapter, I complete the application and prepare it for deployment.