Angular 2 Essentials - Sample Chapter
Angular 2 Essentials - Sample Chapter
$ 39.99 US
25.99 UK
P U B L I S H I N G
E x p e r i e n c e
D i s t i l l e d
Angular 2 Essentials
Your quick, no-nonsense guide to building real-world apps
with Angular 2
Sa
m
pl
C o m m u n i t y
Pablo Deeleman
Angular 2 Essentials
Angular 2
Essentials
ee
Pablo Deeleman
After getting his BA (Hons) degree in marketing and moving through different
roles in the advertising arena, he took his chance and evolved into a self-taught,
passionate UX designer and frontend developer with a crunch for beautifully crafted
CSS layouts and JavaScript thick clients, having produced countless interactive
designs and web desktop and mobile applications ever since.
During these years, he has fulfilled his career as both an UX designer and frontend
developer by successfully leading Internet projects for a wide range of clients and
teams, encompassing European online travel operators, Silicon Valley-based start-ups,
international heavy-traffic tube websites, global banking portals, or gambling and
mobile gaming companies, just to name a few. At some point along this journey, the
rise of Node.js and single-page-application frameworks became a turning point in his
career, being currently focused on building JavaScript-driven web experiences.
After having lived and worked in several countries, Pablo Deeleman currently lives
in Barcelona, Spain, where he leads the frontend endeavor in the Barcelona studio
of Gameloft, the world leader in mobile gaming, and the home of internationally
acclaimed games, such as Despicable Me: Minions Rush and Asphalt 8.
When not writing books or taking part in industry events and talks, he spends most
of his time fulfilling his other passion: playing piano and guitar.
Preface
Over the past years, Angular 1.x has became one of the most ubiquitous JavaScript
frameworks for building cutting edge web applications, either big or small. At
some point, its shortcomings with regard to performance and scalability became too
prominent as soon as applications grew in size and complexity. Angular 2 was then
conceived as a full rewrite from scratch to fulfill the expectations of modern developers,
who demand blazing fast performance and responsiveness in their web applications.
Angular 2 has been designed with modern web standards in mind and allows full
flexibility when picking up your language of choice, providing full support for ES6
and TypeScript, but working equally well with today's ES5, Dart, or CoffeeScript.
Its built-in dependency injection functionalities let the user build highly scalable
and modular applications with an expressive and self-explanatory code, turning
maintainability tasks into a breeze, while simplifying test-driven development to
the max. However, where Angular 2 stands out is when it shows off its unparalleled
level of speed and performance, thanks to its new change detection system that is up
to five times faster than its previous incarnation. Cleaner views and an unsurpassed
standards-compliant templating syntax compound an endless list of powerful
features for building the next generation of web mobile and desktop apps.
Angular 2 is here to stay and will become a game changer in the way modern web
applications are envisioned and developed in the years to come. However, and due
to its disruptive design and architecture, learning Angular 2 might seem a daunting
effort to newcomers.
This is where this book comes inits goal is to avoid bloating the reader with
API references and framework descriptions, but to embrace a hands-on approach,
helping the reader learn how to leverage the framework to build stuff that matters
right from day one. This is learning by doing right from the start.
Preface
This book aims to give developers a complete walkthrough of this new platform and
its TypeScript-flavored syntax by building a web project from back to forth, starting
from the basic concepts and sample components and iterating on them to build up
more complex functionalities in every chapter until we launch a complete, tested,
production-ready sample web application by the end of the book.
Preface
Chapter 10, Unit Testing in Angular 2, will guide the reader through the steps required
for implementing a sound testing foundation in our application, and the general
patterns for deploying unit tests on components, directives, pipes, routes, and services.
[1]
The defining traits of Angular 2 go beyond the concept of just being a mere web
components framework, since its features encompass pretty much everything you
need in a modern web application: component interoperability, universal support
for multiple platforms and devices, a top-notch dependency injection machinery,
a flexible but advanced router mechanism with support for decoupling and
componentization of route definitions, advanced HTTP messaging, and animation or
internationalization, just to name a few.
In this chapter, we will:
Learn how to set up our code environment to work with Angular 2 and
TypeScript
Build our very first Angular 2 web component and learn how to embed it on
a web page
A fresh start
As mentioned before, Angular 2 represents a full rewrite of the Angular 1.x
framework, introducing a brand new application architecture completely built from
scratch in TypeScript, a strict superset of JavaScript that adds optional static typing
and support for interfaces and decorators.
In a nutshell, Angular 2 applications are based on an architecture design that
comprises trees of web components interconnected between them by their own
particular I/O interface. Each component takes advantage under the covers of a
completely revamped dependency injection mechanism. To be fair, this is a simplistic
description of what Angular 2 really is. However, the simplest project ever made in
Angular is cut out by these definition traits. We will focus on learning how to build
interoperable components and manage dependency injection in the next chapters,
before moving on to routing, web forms, or HTTP communication. This also explains
why we will not make explicit references to Angular 1.x throughout the book.
Obviously, it makes no sense to waste time and pages referring to something that
will not provide any useful insights on the topic, besides the fact we assume that you
might not know about Angular 1.x, so such knowledge does not have any value here.
[2]
Chapter 1
Web components
Web components is a concept that encompasses four technologies designed to be
used together to build feature elements with a higher level of visual expressivity and
reusability, thereby leading to a more modular, consistent, and maintainable web.
These four technologies are as follows:
Templates: These are pieces of HTML that structure the content we aim to
render
Shadow DOM: This provides a sandbox to encapsulate the CSS layout rules
and JavaScript behaviors of each custom element
You will see a nice input control featuring a horizontal slider in your browser.
Inspecting such control with the browser developer tools will unveil a concealed
set of HTML tags that were not present at the time you edited your HTML
template. There you have an example of Shadow DOM in action, with an actual
HTML template governed by its own encapsulated CSS with advanced dragging
functionality. You will probably agree that it would be cool to do that yourself.
Well, good news is that Angular 2 gives you the toolset required for delivering this
very same functionality, so we can build our own custom elements (input controls,
personalized tags, and self-contained widgets) featuring the inner HTML markup
of our choice and a very own stylesheet that does not affect (nor is impacted) by the
CSS of the page hosting our component.
[3]
[4]
Chapter 1
Installing dependencies
Our first requirement will obviously be to install Angular 2 onto our workspace,
including its own peer dependencies. The Angular 2 team has made a great effort to
ensure the installation is modular enough to allow us to bring only what we need,
becoming our projects more or less lean depending on the requirements.
At the time of writing, these are all the different third-party libraries that are required
as peer dependencies in an Angular 2 project, apart from the Angular 2 module itself:
zone.js, a polyfill for the Zone specification that is used to handle change
[5]
These dependencies may evolve without prior notice so please refer to the GitHub
repository for the most up-to-date list of requirements.
You will be probably surprised by the amount of libraries that
Angular 2 does need and the fact that these dependencies are
not part of the Angular bundle itself. This is because these
requisites are not specific to Angular 2, but of a vast majority of
modern JavaScript applications nowadays.
With all these dependencies and third-party libraries in mind, you can run the
following set of bash commands in your terminal console, once you have created a
folder for the project we will cover in this book:
$ mkdir angular2-essentials
$ cd angular2-essentials
$ npm init
$ npm install angular2 es6-shim reflect-metadata rxjs zone.js --save
Apart from the dependencies enlisted previously, we will also need to install
the SystemJS universal module loader package in order support module loading
between code units once transpiled into ES5. SystemJS is not the only option
available for managing module loading in Angular 2. In fact we can swap it for other
module loaders such as WebPack (https://webpack.github.io/), although all
examples provided in this book make use of SystemJS for handling code injection.
We will install SystemJS, flagging it as a development dependency by executing the
following command:
$ npm install systemjs --save-dev
Last but not least, we will also install Bootstrap in our application so that we can
easily craft a nice UI for the example application we will build incrementally in each
chapter. This is not an Angular 2 requirement, but a particular dependency of the
project we will carry out throughout this book.
$ npm install bootstrap --save
The installation can throw different alerts and warnings depending on the versions
of each peer dependency required by Angular 2 at this moment in time, so in case
of issues I strongly recommend to fetch the latest version of the package.json file
available at this book's code repository: https://github.com/deeleman/angular2essentials/blob/master/chapter_01/package.json.
[6]
Chapter 1
Download the file to your directory workspace and run npm install. NPM will find
and install all the dependencies for you automatically.
Mac OS users who have not claimed ownership rights on the npm
directory located at /usr/local/bin/npm (or /usr/local/npm
for those users on OS versions prior to Mac OS El Capitan) might
need to execute the npm install with sudo privileges.
Installing TypeScript
We have now a complete set of Angular 2 sources and their dependencies, plus the
Bootstrap module to beautify our project and SystemJS to handle module loading
and bundle generation.
However, TypeScript is probably not available on your system yet. Let's install
TypeScript and make it globally available on your environment so that we can
leverage its convenient CLI to compile our files later on:
$ npm install -g typescript
Great! We're almost done. One last step entails informing TypeScript about how we
want to use the compiler within our project. To do so, just execute the following onetime command:
$ tsc --init --experimentalDecorators --emitDecoratorMetadata --target
ES5 --module system --moduleResolution node
[7]
Simple, right? The set of properties included in our config manifest is self-descriptive
enough, but we can highlight three interesting properties. They are as follows:
rootDir: This points to the folder the compiler will use to scan for
outDir: This defines where the compiled files will be moved unless we
define our own output path by means of the --outDir parameter in the
command line, the compiler will default to the built folder created at
runtime in the same location where the tsconfig.json file lives.
sourceMap: This sets the source code mapping preferences to help debugging.
Toggle its value to true if you want source map files to be generated at runtime
to back trace the code to its source through the browser's dev tools in case
exceptions arise.
Besides these properties, we also can see that we have marked the node_modules
folder as excluded, which means that the tsc command will skip that folder and all
its contents when transpiling TypeScript files to ES5 throughout the application tree.
I would encourage you to refer to the TypeScript compiler wiki
at https://github.com/Microsoft/TypeScript/wiki/
Compiler-Options for a full rundown of options available in
the compiler API.
[8]
Chapter 1
First, we install the typings tool globally and then we leverage the typings CLI to
install the es6-shim types definition file into our project, creating the typings.json
file that will store the references to the source origin for all type definition files we
will install now and later on. A new folder named typings is created and it contains
the definition files we require. Without them, basic ES6 features like the new
functional methods of the Array class would not be available.
Before moving forward, we need to tackle one more step regarding the TypeScript
typings. When installing type definition files, two faade files are generated by the
CLI: typings/main.d.ts and typings/browser.d.ts. However, only one should
be exposed to the TypeScript compiler. Otherwise, it will raise an exception after
finding duplicated type definitions. Since we are building frontend applications, we
will stick to browser.d.ts and exclude main.d.ts and its linked definition files by
marking it as excluded at tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "es5",
[9]
On the other hand, it is actually recommended to exclude the typings folder from
your project distribution by including it in your .gitignore file, same as we usually
do with the node_modules folder. You only want to include the typings.json
manifest when distributing your app and then have all the installation processes
handled by npm, so it is very convenient to include the type definition files installation
as an action handled by the postinstall script in the package.json file. This way,
we can install the npm dependencies and the definition files in one shot. The code is as
follows:
"scripts": {
"typings": "typings",
"postinstall": "typings install"
},
When taking this approach, the typings package should be included in the
package.json as part of the development dependencies. Thus, reinstall it with the
--save-dev flag. Again, please refer to the book code repository at GitHub to fetch
the latest version of the package.json file for this chapter.
Hello, Angular 2!
With the Angular 2 library bundle in place and full support for TypeScript now
available, the time has come to put everything to the test. First, create and empty file
named hello-angular.ts (.ts is the natural extension for TypeScript files) at the
root of our working folder.
[ 10 ]
Chapter 1
Now, open that file and write the following at the top:
import { Component } from 'angular2/core';
import { bootstrap } from 'angular2/platform/browser';
We have just imported the most basic type and function we will need to scaffold a
very basic component in the next section. The importing syntax will be familiar to
those who are already familiar with ECMAScript 6. For those who are not familiar
with its code paradigm, don't worry. We will discuss more on this in Chapter 2,
Introducing TypeScript.
TypeScript classes
Let's now define a class:
class HelloAngularComponent {
greeting: string;
constructor() {
this.greeting = 'Hello Angular 2!';
}
}
ECMAScript 6 (and TypeScript as well) introduced classes as one of the core parts of its
building blocks. Our example features a class field property named greeting typed as
string, which is populated within the constructor with a text string, as you can see in
the preceding code. The constructor function is called automatically when an instance
of the class is created, and each and every property (and functions as well) should be
annotated with the type it represents (or returns in the case of functions).
Do not worry about all this now. Chapter 2, Introducing TypeScript, will give you the
insights you need to better understand the mechanics of TypeScript. Now, let's focus
on the actual layout of our component. You have probably noticed the name structure,
which conforms to another common coding convention in Angular 2. We define each
and every class in Pascal casing by appending a suffix pointing out its type (will it be a
component, directive, pipe, and so on), which is Component for this case.
[ 11 ]
Chapter 1
A new hello-angular.js file will show up within the built directory (or the path
you have defined in the outDir property at tsconfig.json), and it will contain
an ECMAScript 5 version of the TypeScript code we just built. This file already
contains some functional code to implement support for the Metadata decorator
we configured.
Please note the --watch flag in our command. This parameter
informs the compiler that we want the compilation to be
automatically triggered again upon changing any file.
Disregard the flag when you just need to compile your stuff
once and do not need to watch the code for changes.
Our component is looking better by the minute and now we are in a good state to
start using it, but we still need to embed it somewhere in order to see it live. It's time
to define the HTML shell or web container where it will live.
[ 13 ]
This is the most basic, barebones version of an HTML container for an Angular 2
application we can come up with. This is great because most of the presentation logic
and dependency management will be handled by our component itself.
However, two things catch our attention in this template. I am obviously referring
to the script includes in the previous HTML code. The first script tag introduces
support for the SystemJS module loader in this project. As we pointed out in
the previous section, SystemJS is an open source universal module loader, and
since both the Angular 2 library (included in the following script tags block) and
our own Angular 2 component make use of ES6 module loading functionalities,
we need something in place to ensure that both scripts can actually expose and
require modules. Then, we include a reference to the RxJS bundle, which is a core
dependency of the Angular main bundle that has not been integrated in the main
package in order to keep it as lean as possible. Last but not least, we find the Angular
2 polyfills and the Angular 2 main bundle itself.
In the next JavaScript block of the previous code, we can find the SystemJS
configuration block. We basically configure a package named built whose modules
conform to the System.register polyfill module format. An additional property
allows us to refer to those modules without pointing out the .js file extension, as we
will see shortly.
Do not try to reshuffle the sorting layout of these code blocks
unless you want to face unexpected exceptions.
[ 14 ]
Chapter 1
Then, you can run a web server with live-reloading functionality by running the
following command in a terminal shell after moving into your project folder:
$ lite-server
After executing the preceding command, a browser window will be fired, pointing
to your working directory. Please refer to the NPM module official repository
in order to check out all the options available (https://github.com/johnpapa/
lite-server).
It is actually recommended to install the lite-server package
paired up with the typescript and concurrently packages,
all of them as development dependencies installed with the
--save-dev flag. This way, you can run the TypeScript compiler
in watch mode and the local server at the same time with a single
command that can be wrapped in the start script of package.
json. Then, you can start building stuff right away by accessing
your working folder and executing npm start. This book's code
repository in GitHub implements this approach, so feel free to
borrow the package.json example for your convenience.
How cool is that? Now, we can create our own custom elements that render
whatever we define in them. Let's bring up the page in our web server and see it in
action by going to http://localhost:3000/ (or whatever host and port your local
web server operates in).
Unfortunately, if we reload the browser, nothing will happen and we will only see
a blank page with nothing in there. This is because we still need to bootstrap our
component to instantiate it on the HTML page.
Let's return to our component file hello-angular.ts and add a final line of code:
import { Component } from 'angular2/core';
import { bootstrap } from 'angular2/platform/browser';
@Component({
selector: 'hello-angular',
template: '<h1> {{greeting}} </h1>'
})
class HelloAngularComponent {
greeting: string;
constructor() {
this.greeting = 'Hello Angular 2!';
}
}
bootstrap(HelloAngularComponent); // Component is bootstrapped!
[ 16 ]
Chapter 1
The bootstrap command instances the controller class we pass as an argument and
uses it to lay out a complete application scaffold. Basically, the bootstrap method
kickstarts the following actions:
Analyzes the component configured as its first argument and checks its type.
Searches the DOM after an element with a tag matching the component
selector.
Creates a child injector that will handle the injection of dependencies in that
component and all the child directives (including components, which are
directives too) that such a component might host, in a very similar way a tree
has ramifications.
It creates a new Zone. We will not cover Zones in this book, but let's just say
that Zones are in charge of managing the change detection mechanism of
each instance of a bootstrapped component in an isolated fashion.
The component controller class is instantiated straight away and the change
detection machinery is fired. Now that we have the Shadow DOM placeholders
in place, data providers are initiated and data is injected where required.
Later in this book, we will cover how we can leverage the bootstrap command
to display debugging information or how application providers can be globally
overridden throughout the whole application so the dependency injector baked in
Angular 2 picks the right dependency where required.
[ 17 ]
Hopefully, you are running the TypeScript compiler in watch mode. Otherwise,
please execute the tsc command to transpile our code to ES5 and reload the browser.
We can delight ourselves with the rendered content of our very first Angular 2
component. Yay!
[ 18 ]
Chapter 1
Sublime Text 3
This is probably one of the most widespread code editors nowadays, although it
has lost some momentum lately with users favoring other rising competitors such
as GitHub's very own Atom. If this is your editor of choice, we will assume that
it's already installed on your system and you also have Node (which is obvious,
otherwise, you could have not installed TypeScript in first place through NPM). In
order to provide support for TypeScript code editing, you need to install Microsoft's
TypeScript plugin, available at https://github.com/Microsoft/TypeScriptSublime-Plugin. Please refer to this page to learn how to install the plugin and all
the shortcuts and key mappings.
Once successfully installed, it only takes Ctrl + space bar to display code hints based
on type introspection (see the following screenshot). On top of that, we can trigger
the build process and compile the file to the JavaScript we are working on by hitting
the F7 function key. Real time code error reporting is another fancy functionality you
can enable from the command menu.
[ 19 ]
Atom
Developed by GitHub, the highly customizable environment and ease of installation
of new packages has turned Atom into the IDE of choice for a lot of people. It is
worth mentioning that the code examples provided in this book were actually coded
using Atom only.
In order to optimize your experience with TypeScript when coding Angular 2 apps,
you need to install the Atom TypeScript package. You can install it by means of the
APM CLI or just use the built-in package installer. The functionalities included are
pretty much the same as we have in Sublime after installing the Microsoft package:
automatic code hints, static type checking, code introspection, or automatic build
upon save to name a few. On top of that, this package also includes a convenient
built-in tsconfig.json generator.
[ 20 ]
Chapter 1
WebStorm
This excellent code editor supplied by IntelliJ is also a great pick for coding Angular
2 apps based on TypeScript. The IDE comes with built-in support for TypeScript
out of the box so that we can start developing Angular 2 components from day one.
WebStorm also implements a built-in transpiler with support for file watching, so we
can compile our TypeScript code into pure vanilla JavaScript without relying on any
third-party plugins.
[ 21 ]
Let's create a JavaScript file named gulpfile.js at the root of your project with the
following content:
var gulp = require('gulp');
var ts = require('gulp-typescript');
var tsProject = ts.createProject('tsconfig.json');
gulp.task('build', function() {
var tsResult = tsProject.src().pipe(ts(tsProject));
return tsResult.js.pipe(gulp.dest('./built'));
});
You will need a tsconfig.json file at the root of your project, so our Gulp task
can fetch our compilation preferences from it. From this moment onwards, we can
launch the build processing over the files listed at the files' array property in our
tsconfig.json file by executing the following command:
$ gulp build
Chapter 1
Now, you can launch the build process and watch the file changes by executing the
following command:
$ gulp watch
Improving productivity
Sometimes, we need some helpers to boost our focus, especially when we deal
with too abstract stuff that requires additional attention. A widely accepted
approach is the Pomodoro technique, in which we put together a task list and then
split the deliverables into to-do items that won't require us more than 25 minutes to
accomplish. When we pick any of those to-do items, we focus under distraction-free
mode on its delivery for 25 minutes with the help of a countdown timer. You can
grab more information about this technique at http://pomodorotechnique.com.
In this book, we are going to build a major component that represents this
functionality and fill the component with additional functionalities and UI items
wrapped inside their own components. To do so, we will use the Pomodoro
technique, so let's start by creating a Pomodoro timer.
Our new component is, in fairness, not that much different from the one we previously
had. We updated the names to make them more self-descriptive and then defined
two property fields, statically typed as numbers in our PomodoroTimerComponent
class. These are rendered in the contained view, wrapped inside an <h1> element.
Now, open the index.html file and replace the <hello-angular></hello-angular>
custom element with our new <pomodoro-timer></pomodoro-timer> tag. You can
duplicate index.html and save it under a different name if you do not want to loose
the HTML side of our fancy "Hello World" component.
A note about naming custom elements
Selectors in Angular 2 are case sensitive. As we will see later in this
book, components are a sub set of directives that can support a wide
range of selectors. When creating components, we are supposed to
set a custom tag name in the selector property by enforcing a dashcasing naming convention. When rendering that tag in our view, we
should always close the tag as a non-void element. So <customelement></custom-element> is correct, while <customelement /> will trigger an exception. Last but not least, certain
"common" camel case names might conflict with the Angular 2
implementation, so avoid names like AppView or AppElement.
You will want to update the reference in your System.import(...) block to point to
our new component as well:
System.import('built/pomodoro-timer')
.then(null, console.error.bind(console));
[ 24 ]
Chapter 1
If you bring up a browser window and load this file, you will see a representation
of the numbers defined in the component class. But we want to do more than just
display a handful of numbers, right? We actually want them to represent a time
countdown, and we can achieve that by introducing these changes. Let's first
introduce a function we can iterate on in order to update the countdown. Add this
function after the constructor function:
tick(): void {
if (--this.seconds < 0) {
this.seconds = 59;
if (--this.minutes < 0) {
this.minutes = 24;
this.seconds = 59;
}
}
}
As you can see here, functions in TypeScript need to be annotated with the type of
the value they return, or just void if none. Our function assesses the current value
of both minutes and seconds, and then either decreases their value or just resets
it to the initial value. Then this function is called every second by triggering a time
interval from the class constructor:
constructor() {
this.minutes = 24;
this.seconds = 59;
setInterval(() => this.tick(), 1000);
}
Here, we spot for the first time in our code an arrow function (also known as a
lambda function, fat arrow, and so on), a new syntax for functions brought by
ECMAScript 6 that we will cover in more detail in Chapter 2, Introducing TypeScript.
The tick function is also marked as private, so it cannot be inspected or executed
outside a PomodoroTimerComponent object instance.
So far so good! We have a working Pomodoro timer that countdowns from 25 minutes
to 0, and then starts all over again. The problem is that we are replicating code here
and there. So, let's refactor everything a little bit to prevent code duplication.
constructor() {
this.resetPomodoro();
setInterval(() => this.tick(), 1000);
}
resetPomodoro(): void {
this.minutes = 24;
[ 25 ]
We have wrapped the initialization (and reset) of minutes and seconds inside
our function resetPomodoro, which is called upon instantiating the component
or reaching the end of the countdown. Wait a moment though! According to the
Pomodoro technique, pomodoro practitioners are allowed to rest in between
pomodoros or even pause them should an unexpected circumstance gets on the
way. We need to provide some sort of interactivity so the user can start, pause, and
resume the current pomodoro timer.
[ 26 ]
Chapter 1
In the meantime, just focus on the fact that we introduced a new chunk of HTML that
contains a button with an event handler that listens to click events and executes the
togglePause method upon clicking. This (click) attribute is something you might
not have seen before, even though it is fully compliant with the W3C standards.
Again, we will cover this in more detail in Chapter 3, Implementing Properties and Events
in Our Components. Let's focus on the togglePause method and the new buttonLabel
binding. First, let's modify our class properties so that they look like this:
class PomodoroTimerComponent {
minutes: number;
seconds: number;
isPaused: boolean;
buttonLabel: string;
// Rest of code will remain as it is below this point
}
We introduced two new fields. First is buttonLabel that contains the text that
will be later on displayed on our newly-created button. isPaused is a newlycreated variable that will assume a true/false value, depending on the state of our
timer. So, we might need a place to toggle the value of such a field. Let's create the
togglePause method we mentioned earlier:
togglePause(): void {
this.isPaused = !this.isPaused;
// if countdown has started
if (this.minutes < 24 || this.seconds < 59) {
this.buttonLabel = this.isPaused ? 'Resume' : 'Pause';
}
}
By executing togglePause every time, we reset the Pomodoro to make sure that
whenever the Pomodoro reaches a state where it requires to be reset, the countdown
behavior will switch to the opposite state it had previously. There is only one tweak
left in the controller method that handles the countdown:
private tick(): void {
if (!this.isPaused) {
this.buttonLabel = 'Pause';
if (--this.seconds < 0) {
this.seconds = 59;
if (--this.minutes < 0) {
this.resetPomodoro();
}
}
}
}
Obviously, we do not want the countdown to continue when the timer is supposed
to be paused, so we wrap the whole script in a conditional. In addition to this, we
will want to display a different text on our button whenever the countdown is not
paused and once again when the countdown reaches its end, stopping and then
resetting the pomodoro to its initial values will be the expected behavior. This
reinforces the need of invoking the togglePause function within resetPomodoro.
Chapter 1
<p>
<button (click)="togglePause()">
{{ buttonLabel }}
</button>
</p>
`
})
Basically, we appended the pipe name to the interpolated binding in our template
separated by a pipe (|) symbol, hence the name. Reload the template and you will see
how the seconds figure always displays two digits, regardless of the value it assumes.
We have created a fully functional Pomodoro Timer widget that we can reuse or
embed in more complex applications. Chapter 5, Building an Application with Angular
2 Components, will guide us through the process of embedding and nesting our
components in the context of larger component trees.
In the meantime, let's add some UI beautification to make our component
more appealing. We already introduced a class attribute in our button tag as an
anticipation of the implementation of the Bootstrap CSS framework in our project.
Let's import the actual stylesheet we downloaded through NPM when installing the
project dependencies. Open pomodoro-timer.html and add this snippet at the end
of the <HEAD> element:
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/
bootstrap.min.css">
Now, let's beautify our UI by inserting a nice page header right before our
component:
<body>
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<strong class="navbar-brand">My Pomodoro Timer</strong>
</div>
</div>
</nav>
<pomodoro-timer></pomodoro-timer>
</body>
[ 29 ]
Tweaking the component button with a Bootstrap button class will give it more
personality and wrapping the whole template in a centering container and
appending a nice icon at the top will definitely compound up the UI. So let's update
the template in our template to look like this:
<div class="text-center">
<img src="assets/img/pomodoro.png" alt="Pomodoro">
<h1> {{ minutes }}:{{ seconds | number: '2.0' }} </h1>
<p>
<button (click)="togglePause()"
class="btn btn-danger">
{{ buttonLabel }}
</button>
</p>
</div>
For the icon, we picked a bitmap icon depicting a pomodoro. You can use any
bitmap image of your choice or you can just skip the icon for now, even though we
will need an actual pomodoro icon in the forthcoming chapters. This is how our
Pomodoro timer app looks after implementing all these visual tweaks:
[ 30 ]
Chapter 1
Summary
We looked at web components according to modern web standards and how
Angular 2 components provide an easy and straightforward API to build our
own components. We covered TypeScript and some basic traits of its syntax as a
preparation for Chapter 2, Introducing TypeScript. We saw how to set up our working
space and where to go to find the dependencies we need to bring TypeScript into the
game and use the Angular 2 library in our projects, going through the role of each
dependency in our application.
[ 31 ]
Our first component gave us the opportunity to discuss the form of a controller class
containing property fields, constructor, and utility functions, and why metadata
annotations are so important in the context of Angular 2 applications to define
how our component will integrate itself in the HTML environment where it will
live. Now, we also know how to deploy web server tools and enhance our code
editors to make our lives easier when developing Angular 2 apps, leveraging type
introspection and checking on the go. Our first web component features its own
template and such templates host property bindings declaratively in the form of
variable interpolations, conveniently formatted by pipes. Binding event listeners is
now easier than ever and its syntax is standards-compliant.
The next chapter will cover, in detail, all the TypeScript features we need to know to
get up to speed with Angular 2 in no time.
[ 32 ]
www.PacktPub.com
Stay Connected: