CHAPTER 3
In Chapter 2, we looked at getting your development environment set up how you want it—with Aurelia, we have many options and choices. In this chapter, we are going to be looking at the skeleton navigation and evaluating all the moving pieces. You can either follow along with the setup that you liked best from Chapter 2, or you can follow along from the Git repository for this book that has a project dedicated for each chapter. Before we dig into any Aurelia-specific features, it is necessary to talk about some new programming paradigms that we get from ES6 and ES7. We will then look at some dependency injection and how it is useful. Finally, we will look at decorators and async/await features.
Aurelia gives you the ability to develop your applications using ES6 and ES7 features natively using transpiling or Microsoft TypeScript. We will be looking at ES6 and ES7 capabilities in this book, but be aware that you also have the option of using TypeScript. Below is a list of some of the features we get while developing applications in Aurelia:
All the features of ES6 and ES7 deserve their own dedicated book, and you can check out ES6 Succinctly here if you are interested in what it brings to the table. We will cover each of the listed items briefly to get you up and running.
If you are familiar with developing in Java or C#, then you will be very comfortable with the concept of classes. In the new JavaScript (ES6), this is a nice added feature that allows us to maintain a clean separation of concerns with very little ceremony. Before we had classes, we had to use the revealing module pattern to ensure that we were not polluting the global namespace. Let’s take a look at what a class looks like in JavaScript:
Code Listing 55
class Welcome { constructor() { } } |
This syntax is clean and simple.
Another long-awaited feature is better scoping in JavaScript. In ES5, regardless of whether our variables were declared inside functions or blocks, they would be hoisted to the top block level, and this would cause a lot of problems. We now have a new keyword, let, and this gives us true block-level scoping. Consider the following:
Code Listing 56
class Welcome { constructor() { this.result = 0; } add(first, second) { let result = -1;
if (first && second) { let result = first + second; }
return result; } } |
Before we go on and use this class and the add function, let’s look at what we have created here. In ES5, this could potentially cause a lot of headaches and problems. We are defining a class-level variable inside the constructor called result. Next, we create a function-level variable called result and initialize it to -1. We then create a block-level variable and initialize it to the first and second variables. Finally, we return the variable result back from the function.
Let’s now write a little code to test this class and see what happens:
Code Listing 57
let w = new Welcome(); console.log(w.add(1,2)); // -1 console.log(w.result); // 0 |
Our code is now more consistent and behaves as would be expected. You can see that the add function returns -1 when we call it. You can also see that when we access the class-level variable, we get 0 back.
Strings in JavaScript have always been a pain, and have always been limited when compared to other mature languages. Template strings change this completely and give us a lot of power with our string manipulation. Some of the chief features are:
Before we look at any of these new features, we must learn the pattern for creating template strings. Consider the following:
Code Listing 58
let message = `Hello World`; |
This simple example introduces the use of backticks (`) instead of single or double quotes. That is all there is to template strings. Let’s now look at how we can use them.
String interpolation gives us the ability to substitute values within our template strings with variables. Let’s look at the following example:
Code Listing 59
let fname = 'Matt'; let lname = 'Duffield'; let message = `Hello ${fname} ${lname}, how are you?`; |
When we execute this script, we get: “Hello Matt Duffield, how are you?”
Embedded expressions allow us to embed JavaScript expressions in our template strings. Consider the following example:
Code Listing 60
let three = 3; let four = 4; let message = `The product of 3 x 4 is ${three*four}`; |
When we execute this script, we get: “The product of 3 x 4 is 12”. We can call functions and even access members off of our objects, as well.
Multiline strings are by far one of the most awaited features of ES6, as they give us cleaner syntax when trying to author multiline content. Let’s look at the following example:
Code Listing 61
let user = 'Matt'; let message = `Thank you ${user} for contact the support team! We value your business and are more than happy to answer any questions you may have.`; |
Note: The creators of Aurelia wisely chose to use the same syntax notation for their databinding engine.
Decorators allow us to annotate pieces of code to help reduce redundancies. Consider the following example of how we use decorators for dependency injection:
Code Listing 62
import {inject} from 'aurelia-framework'; import {DataService} from 'services/data-service'; @inject(DataService) class Customer { constructor(dataService) { this.dataService = dataService; } ... } |
You will see other advantages of using decorators when working with TypeScript in Aurelia, as well. If you wish to use dependency injection without the inject decorator, you can write it out as follows:
Code Listing 63
import {DataService} from 'services/data-service'; class Customer { static inject = [DataService]; constructor(dataService) { this.dataService = dataService; } ... } |
As you get more comfortable with Aurelia, you will see uses of decorators throughout the framework and in plugins.
Note: By default, Aurelia handles all dependencies as singletons. You can change this by decorating your class with the @transient decorator.
Writing code that allows you to make asynchronous calls while keeping the code clean has long plagued JavaScript users. Using libraries and promises, we have come closer, but now with async and await coming in ES7, we are getting a paradigm that makes asynchronous programming much less difficult. First, let’s look at how we currently implement our logic without async and await:
Code Listing 64
import {DataService} from 'services/data-service'; class Customer { static inject = [DataService]; data = []; constructor(dataService) { this.dataService = dataService; } activate() { return this.dataService.getCustomers() .then(response => { this.data = resonse.json(); }); } } |
If we return a promise from the activate function part of the screen lifecycle, Aurelia will wait until the call returns and executes the then callback before binding to the screen. We will go into Aurelia’s screen activation lifecycle later, but this is a common scenario and needs to be shown so you can see the differences in implementation. Now, let’s look at the same implementation using async and await:
Code Listing 65
import {DataService} from 'services/data-service'; class Customer { static inject = [DataService]; data = []; constructor(dataService) { this.dataService = dataService; } async activate() { let response = await this.dataService.getCustomers(); this.data = await response.json(); } } |
We are effectively doing exactly the same thing. In fact, Babel will transpile this code to look pretty much like the first example. This is much cleaner and helps reduce the madness involved writing a lot asynchronous code.