Mastering Angular Components - Second Edition

4.8 (6 reviews total)
By Gion Kunz
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Component-Based User Interfaces

About this book

Angular framework embraces a mature user interface (UI) component architecture, a powerful tool when developing scalable application interfaces. The simple and deterministic design of Angular components supports you in building large and scalable component-based applications.

Mastering Angular Components covers a new holistic way of thinking about UI development, and helps you discover the power of Angular 6 components through various examples. The book starts with an introduction to component-based user interfaces and how Angular 6 manages this concept. Following a step-by-step approach, you will build a fully functional task-management application using Angular. You’ll start with basic layout components, adding some core task-management components before completing the first draft of your application. You will then move on to working through a refactoring exercise in order to learn how to enhance the way you deal with states and data. Once you have got to grips with all this, you will study how to make use of the Angular router within your application.

By the end of this book, you will be able to constantly refine the functionality of your application and create components that render SVG graphics, add a time-tracking feature, visualize data using third-party library Chartist, and even create a plugin architecture using Angular components.

Publication date:
July 2018
Publisher
Packt
Pages
402
ISBN
9781788293532

 

Chapter 1. Component-Based User Interfaces

Although we'll cover a lot of Angular-related topics in this book, the focus will be mainly on creating component-based user interfaces. It's one thing to understand a framework like Angular, but it's a whole different thing to establish an effective workflow using a component-based architecture. In this book, I'll try to explain the core concepts behind Angular components and how we can leverage this architecture to create modern, efficient, and maintainable user interfaces.

Besides learning all the necessary concepts behind Angular, together, we will create a task-management application from scratch. This will allow us to explore different approaches to solve common UI problems using the component system that is provided by Angular.

In this chapter, we will take a look at how component-based user interfaces help us build greater applications. Over the course of this book, we will build an Angular application together, where we will use the component-based approach to its full potential. This chapter will also introduce you to the technologies that are used in this book.

The topics that we will cover in this chapter are as follows:

  • An introduction to component-based user interfaces
  • Encapsulation and composition using component-based user interfaces
  • Evolution of UI frameworks
  • The standard web components
  • An introduction to the Angular component system
  • Writing your first Angular component
  • Basics of NgModule
  • An overview and history of ECMAScript and TypeScript
  • ECMAScript 7 decorators as meta annotations
  • An introduction to Node.js-based tooling using Angular CLI
 

Thinking in components


Today's user interfaces do not consist of just a bunch of form elements that are cobbled together onto a screen. Modern users experience designing innovative visual presentations of interactive content challenges technology more than ever.

Sadly, we almost always tend to think in pages when we flesh out concepts for web applications, such as the pages within a printed book. Thinking about a book, this is probably the most efficient way to convey information for this kind of content and medium. You can skim through the pages one by one without any real physical effort, read paragraph by paragraph, and just scan through the chapters that you don't find interesting.

The problem with thinking in pages too much is that this concept, which is borrowed from books, does not really translate well to how things work in the real world. The world is created from components that form a system of components together.

Take our bodies as an example. We mostly consist of independent organs that interact with each other using electrical and chemical signals. Organs themselves consist of proteins that, on their own work, like machines to form a system. Down to the molecules, atoms, protons, and quarks, we can't really tell where one starts and where it ends. What we can tell for sure is that it's all about systems of components with inter-dependencies, and it is not about pages.

Modern user interfaces are very much like the real world consisting of systems of components. If, where, and how they are distributed to pages is subordinate while designing them. Also, they should work independently, and they should interact with each other on an interdependent level.

 

Components – the organs of user interfaces


"We're not designing pages, we're designing systems of components."                                                                                                          - Stephen Hay

This quote by Stephen Hay from BDConf in Orlando 2012 brings it to the point. Interface design is really not about pages. To create efficient user interfaces for not only the users but also the developers who maintain them, we need to think in systems of components. Components are independent, but they can interact with each other and create larger components when they are arranged together. We need to look at user interfaces holistically, and using components enables us to do this.

In the following topics, we're going to explore a few fundamental aspects of components. Some of these are already known from other concepts, such as object-oriented programming (OOP), but they appear in a slightly different light when thinking about components.

Encapsulation

Encapsulation is a very important factor when thinking about maintenance in a system. Having a classical OOP background, I've learned that encapsulation means bundling logic and data together into an isolated container. This way, we can operate on the container from the outside and treat it like a closed system.

There are many positive aspects of this approach when it comes to maintainability and accessibility. Dealing with closed systems is important for the organization of our code. However, this is even more important because we can organize ourselves while working with code:

Organizing a system in encapsulated components allows us to reason about it much more easily

I have a pretty bad memory, and it's very important for me to find the right focus level when working on code. Immediate memory research has tells us that the human brain can remember about seven items at once on average. Therefore, it's crucial for us to write code in such a way that it allows us to focus on fewer and smaller pieces at once.

A clear encapsulation helps us in organizing our code. We can perhaps forget all the internals of the closed system and about the kind of logic and data that we've put into it. We should focus only on its surface, which allows us to work on a higher-abstraction level. Similar to the previous diagram, without using a hierarchy of encapsulated components, we'd have all our code cobbled together on the same level.

Encapsulation encourages us to isolate small and concise components and build a system of components. During development, we can focus on the internals of one component and only deal with the interface of other components.

Sometimes, we forget that all the organization of the coding we actually perform is for ourselves and not for the computer that runs this code. If this was for the computer, then we would probably all start writing in machine language again. A strong encapsulation helps us access specific code easily, focus on one layer of the code, and trust the underlying implementations within capsules.

The following TypeScript example shows you how to use encapsulation to write maintainable applications. Let's assume that we are in a T-shirt factory, and we need some code to produce T-shirts with a background and foreground color. This example uses some language features of TypeScript. If you're not familiar with the language features of TypeScript, don't worry too much at this point. We will learn about these later in this chapter:

// This class implements data and logic to represent a color 
// which establishes clean encapsulation. 
class Color { 
  constructor(private red: number, private green: number, private blue: number) {} 

  // Using this function we can convert the internal color values 
  // to a hex color string like #ff0000 (red). 
  getHex(): string { 
    return '#' + Color.getHexValue(this.red) + Color.getHexValue(this.green) + 
      Color.getHexValue(this.blue); 
  } 

  // Static function on Color class to convert a number from 
  // 0 to 255 to a hexadecimal representation 00 to ff 
  static getHexValue(number): string { 
    const hex = number.toString(16); 
    return hex.length === 2 ? hex : '0' + hex; 
  } 
} 

// Our TShirt class expects two colors to be passed during 
// construction that will be used to render some HTML 
class TShirt { 
  constructor(private backgroundColor: Color, private foregroundColor: Color) {} 

  // Function that returns some markup which represents our T-Shirts 
  getHtml(): string { 
    return ` 
      <t-shirt style="background-color: ${this.backgroundColor.getHex()}"> 
        <t-shirt-text style="color: ${this.foregroundColor.getHex()}"> 
          Awesome Shirt! 
        </t-shirt-text> 
      </t-shirt> 
    `; 
  } 
} 

// Instantiate a blue colour 
const blue: Color = new Color(0, 0, 255); 
// Instantiate a red color 
const red: Color = new Color(255, 0, 0); 
// Create a new shirt using the above colours 
const awesomeShirt: TShirt = new TShirt(blue, red); 
// Adding the generated markup of our shirt to our document 
document.body.innerHTML = awesomeShirt.getHtml(); 

Using a clean encapsulation, we can now work with the abstraction of color in our T-shirt. We don't need to worry about how to calculate the hexadecimal representation of colors at the T-shirt level because this is already done by the Color class. This makes your application maintainable and keeps it very open for change.

I strongly recommend that you read about the SOLID principles if you haven't done so already. As the name already suggests, this assembly of principles is a solid power tool that can change the way you organize code tremendously. You can learn more about the SOLID principles in the book Agile Principles, Patterns, and Practices, by Robert C. Martin.

Composability

Composition is a special kind of reusability. You don't extend an existing component, but you create a new, larger component by composing many smaller components together into a system of components.

In OOP languages, composition is often used to get around the multiple inheritance issues that most OOP languages have. Subclass polymorphism is always great until you reach the point where your design does not match the latest requirements in your project. Let's look at a simple example that illustrates this problem.

You have a Fisher class and a Developer class, both of which hold specific behaviors. Now, you'd want to create a FishingDeveloper class that inherits from both Fisher and Developer. Unless you're using a language that supports multiple inheritance (such as C++, which does this to a certain extent), you will not be able to reuse this functionality using inheritance. There is no way to tell the language that your new class should inherit from both superclasses. Using composition, you can easily solve this problem. Instead of using inheritance, you're composing a new FishingDeveloper class that delegates all behavior to an internal Developer and Fisher instance:

interface IDeveloper {
  code(): void;
}

interface IFisher {
  fish(): void;
}

class Developer implements IDeveloper { 
 constructor(private name: string) {}

 code(): void { 
   console.log(`${this.name} writes some code!`); 
 } 
} 

class Fisher implements IFisher {
 constructor(private name: string) {}

 fish(): void { 
   console.log(`${this.name} catches a big fish!`); 
 } 
} 

class FishingDeveloper implements IFisher, IDeveloper { 
 constructor(private name: string) { 
   this.name = name; 
   this.developerStuff = new Developer(name); 
   this.fisherStuff = new Fisher(name); 
 } 

 code(): void { 
   this.developerStuff.code(); 
 } 

 fish(): void { 
   this.fisherStuff.fish(); 
 } 
} 

var bob: FishingDeveloper = new FishingDeveloper('Bob'); 
bob.code(); 
bob.fish(); 

Experience has taught us that composition is probably the most efficient way to reuse code. In contrast to inheritance, decoration, and other approaches to gain reusability, composition is probably the least intrusive and the most flexible.

Recent versions of some languages also support a pattern called traits, that is, mixins. Traits allow you to reuse certain functionality and attributes from other classes in a way that is similar to multiple inheritance.

If we think about the concept of composition, it's nothing more than designing organisms. We have the two Developer and Fisher organisms, and we unify their behaviors into a single FishingDeveloper organism.

Components, invented by nature

Components, embracing encapsulation, and composition are an effective way to build maintainable applications. Composed from components, applications are very resistant to the negative implications of change, and change is a necessary thing that will happen to every application. It's only a matter of time until your design will be challenged by the effects of change; therefore, it's very important to write code that can handle change as smoothly as possible.

Nature is the best teacher. Almost all the achievements in technological developments have their origin in observations of how nature solves problems. If we look at evolution, it's an ongoing redesign of matter by adapting to outer forces and constraints. Nature solves this by constant change using mutation and natural selection.

If we project the concept of evolution onto developing an application, we can say that nature does actually refactor its code in every single moment. This is actually the dream of every product manager—an application that can undergo constant change but does not lose any of its efficiency.

I believe that there are two key concepts that play a major role in nature that allows it to apply constant change in its design without losing much efficiency. This uses encapsulation and composition. Coming back to the example of our bodies, we can actually tell that our organs use a very clear encapsulation. They use membranes to create isolation, veins to transport nutrition, and synapses to send messages. Also, they have interdependencies, and they communicate using electrical and chemical messages. Most obviously, they form larger systems, which is the core concept of composition.

Of course, there are many other factors, and I'm not a professor in biology. However, I think it's a fascinating thing to see that we have learned to organize our code very similarly to how nature organizes matter.

The idea of creating reusable UI components is quite old, and it was implemented in various languages and frameworks. One of the earliest systems that used UI components was probably the Xerox Alto system back in the 1970s. It used reusable UI components that allowed developers to create an application by composing them on a screen where users could interact with them:

The user interface of file manager on the Xerox Alto system from the 1970s

Early frontend UI frameworks, such as DHTMLX, Ext JS, or jQuery UI implemented components in a more limited fashion that didn't provide great flexibility or extensibility. Most of these frameworks just provided widget libraries. The problem with UI widgets is that they mostly don't embrace the pattern of composition enough. You can arrange widgets on a page and they provide encapsulation, but with most toolkits, you can't create larger components by nesting them inside each other. Some toolkits solve this by providing a special kind of widget which was mostly called a container. However, this is not the same as a full-fledged component tree that allows you to create systems within systems. Containers were actually meant to provide a visual layout container rather than a composite container to form a larger system.

Usually, when working with widgets on a page of our application, we'd have a large controller that controls all these widgets, user input, and states. However, we are left with two levels of composition, and there's no way that we can structure our code more granularly. There is the page and there are the widgets. Having a bunch of UI widgets is simply not enough, and we are almost back to the state where we create pages plastered with form elements.

I've been a user of JavaServer Faces for years, and besides all its problems, the concept of having reusable custom elements was groundbreaking. Using XHTML, one could write so-called composite components that consisted of other composite components or native HTML elements. A developer could gain a fantastic level of reusability using composition. In my view, the big issue with this technology was that it did not address the concerns in the frontend enough to become really usable for complex user interactions. In fact, a framework like this should live completely within the frontend.

My UI framework wishlist

Usually, when UI frameworks get compared, they get measured against each other based on metrics, such as widget count, theming capabilities, and asynchronous data retrieval features. Each framework has its strengths and weaknesses, but leaving all the extra features aside and reducing it to the core concerns of a UI framework, I only have a few metrics left that I'd like to be assessed. These metrics are, of course, not the only ones that are important in today's UI development, but they also are the main factors toward building a clean architecture that supports the principle of change:

  • I can create encapsulated components with clear interfaces
  • I can create larger components by using composition
  • I can make components interact with each other within their hierarchy

If you're looking for a framework which enables you to take full advantage of component-based UI development, you should look for these three key measures.

First of all, I think it's very important to understand the main purpose of the web and how it evolved. If we think of the web in its early days in the 1990s, it was probably only about hypertext. There were very basic semantics that could be used to structure information and display them to a user. HTML was created to hold structure and information. The need for custom visual presentation of information led to the development of CSS right after HTML started being widely used.

It was in the mid 1990s when Brendan Eich invented JavaScript, and it was first implemented in Netscape Navigator. By providing a way to implement behavior and state, JavaScript was the last missing piece for a full web customization:

Technology

Concern

HTML

Structure and information

CSS

Presentation

JavaScript

Behavior and state

 

We have learned to keep these concerns as separate as possible in order to maintain a clean architecture. Although there are different opinions on this and some recent technologies also move away from this principle, I believe that a clean separation of these concerns is very important to create a maintainable application.

Leaving this view aside, the standard definition of encapsulation from OOP is just concerned about coupling and isolation of logic and data. This probably applies well to classic software components. However, as soon as we consider a user interface as part of an architecture, there is a new dimension that is added.

Classical MVC frameworks are view centric, and developers organize their code based on pages. You'll probably go ahead and create a new view that represents a page. Of course, your view needs a controller and model, so you'll also create them. The problem with organization by pages is that there's little to no gain of reusability. Once you've created a page and you'd like to reuse only some parts of the page, you will need a way to encapsulate only a specific part of this model—the view and the controller.

UI components solve this problem nicely. I like to see them as a modular approach to MVC. Although they still embrace the MVC pattern, they also establish encapsulation and composability. This way, a view is a component itself, but it also consists of components. By composing views of components, one can gain a maximum amount of reusability:

UI components embrace MVC, but they also support encapsulation and composition on a much lower level

Technically, there are some challenges when implementing components with web technologies. JavaScript was always flexible enough to implement different patterns and paradigms. Working with encapsulation and composition isn't an issue at all, and the controlling part and the model of components can easily be implemented. Approaches, such as the revealing module pattern, namespaces, prototypes, or the recent ECMAScript 6 modules, provide all the tools that are needed from the JavaScript side.

However, for the view part of our components, we face some limitations. Although HTML supports great flexibility in terms of composability because the DOM tree is nothing else than a big composition, we have no way to reuse these compositions. We can only create one large composition, which is the page itself. HTML being only the final view that was delivered from the server, this was never really a real concern. Today's applications are much more demanding, and we need to have a fully-encapsulated component running in the browser, which also consists of a partial view.

We face the same problem with CSS. There is no real modularization and encapsulation while writing CSS, and we need to use namespaces and prefixes in order to segregate our CSS styles. Still, the whole cascading nature of CSS can easily destroy all encapsulation that we try to bring in place using CSS structuring patterns.

Time for new standards

Web standards have been evolving immensely in the last couple of years. There are so many new standards, and the browser became such a big multimedia framework, that it's hard for other platforms to compete with this.

I'd even go as far as to say that web technology will actually replace other frameworks in the future, and it probably will be renamed to multimedia technology or something similar. There's no reason why we need to use different native frameworks to create user interfaces and presentations. Web technologies embed so many features that it's hard to find a reason not to use them for any kind of application. Just look at the Firefox OS or the Chrome OS, which are designed to run with web technologies. I think it's only a matter of time until more operating systems and embedded devices make use of web technologies to implement their software. This is why I believe that at some point it will be questionable whether the term web technologies is still appropriate or whether we should replace it with a more general term.

Although we usually just see new features appear in browsers, there is a very open and long-winded standardization process behind them. It's very important to standardize features, but this takes a lot of time, especially when people disagree about different approaches to solving problems.

Coming back to the concept of components, this is something where we really need support from web standards to break the current limitations. Fortunately, the W3C (World Wide Web Consortium) thought the same, and a group of developers started to work on specifications under the hood of an umbrella specification called web components.

The following topics will give you a brief overview over two specifications that also play a role in Angular components. One of Angular's core strengths is that it acts more like a superset of web standards rather than being a complete isolated framework.

Template elements

Template elements allow you to define regions within your HTML, which will not be rendered by the browser. You can then instantiate these document fragments with JavaScript and then place the resulting DOM within your document.

While the browser is actually parsing the template content, it only does so in order to validate the HTML. Any immediate actions that the parser would usually execute will not be taken. Within the content of template elements, images will not be loaded and scripts won't be executed. Only after a template is instantiated will the parser take the necessary actions, as follows:

<body> 
<template id="template"> 
  <h1>This is a template!</h1> 
</template> 
</body> 

This simple HTML example of a template element won't display the heading on your page. As the heading is inside a template element, we first need to instantiate the template and add the resulting DOM into our document:

var template = document.querySelector('#template'); 
var instance = document.importNode(template.content, true); 
document.body.appendChild(instance); 

Using these three lines of JavaScript, we can instantiate the template and append it into our document.

Note

Template elements are used by Angular in order to instantiate dynamic parts of your user interface. This will be the case while conditionally rendering parts of your template using the ngIf directive, or by repeating a template using the ngFor directive.

Shadow DOM

This part of the web components specification was the missing piece to create proper DOM encapsulation and composition. With shadow DOM, we can create isolated parts of the DOM that are protected against regular DOM operations from the outside. Also, CSS will not reach into shadow DOM automatically, and we can create local CSS within our component.

Note

If you add a style tag inside shadow DOM, the styles are scoped to the root within the shadow DOM, and they will not leak outside. This enables a very strong encapsulation for CSS.

Content insertion points make it easy to control content from the outside of a shadow DOM component, and they provide some kind of an interface to pass in content.

At the time of writing this book, shadow DOM is supported by most browsers, although it still needs to be enabled in Firefox.

 

Angular's component architecture


For me, the concept of directives from the first version of Angular changed the game in frontend UI frameworks. This was the first time that I felt that there was a simple yet powerful concept that allowed the creation of reusable UI components. Directives could communicate with DOM events or messaging services. They allowed you to follow the principle of composition, and you could nest directives and create larger directives that solely consisted of smaller directives arranged together. Actually, directives were a very nice implementation of components for the browser.

In this section, we'll look into the component-based architecture of Angular and how the things we've learned about components will fit into Angular.

Everything is a component

As an early adopter of Angular and while talking to other people about it, I got frequently asked what the biggest difference is to the first version. My answer to this question was always the same. Everything is a component:

Within the Angular architecture, a component is a directive with an additional view

For me, this paradigm shift was the most relevant change that both simplified and enriched the framework. Of course, there are a lot of other changes with Angular. However, as an advocate of component-based user interfaces, I've found that this change is the most interesting one. Of course, this change also came with a lot of architectural changes.

Angular supports the idea of looking at the user interface holistically and fosters composition with components. However, the biggest difference to its first version is that now, your pages are no longer global views; they are simply components that are assembled from other components. If you've been following this chapter, you'll notice that this is exactly what a holistic approach to user interfaces demands. No more pages, but systems of components.

Note

Angular still uses the concept of directives, although directives are now really what the name suggests. They are orders for the browser to attach a given behavior to an element. Components are a special kind of directive that come with a view.

 

Your first component


Keeping up the tradition, before we start building a real application together, we should write our first hello world component with Angular:

import {Component} from '@angular/core';

@Component({ 
  selector: 'hello-world', 
  template: '<div>Hello {{name}}</div>' 
}) 
class HelloWorldComponent {
  name: string = 'World';
}

This is already a fully-working Angular component. We used ECMAScript 6 classes to create the necessary encapsulation required for a component. You can also see a meta-annotation that is used to declaratively configure our component. This statement, which looks like a function call that is prefixed with an at symbol, actually comes from the ECMAScript 7 decorator proposal. For the moment, you can think of decorators as a way to attach metadata to our component class. 

Note

ECMAScript 7 decorators are still very experimental at the time of writing this book. We're using TypeScript in the examples of this book, which is already implementing the decorator proposal with a slight twist. The Angular core team has decided to go with this experimental technology, since it reduces the overall amount of code and introduces an aspect oriented flavor to the Angular API.

It's important to understand that an element can only be bound to one single component. As a component always comes with a view, there is no way that we can bind more than one component to an element. On the other hand, an element can be bound to many directives, as directives don't come with a view—they only attach behavior.

In the Component decorator, we need to configure everything that is relevant to describe our component for Angular. This, of course, also includes our template for the view. In the preceding example, we are specifying our template directly within JavaScript as a string. We can also use the templateUrl property to specify a URL where the template should be loaded from.

The second configuration, applied using the selector property, allows us to specify a CSS selector, which is used by Angular to attach the component to certain elements within our view. Every time Angular encounters an element which matches the component's selector, it will render the given component into that element.

Now, let's enhance our example a little bit so that we can see how we can compose our application from smaller components:

import {Component} from '@angular/core';

@Component({ 
  selector: 'shout-out', 
  template: '<strong>{{words}}</strong>' 
}) 
class ShoutOutComponent { 
  @Input() words: string; 
} 

@Component({ 
  selector: 'hello-world' 
  template: '<shout-out words="Hello, {{name}}!"></shout-out>'
}) 
class HelloWorldComponent { 
  name: string = 'World';
}

You can see that we have now created a small component that allows us to shout out words as we like. In our Hello World application, we make use of this component to shout out Hello, World!

Within the template of our hello world component, we are including the shouting component by placing an HTML element which matches the CSS element selector of the shouting component.

Over the course of this book and while writing our task management application, we will learn a lot more about the configuration and implementation of components. However, before we start with this in the Chapter 2Ready, Set, Go!, we should take a look at some tools and language features that we'll use during this book.

Angular NgModule

Organizing an application solely by composing components comes with some challenges. Angular supports the concept of application modules, which essentially are just containers around components that help structure your application.

The concept of NgModule was introduced to mainly solve the following issues:

  • Explicit template parsing: With the use of modules and by declaring all components, directives, pipes, and providers which are used inside of your application module, Angular is able to parse HTML templates very explicitly. This is really helpful when it comes to debugging. Let's say you're including an element within one of your component templates which does not match any of the selectors specified by the components within your module. Angular can now assert an error because you explicitly told it what components are available within your module. Without telling Angular which components belong to your application module, it would not be able to know if you're including a non-existing component within your template.
  • Simpler dependency resolution: Since Angular can now simply resolve your main application module to find out what components are present within your application, things get simplified a lot. Imagine you have a very complex application existing of hundreds of components. Without modules, Angular would need to follow each individual component to find out how they are dependent on each other. With modules, Angular can simply check what components are declared inside of the module to find all components.
  • Lazy loading with the Angular router: The router of Angular is able to load parts of your application lazily when required. This is a very powerful feature, but it requires that you declare a bundle of application artefacts like components or directives, to be loaded asynchronously after your main application has started. NgModule comes in very handy at this point. By creating a separate module using NgModule, you can now define a part of your application consisting of new components and other artefacts. Within the build of your application, this module is then built separately into its own JavaScript resource, which can be loaded asynchronously at runtime by the router.

Your application requires at least one main module, which is declaring all your application components. Let's look at a very simple example and build the main module for our HelloWorld component:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {HelloWorldComponent} from './hello-world.component';
import {ShoutOutComponent} from './shout-out.component';

@NgModule({
  declarations: [HelloWorldComponent, ShoutOutComponent],
  imports: [BrowserModule],
  bootstrap: [HelloWorldComponent]
})
export class HelloWorldAppModule { }

Similar to a component definition, we're using an ES6 class and a decorator to define an Angular module. The NgModule decorator of our main application module has three configuration properties:

A module dependency tree: Module A importing module B and C so that all components are available to module A

The declarations property is used to tell Angular what components, directives, and pipes are present within this module. If our application consists of 30 components, we need to add them all to the declarations of the NgModule. Every time you create a new component, you will also need to add it to the declarations array within your application module.

Within the array of the imports property, we can tell Angular to import other NgModule. This way, you can compose your application modules from many smaller modules if you like. However, there's no real benefit of structuring your application into submodules, unless you're exporting a submodule as a library, or if you're using the lazy loading feature of the router, as discussed earlier. It's crucial that you always import the Angular BrowserModule within your main application module. The BrowserModule contains all the Angular core components, directives, and other dependencies which are required in order to run your application within a browser environment.

Finally, the bootstrap property is telling Angular which of your components should be rendered first. You should specify your main application component here, which is representing the root component of your application. In the second chapter of this book, we'll take a closer look at the bootstrapping mechanism of Angular.

 

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.

Note

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.

Note

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.

Note

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.

Note

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.

 

Tools


In order to make use of all these future technologies, we need some tools to support us. We were already talking about ECMAScript 6 and decorators, where we actually prefer TypeScript decorators, as they support the constructor parameter decorators that are used by Angular. Although the ECMAScript 6 syntax supports modules, we still need some sort of module loader that will actually load the required modules in the browser or help us generate an executable bundle.

Node.js and npm

Node.js is JavaScript on steroids. Initially, a fork of the V8 JavaScript engine from the Google Chrome browser, Node.js was extended with more functionality, specifically to make JavaScript useful on the server-side. File handling, streams, system APIs, and a huge ecosystem of user-generated packages are just some of the facts that make this technology an outstanding partner for your web development.

The node package manager, NPM, is a door to over 200,000 packages and libraries that can help you build your own application or library. The Node.js philosophy is very similar to the UNIX philosophy, where packages should stay small and sharp, but they should use composition to achieve greater goals.

To build our application, we will rely on Node.js as the host for the tools that we're going to use. We should, therefore, make sure that we install Node.js on our machine so that we are prepared for the next chapter, where we will start to craft our task management application.

Note

The code within this book was written using Node.js 8.9.0. Please make sure you're installing an equivalent Node.js version on your system. You can get Node.js from their website at https://nodejs.org, and it should be a breeze to install this on any kind of operating system by following the instructions on their website.

Once you've installed Node.js, we can perform a simple test to check whether everything is up and running. Open a Terminal console and execute the following command:

node -e "console.log('Hello World');"

Angular CLI

There are many ways to start out with a new Angular project. The most convenient one is probably to use the Angular CLI. The CLI, as the name already suggests, is a command-line interface to create new projects as well as new artefacts within an existing project.

The following instructions are guiding you through the creation of your first Angular project using the Angular CLI tool.

  1. Let's start by installing the Angular CLI on your system. Execute the following command on your command line:
npm install -g @angular/[email protected]
  1. After you've installed the Angular CLI tool, you can now use it to scaffold a new Angular project. You can access the tool executable by typing ng in your terminal. Let's open another Terminal window and create a new Angular project using the Angular CLI tool:
ng new my-first-app --prefix mac
  1. The previous step will take a while since all dependencies of your project need to be installed first. After completion, we can now use the CLI tool to start a local development server:
cd my-first-app
ng serve
  1. You can now launch your favourite browser and open up the address http://localhost:4200, where you should see the message welcome to mac.

Congratulations! You've just created your first Angular application using the Angular CLI tool! As I already told you, the convenience level of starting an Angular project like this is really great.

The CLI tool can be viewed as a scaffolding tool which helps you set up the necessary tooling as well as the structure of your project. Let's take a look at the most important features you'll get for free when you're using the CLI to give birth to your project:

  • TypeScript: Maybe obvious, but in order to use a transpiler, there will be many manual steps involved for you to set up the necessary tooling.
  • Webpack: This massive power tool is solving a lot of problems you probably haven't even though about yet. Along with TypeScript transpilation, its main concern is to load ECMAScript modules and provides you with a development server to preview and work on your project. Finally, it's also the tool which helps you to create an optimized bundled version of your project for production use.
  • Karma, Jasmine, and Protractor: This trio is unbeatable when it comes to testing! While Karma runs your executable specifications, Jasmin helps you write your tests. Protractor, on the other hand, can be used to create full end-to-end, integrational tests.

Note

You could also use the ECMAScript 5 style of writing Angular applications, which would allow you to develop your application right away without additional tooling. However, if you want to leverage the full potential of Angular, you should write your application in TypeScript rather than JavaScript. The Angular API is optimized to use features from future JavaScript versions and TypeScript, in order to provide the best ease of development.

Please go ahead and explore the source code that has been generated using the Angular CLI. Over the course of the chapters in this book, we will gain more in-depth knowledge, which will help you understand and put all of those pieces together. For the moment, we were just concerned about the installation of the Angular CLI and gave it a quick dry run.

 

Summary


In this chapter, we looked at a component-based approach to structure user interfaces. We talked about the necessary background to understand why we are moving in this direction with the web standard and frameworks, such as Angular. We also ensured that we are prepared with all the technology that we will use in the upcoming chapters in this book. You created your first simple Angular application using the Angular CLI tool. Now, we are ready to start building our task-management system using a component-based architecture to its full potential.

In the next chapter, we're going to start building our task management application using Angular components. We'll look at the initial steps that are required to create an Angular application from scratch and flesh out the first few components in order to build a task list.

About the Author

  • Gion Kunz

    Gion Kunz has over 12 years of experience in writing interactive user interfaces using JavaScript. He's worked with AngularJS since 2012, is one an early adopter of Angular 2 and loves to speak about Angular at conferences.

    In 2018 he founded his own company syncrea, where he helps customers create websites and applications using front-end web technologies. Besides working for his clients, Gion is a tutor at the SAE Institute in Zurich and loves to get his students enthusiastic about the Web.

    Browse publications by this author

Latest Reviews

(6 reviews total)
This is a fantastic work. The book explains Angular using a sample project. And the sample angular project details are explained in detail as well. But there is not much details about CSS styles.
Gion, write a good book, it easy to understand and structured source code example. This book teach step by step, so I can understand easily. It'll be best if the source code bundle have start and final code. Because I need to practice with start boiler plate maybe and review my code with final code. But after all, I like this book and teach me new technique and best practice Thank you for create an exellent book
Un buon libro, esauriente, completo

Recommended For You

Book Title
Unlock this book and the full library for only $5/m
Access now