Book Image

Mastering Angular Components - Second Edition

By : Gion Kunz
Book Image

Mastering Angular Components - Second Edition

By: Gion Kunz

Overview of this book

Mastering Angular Components will help you learn how to invent, build, and manage shared and reusable components for your web projects. Angular components are an integral part of any Angular app and are responsible for performing specific tasks in controlling the user interface. Complete with detailed explanations of essential concepts and practical examples, the book begins by helping you build basic layout components, along with developing a fully functional task-management application using Angular. You’ll then learn how to create layout components and build clean data and state architecture for your application. The book will even help you understand component-based routing and create components that render Scalable Vector Graphics (SVG). Toward the concluding chapters, you’ll be able to visualize data using the third-party library Chartist and create a plugin architecture using Angular components. By the end of this book, you will have mastered the component-based architecture in Angular and have the skills you need to build modern and clean user interfaces.
Table of Contents (12 chapters)

JavaScript of the future

It was not so long ago that somebody asked me whether we should really use the bind function of ECMAScript 5.1, as then we'd probably run into browser compatibility issues. The web moves very fast, and we need to keep up the pace. We can't write code that does not use the latest features, even if this would cause issues in old browsers.

The fantastic people from TC39, the technical committee that is responsible for writing the ECMAScript specification, have done a great job progressively enhancing the JavaScript language. This, and the fact that JavaScript is so flexible, allows us to use so-called polyfills and shims to make our code run in older browsers.

ECMAScript 6 (also referred to as ECMAScript 2015) was published in June 2015, exactly four years after its predecessor. There is a massive amount of new API additions as well as a whole bunch of new language features. The language features are syntactic sugar, and ECMAScript 6 can be transpiled to its previous version where it runs perfectly in older browsers. At the time of writing this book, none of the current browser versions have fully implemented ECMAScript 6, but there's absolutely no reason not to use it for production applications.

Syntactic sugar is a design approach where we evolve a programming language without breaking backwards compatibility. This allows language designers to come up with new syntax, which enriches developer experience but does not break the web. Every new feature needs to be translatable to the old syntax. This way, so-called transpilers can be used to convert code to older versions.

I speak JavaScript, translate, please!

While compilers compile from a higher-level language to a lower-level language, a transpiler or transcompiler acts more like a converter. It is a source-to-source compiler that translates code to run in a different interpreter.

Recently, there's been a real battle among new languages that are transpiled to JavaScript and can run in the browser. I used Google Dart for quite some time, and I must admit, I really loved the language features. The problem with nonstandardized languages is that they depend heavily on community adoption and the hype. Also, it's almost certain that they will never run natively within the browser. This is also the reason why I prefer standard JavaScript, and the JavaScript of the future using transpilers and polyfills.

Some people argue that transpilers introduce code that isn't very performant and, therefore, recommend that you do not use ECMAScript 6 and transpilers at all. I don't agree with this for many reasons. Usually, this is about performance in micro or even nanosecond areas where this often really does not matter for most applications.

I don't mean that performance doesn't matter, but performance always needs to be discussed within a context. If you're trying to optimize a loop within your application by reducing processing time from 10 microseconds to five microseconds where you'd never iterate over more than 100 items, then you're probably spending your time on the wrong things.

Also, a very important fact is that transpiled code is designed by people who understand micro performance optimization much better than I do, and I'm sure their code runs faster than mine. On top of this, a transpiler is probably also the right place where you'd want to do performance optimization because this code is automatically generated and you don't lose maintainability of your code through performance quirks.

I'd like to quote Donald Knuth here and say that premature optimization is the root of all evil. I really recommend that you read his paper on this topic (Donald Knuth, December 1974, Structured Programming with go to Statements). Just because the goto statements got banished from all modern programming languages, it doesn't mean this is less of a good read.

Later on in this chapter, you'll learn about tools that help you use transpilers easily within your project, and we'll take a look at the decisions and directions Angular went with regarding their source code.

Let's look at a few language features that come with ECMAScript 6 and make our life much easier.

Classes

Classes were among one the most requested features in JavaScript, and I was one of the people voting for it. Well, coming from an OOP background and being used to organizing everything within classes, it was hard for me to let go. Although, after working with modern JavaScript for some time, you'll reduce their use to the bare minimum and to exactly what they are made for—inheritance.

Classes in ECMAScript 6 provide you with syntactic sugar to deal with prototypes, constructor functions, super calls, and object property definitions in a way that you have the illusion that JavaScript could be a class-based OOP language:

class Fruit { 
constructor(name) { this.name = name; }
}
const apple = new Fruit('Apple');

As we learned in the previous topic about transpilers, ECMAScript 6 can be de-sugared to ECMAScript 5. Let's take a look at what a transpiler will produce from this simple example:

function Fruit(name) { this.name = name; } 
var apple = new Fruit('Apple');

This simple example can easily be built using ECMAScript 5. However, once we use the more complex features of class-based object-oriented languages, the de-sugaring gets quite complicated.

ECMAScript 6 classes introduce simplified syntax to write class member functions (static functions), the use of the super keyword, and inheritance using the extends keyword.

If you would like to read more about the features in classes and ECMAScript 6, I highly recommend that you read the articles of Dr. Axel Rauschmayer (http://www.2ality.com/).

Modules

Modules provide a way to encapsulate your code and create privacy. In object-oriented languages, we usually use classes for this. However, I actually believe that this is an anti-pattern rather than a good practice. Classes should be used where inheritance is desired and not just to structure your code.

I'm sure that you've encountered a lot of different module patterns in JavaScript already. One of the most popular ones that creates privacy using a function closure of an immediately invoked function expression (IIFE) is probably the revealing module pattern. If you'd like to read more about this and maybe other great patterns, I recommend the book Learning JavaScript Design Patterns, by Addy Osmani.

Within ECMAScript 6, we can now use modules to serve this purpose. We simply create one file per module, and then we use the import and export keywords to connect our modules together.

Within the ECMAScript 6 module specification, we can actually export as many things as we like from each module. We can then import these named exports from any other module. We can have one default export per module, which is especially easy to import. Default exports don't need to be named, and we don't need to know their name when importing them:

import SomeModule from './some-module.js'; 
var something = SomeModule.doSomething();
export default something;

There are many combinations on how to use modules. We will discover some of these together while working on our task management application during the upcoming chapters. If you'd like to see more examples on how to use modules, I can recommend the Mozilla Developer Network documentation (https://developer.mozilla.org) on the import and export keywords.

Template strings

Template strings are very simple, but they are an extremely useful addition to the JavaScript syntax. They serve three main purposes:

  • Writing multiline strings
  • String interpolation
  • Tagged template strings

Before template strings, it was quite verbose to write multiline strings. You needed to concatenate pieces of strings and append a new-line character yourself to the line endings:

const header = '<header>\n' + 
' <h1>' + title + '</h1>\n' +
'</header>';

Using template strings, we can simplify this example a lot. We can write multiline strings, and we can also use the string interpolation functionality for our title variable that we used to concatenate earlier:

const header = ` 
<header>
<h1>${title}</h1>
</header>
`;

Note the back ticks instead of the previous single quotes. Template strings are always written between back ticks, and the parser will interpret all characters in-between them as part of the resulting string. This way, the new-line characters present in our source file will also be part of the string automatically.

You can also see that we have used the dollar sign, followed by curly brackets to interpolate our strings. This allows us to write arbitrary JavaScript within strings and helps a lot while constructing HTML template strings.

You can read more about template strings on the Mozilla Developer Network.

TypeScript

TypeScript was created in 2012 by Anders Hejlsberg with the intention to implement the future standard of ECMAScript 6 but also to provide a superset of syntax and features that were not part of the specification.

There are many features in TypeScript that are a superset to the ECMAScript 6 standard, including, but not limited to the following:

  • Optional static typing with type annotations
  • Interfaces
  • Enum types
  • Generics

It's important to understand that all of the features that TypeScript provides as a superset are optional. You can write pure ECMAScript 6 and not take advantage of the additional features that TypeScript provides. The TypeScript compiler will still transcompile pure ECMAScript 6 code to ECMAScript 5 without any errors.

Most of the features that are seen in TypeScript are actually already present in other languages, such as Java and C#. One goal of TypeScript was to provide language features that support workflows and better maintainability for large-scale applications.

The problem with any nonstandard language is that nobody can tell how long the language will be maintained and how fast the momentum of the language will be in the future. In terms of support, the chances are high that TypeScript, with its sponsor, Microsoft, will actually have a long life. However, there's still no guarantee that the momentum and trend of the language will keep moving at a reasonable pace. This problem does obviously not exist for standard ECMAScript 6 because it's what the web of the future is made of and what browsers will speak natively.

Still, there are valid reasons to use the extended features of TypeScript if you'd want to address the following concerns that clearly outweigh the negative implications of an uncertain future in your project:

  • Large applications that undergo a huge amount of changes and refactoring
  • Large teams that require a strict governance while working on code
  • Creation of type-based documentation which would otherwise be difficult to maintain

Today's version of Angular is purely based on TypeScript and therefore it's your best option if you're starting to use Angular as your framework. There are also ways to use Angular with pure ECMAScript even without using a transpiler, however, you'll be missing some great language features and support.

Within this book, we're using TypeScript for all examples as well as to create our task management system. Most of the features we're going to be using have already been or will be explained to you within this chapter. The typing system of TypeScript is pretty self-explanatory, however, if you'd like to know more about TypeScript and its features, I highly recommend that you visit the TypeScript documentation on their official website: https://www.typescriptlang.org.

History with TypeScript in Angular

When the Angular project was developed, it was important for the core team to include the best language support they could get. While evaluating different languages, they have actually considered Google Dart and TypeScript as potential candidates to implement the framework. However, there was one major feature missing in the superset which TypeScript provided. Let's look again at our first Angular component, which we wrote in a previous section:

@Component({ 
selector: 'hello-world',
template: '<div>Hello World</div>'
})
class HelloWorld {}

An Angular component always consists of an EMCAScript 6 class as well as the @Component decorator which is used to configure our component. At the time when Google started developing the Angular project, there was no such thing as the ECMAScript 7 decorator proposal and TypeScript did not support something similar. Still, the Angular team didn't want to miss out on such a great language feature which can simplify and ease the use of their framework API. This marked the birth of AtScript. AtScript was created by the Angular core team as a fork of TypeScript which added the possibility to write meta annotations using an at symbol. At the same time, the ECMAScript 7 decorator proposal was created to propose a similar feature to the JavaScript standard. It was only a few months later with TypeScript's Version 1.5 that Microsoft announced that they would include experimental support for decorators in the TypeScript transpiler.

Today, Angular has switched completely to TypeScript and AtScript as well as Dart, which is no longer supported in the core project. They have changed their code to run with the experimental decorator support of TypeScript and no longer rely on a custom solution.

From this rather long-winded history, you can get that the Angular core team has fought hard to be able to use a decorator language feature. And they have succeeded. Given the importance of this feature, we'll talk a bit about the possibilities we have with ECMAScript 7 decorators within the next section.

Decorators

Decorators are not part of the ECMAScript 6 specification, but they were proposed to the ECMAScript 7 standard for 2016. They provide us with a way to decorate classes and properties during design time. This allows a developer to use meta-annotations while writing classes, and declaratively attach functionality to the class and its properties.

Decorators are named after the decorator pattern that was initially described in the book Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma and his colleagues, also known as the Gang of Four (GoF).

The principle of decoration is that an existing procedure is intercepted and the decorator has the chance to either delegate, provide an alternative procedure, or do a mix of both:

Visualization of decoration in a dynamic environment with the example of a simple access procedure

Decorators in ECMAScript 7 can be used to annotate classes and class properties. Note that this also includes class methods, as class methods are also properties of the class prototype object. Decorators get defined as regular functions, and they can be attached to classes or class properties with the at symbol. Our decorator function will then be called with contextual information about the location of inclusion every time that the decorator is placed.

Let's take a look at a simple example that illustrates the use of a decorator:

function logAccess(obj, prop, descriptor) { 
const delegate = descriptor.value;
descriptor.value = function() {
console.log(`${prop} was called!`);
return delegate.apply(this, arguments);
};
}

class MoneySafe {
@logAccess
openSafe() {
this.open = true;
}
}

const safe = new MoneySafe();
safe.openSafe(); // openSafe was called!

We have created a logAccess decorator that will log all function calls that are tagged with the decorator. If we look at the MoneySafe class, you can see that we have decorated the openSafe method with our logAccess decorator.

The logAccess decorator function will be executed for each annotated property within our code. This enables us to intercept the property definition of the given property. Let's take a look at the signature of our decorator function. Decorator functions that are placed on class properties will be called with the target object of the property definition as a first parameter. The second parameter is the actual property name that is defined, followed by the last parameter, which is the descriptor object that is supposed to be applied to the object.

The decorator gives us the opportunity to intercept the property definition. In our case, we use this ability to exchange the descriptor value (which is the annotated function) with a proxy function that will log the function call before calling the origin function (delegation). For simplification purposes, we've implemented a very simple yet incomplete function proxy. For real-world scenarios, it would be advisable to use a better proxy implementation, such as the ECMAScript 6 proxy object.

Decorators are a great feature to leverage aspect-oriented concepts and declaratively add behavior to our code at design time.

Let's look at a second example where we use an alternative way to declare and use decorators. We can treat decorators like function expressions where our decorator function is rewritten as a factory function. This form of usage is especially useful when you need to pass along configuration to the decorator, which is made available in the decorator factory function:

function delay(time) { 
return function(obj, prop, descriptor) {
const delegate = descriptor.value;
descriptor.value = function() {
const context = this;
const args = arguments;
return new Promise(function(success) {
setTimeout(function() {
success(delegate.apply(context, arguments));
}, time);
});
};
};
}

class Doer {
@delay(1000)
doItLater() {
console.log('I did it!');
}
}

const doer = new Doer();
doer.doItLater(); // I did it! (after 1 second)

We have now learned how ECMAScript 7 decorators can help you write declarative code that has an aspect-oriented twist to it. This simplifies development a lot because we can now think of behavior that we add to our classes during design time when we actually think about the class as a whole and write the initial stub of the class.

Decorators in TypeScript are slightly different than the decorators from ECMAScript 7. They are not limited to classes and class properties, but they can be placed on parameters within the class methods. This allows you to annotate function parameters, which can be useful in some cases:

class TypeScriptClass { 
constructor(@ParameterDecorator() param) {}
}

Angular uses this feature to simplify dependency injection on class constructors. As all directive, component, and service classes get instantiated from Angular dependency injection and not by us directly, these annotations help Angular find the correct dependencies. For this use case, function parameter decorators actually make a lot of sense.

Currently, there are still issues with the implementation of decorators on class method parameters, which is also why ECMAScript 7 does not support it. The TypeScript compiler has worked around this issue but is currently not compliant to the ECMAScript 7 proposal.