you can write angular applications using just the JavaScript features that work in older browsers, without needing to add the additional step of using the typeScript compiler to process files.
the problem with this approach is that it results in code that is difficult to read and difficult to manage.
there are some recent JavaScript features that simplify the way that angular features are defined and applied and one feature, called decorators, that is essential for effective angular development but that is only a proposal for the JavaScript standardization process and isn’t supported by any browser.
My advice is to embrace the complete angular experience, even though it can take some time and effort to master the new JavaScript features. the result will be a better development experience, containing code that is more concise, easier to read, and simpler to maintain. this is the approach i have taken in this book, and every example assumes that this is the path you are following.
this book doesn’t explain how to create angular applications using just classic JavaScript features.
if this is the path that you want to follow, then the best place to start is the JavaScript section of the angular website, https://angular.io/docs/js/latest, which contains basic information to help you get started.
Table 5-1. Chapter Summary
Problem Solution Listing
Add JavaScript to an HTML document
Use the script element or a JavaScript module loader
1–6 Create JavaScript functionality Use JavaScript statements 7 Create groups of statements that are
executed on command
Use functions 8, 9, 12–14
Define functions that can handle more or fewer arguments that parameters
Use default or rest parameters 10–11
Express functions more concisely Use arrow functions 15 Store values and objects for later use Declare variables using the let or var
keywords
16–17 Store basic data values Use the JavaScript primitive types 18–21 Control the flow of JavaScript code Use conditional statements 22 Determine whether two objects or
values are the same
Use the quality and identity operators 23–24 Explicitly convert types Use the to<type > methods 25–27 Store related objects or values
together in sequence
Use an array 28–33
Preparing the Example Project
To prepare for this chapter, I started a new project by creating a folder called JavaScriptPrimer. I added a file called package.json within the JavaScriptPrimer folder and added the configuration shown in Listing 5-1.
Listing 5-1. The Contents of the package.json File in the JavaScriptPrimer Folder {
"dependencies": { "core-js": "2.4.1",
"classlist.js": "1.1.20150312", "systemjs": "0.19.40",
"bootstrap": "4.0.0-alpha.4"
},
"devDependencies": { "lite-server": "2.2.2", "typescript": "2.0.3", "typings": "1.4.0", "concurrently": "3.1.0"
},
"scripts": {
"start": "concurrently \"npm run tscwatch\" \"npm run lite\" ", "tsc": "tsc",
"tscwatch": "tsc -w", "lite": "lite-server", "typings": "typings"
} }
The packages that are specified in the package.json file include polyfill libraries that add support for essential features to older JavaScript browsers, a JavaScript module loader, and the Bootstrap CSS framework. The tools required to use TypeScript to generate JavaScript files are also included. Run the following command in the JavaScriptPrimer folder to download and install the packages:
npm install
Creating the HTML and JavaScript Files
I created a file called primer.ts in the JavaScriptPrimer folder and added the code shown in Listing 5-2.
This is just a placeholder to get the project started.
Listing 5-2. The Contents of the primer.ts File in the JavaScriptPrimer Folder console.log("Hello");
I also added a file called index.html to the JavaScriptPrimer folder and added the HTML elements shown in Listing 5-3.
Listing 5-3. The Contents of the index.html File in the JavaScriptPrimer Folder
<!DOCTYPE html>
<html>
<head>
<title>Primer</title>
<meta charset="utf-8" />
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet" />
<script src="primer.js"></script>
</head>
<body class="m-a-1">
<h3>JavaScript Primer</h3>
</body>
</html>
The HTML file includes a script element that loads a file called primer.js. This file doesn’t exist yet, but the TypeScript compiler will generate it when it processes the primer.ts file.
Configuring the TypeScript Compiler
The TypeScript compiler requires a configuration file that specifies how it should generate JavaScript files. I created a file called tsconfig.json in the JavaScriptPrimer folder and added the configuration shown in Listing 5-4.
Listing 5-4. The Contents of the tsconfig.json File in the JavaScriptPrimer Folder {
"compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true },
"exclude": [ "node_modules" ] }
I explain what each configuration setting does in Chapter 11, but for now, it is enough to just create the file.
Running the Example Project
Once you have created all the required files, run the following command to start the TypeScript compiler and the development HTTP server:
npm start
A new browser tab or window will open and display the contents of the index.html file, as shown in Figure 5-1.
Open the browser’s F12 developer tools (so called because they are typically opened by pressing the F12 key) and look at the JavaScript console, which is shown in Figure 5-2.
The JavaScript console shows the result of the call to the console.log function from Listing 5-3. Rather than show a screenshot of the browser’s JavaScript console for each example, I’ll just show the result text, like this:
Hello
Figure 5-1. Running the example application
Figure 5-2. The Google Chrome JavaScript console
...
<script src="primer.js"></script>
...
The name of the file loaded by this element is primer.js. When using the TypeScript compiler, there is an indirection relationship between the files that contain the JavaScript code that you write and the files that are loaded by the browser.
This can be an awkward transition to make if you are used to writing JavaScript files directly, but it allows the TypeScript compiler to translate the most recent features from the JavaScript specification into code that will run on older browsers.
Using a JavaScript Module Loader
Manually keeping track of two sets of files and making sure that the HTML file contains the correct set of script elements is an error-prone process. You are likely to add a script element that includes a file with the .ts extension rather than its .js counterpart or forget to add a script element for a new file. It is also hard to correctly order the script elements. Browsers execute the content of JavaScript files in the order defined by the script elements in the HTML document, and it is easy to create a situation where code in the file loaded by one script element depends on functionality loaded by another script element that the browser has yet to load.
Managing the JavaScript content in an application can be simplified by using a module loader, which takes responsibility for detecting and resolving dependencies between JavaScript files, loading them, and ensuring their contents are executed in the right order. The package.json file I created at the start of the chapter included the SystemJS module loader, and Listing 5-5 shows how it can be applied to the HTML document to manage the JavaScript files.
Listing 5-5. Using a JavaScript Module Loader in the index.html File
<!DOCTYPE html>
<html>
<head>
<title>Primer</title>
<meta charset="utf-8" />
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet" />
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script>
System.config({ packages: {"": {}}});
System.import("primer").catch(function(err){ console.error(err); });
</script>
</head>
<body class="m-a-1">
<h3>JavaScript Primer</h3>
</body>
</html>
I explain how to create JavaScript modules in Chapter 6 and how the SystemJS module loader is used in more detail in Chapter 11, where I also use it to load Angular and some of the libraries it depends on. In this listing, there is a script element that loads the SystemJS module loader and another that configures and applies it. The most important change in the listing is this statement:
...
System.import("primer").catch(function(err){ console.error(err); });
...
This statement tells the module loader to load a module called primer, which is translated into a request for the primer.js file. One benefit of using the module loader is that you don’t have to pay attention to the file extensions in individual script elements.
Understanding the Basic Workflow
To understand the way that the different workflow steps relate to one another, add a statement to the primer.ts file, as shown in Listing 5-6.
Listing 5-6. Adding a Statement in the primer.ts File console.log("Hello");
console.log("Apples");
When you save the change to the primer.ts file, the following process will occur:
1. The TypeScript compiler will detect the change to the primer.ts file and compile it to generate a new primer.js file that can run in any browser.
2. The development HTTP server detects the change to the primer.js file and signals the browser to reload the HTML document.
3. The browser reloads the HTML document and starts processing the elements it contains. It loads the JavaScript files specified by the script elements in the HTML document, including those for the JavaScript module loader.
4. The JavaScript module loader processes its configuration and asynchronously requests the primer.js file from the HTTP server.
5. The JavaScript module loader asks the browser to execute the code in the primer.js file, which writes out two messages to the browser’s JavaScript console.
The overall result is that you will see the following messages displayed:
Hello Apples
This may seem like a large number of steps for a simple application, but this is an approach that scales up to handle complex projects and allows recent JavaScript features to be used.
Getting used to the extra steps required in Angular development can take a while and the development workflow can feel indirect and cumbersome, but it does eventually become second nature, especially when the changes to the code files lead to the browser being automatically reloaded.
Using Statements
The basic JavaScript building block is the statement. Each statement represents a single command, and statements are usually terminated by a semicolon (;). The semicolon is optional, but using them makes your code easier to read and allows for multiple statements on a single line. In Listing 5-7, I have added a pair of statements to the JavaScript file.
Listing 5-7. Adding JavaScript Statements in the primer.ts File console.log("Hello");
console.log("Apples");
console.log("This is a statement");
console.log("This is also a statement");
The browser executes each statement in turn. In this example, all the statements simply write messages to the console. The results are as follows:
Hello Apples
This is a statement This is also a statement
Defining and Using Functions
When the browser receives JavaScript code, either directly through a script element or indirectly through the module loader, it executes the statements it contains in the order in which they have been defined. This is what happened in the previous example. The module loader loaded the primer.js file, and the statements it contains were executed one by one, all of which wrote a message to the console.
You can also package statements into a function, which won’t be executed until the browser encounters a statement that invokes the function, as shown in Listing 5-8.
Listing 5-8. Defining a JavaScript Function in the primer.ts File let myFunc = function () {
console.log("This is a statement");
};
myFunc();
Defining a function simple: use the let keyword followed by the name you want to give the function, followed by the equal sign (=) and the function keyword, followed by parentheses (the ( and ) characters).
The statements you want the function to contain are enclosed between braces (the { and } characters).
In the listing I used the name myFunc, and the function contains a single statement that writes a message to the JavaScript console. The statement in the function won’t be executed until the browser reaches another statement that calls the myFunc function, like this:
...
myFunc();
...
Executing the statement in the function produces the following output:
This is a statement
Other than demonstrating how functions are defined, this example isn’t especially useful because the function is invoked immediately after it has been defined. Functions are much more useful when they are invoked in response to some kind of change or event, such as user interaction.