A tour of Angular for web developers: Composing the user interface with components
Tags: angular, webdev, tutorial
Published: 2020-08-23 18:45:21

This is the second part of our brief tour of the Angular framework. In this post, we take a closer look at the primary building block of an angular application, the ***component***.

This series is best read in sequence, as each one usually builds upon the previous ones. The target audience is somewhat experienced web developers, specially those coming from another framework like React, Vue, or Ember. However, even if you are a beginner with a decent grasp of HTML, CSS, TypeScript, HTTP and NodeJS, you should still be able to easily follow along.  and at the end of the series, have a good idea of how Angular works and how to go about using it to build modern web applications.



<a name="toc"></a>

## Table of Contents

- [The journey so far](#journey_so_far)
- [The anatomy of a component](#angular_components_structure)
- [Interpolation](#angular_components_interpolation)
- [Pipes](#angular_components_pipes)
- [A new pipe](#angular_components_pipes_custom)
- [A new component](#angular_components_creating)
- [Repeating UI elements](#angular_components_ngfor)
- [Conditionally rendering elements](#angular_components_conditionally_rendering_elements)
- [Binding to properties, events, and attributes](#angular_components_binding)
- [Passing data between components](#angular_components_input_output)
- [What's next](#next)

<a name="journey_so_far"></a>

## The journey so far

In the previous post, we created a new Angular workspace and then added a web application project to it. If you haven't read through that yet, please go ahead and do.

{% post nalaka/a-tour-of-angular-for-web-developers-4he1 %}

Here's a recap of the commands we used:

```shell
# Install ng (The Angular CLI)
npm install -g @angular/cli

# Create an empty workspace
ng new into-angular \
        --strict --create-application="false"

# Create a web app project
ng generate application hello \
       --strict  --inline-template  --inline-style

cd into-angular  

# Run hello project in local dev server
ng serve hello --open             

# Run unit tests for hello project
ng test hello

# Run end-to-end tests for hello project
ng e2e hello

# Do a production build on the hello project
ng build hello --prod

# Install serve (simple web server)
npm install -g serve

# Serve the production build locally
serve into-angular/dist/hello
```

Back to [TOC](#toc).

<a name="angular_components_structure"></a>

## The anatomy of a component

Now open the  `into-angular/projects/hello/src/app/app.component.ts` file. It defines `AppComponent`,  the component that sits at the top of the component hierarchy making up our apps' user interface.

The generator has populated this file with some content useful for Angular novices. After having a look at that, delete everything and replace it with:

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

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h1>Hello {{ name }}!</h1>
    </div>
  `,
  styles: ['h1 { color: darkred; }']
})
export class AppComponent {
  name = 'world';
}
```

This is the classical "Hello World Program"-  a minimal program that displays the text "Hello World!".

If the `ng serve` command  was already running when the above change was made, you should see the development server picking up the change to the `app.component.ts` file, do a build, and hot reload the browser; this time with just the text *"Hello World!"*.

Let's take a closer look:

![The anatomy of a simple Angular component](https://dev-to-uploads.s3.amazonaws.com/i/ulqdo67pvlotpvst2l7e.png)

The *@Component* decorator "decorates" the plain class `AppComponent` with component metadata and identifies it as a component.

- The *selector* is a CSS selector expression for locating DOM nodes where this component should be attached to.
- The *template* is a string of HTML that represents the structure of the contents of this component.
- The `styles` is an array of CSS strings that applies styling to the DOM elements making up this component.

Having the components' code, the HTML template, and the CSS styles in a single `app.component.ts` file was  a nice feature. However, for more complex components, it is more convenient to move the HTML and CSS files to separate files. We can do that with this:

```typescript
@Component({
  selector: 'app-root',
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
```

In fact, this is the default behavior. It is what would have been generated if we had omitted the `--inline-template`, and ` --inline-style` options to the `ng generate application` command.

Back to [TOC](#toc).

<a name="angular_components_interpolation"></a>

## Interpolation

The HTML template in above example contains a [template expression](https://angular.io/guide/interpolation) enclosed in a pair of double braces. 

```handlebars
<h1>Hello {{ name }}!</h1>
```

Template expressions are side-effect free JavaScript expressions that are evaluated in the context of a component object. In this case, the context is:

```typescript
name = 'world';
```

Since the components' name property is initialized to the value `'world'`, when the component first renders, we see that the *text node* inside the *h1* element contains the text `Hello world!`. 

If we programmatically change change the value of the components' `name` property, Angular will detect the change, re-evaluate the template expression, and update the DOM.

Back to [TOC](#toc).

<a name="angular_components_pipes"></a>

## Pipes

Try changing the template expression to:

```handlebars
<h1>Hello {{ name | uppercase }}!</h1>
```

What do you see on the screen. Can you guess what's going on?

Try changing to:

```handlebars
<h1>Hello {{ name | uppercase | titlecase }}!</h1>
```

What do you see on the screen?

The "`|`" character is used in template expressions to act as a "pipe"  that pumps the left-side expression as a parameter to the function on the right. These can be chained as we see above to form a "pipe line" through which the value of the left most expression flows through. 

- The expression `name` starts with the value *"world"*
- The expression `name | uppercase` is equivalent to `uppercase("world")`, which evaluates to *WORLD*
- And `name | uppercase | titlecase` is equivalent to `titlecase(uppercase(name)` which evaluates to `titlecase("WORLD")` which finally evaluates to *"World"*

So we can use pipes to transform our template expressions. Angular comes with a bunch of [built-in pipes](https://angular.io/api?type=pipe), let's try the *date* pipe to format a time string:

```handlebars
{{ currentTime | date: 'h:mm a on MMMM d, y' }}
```

Of course you need the `currentTime` property set in the component:

```typescript
currentTime = Date()
```

It is also quite easy to [create our own custom pipe](https://angular.io/guide/pipes#creating-pipes-for-custom-data-transformations)- it is just a simple function after all.

Back to [TOC](#toc).

<a name="angular_components_pipes_custom"></a>

## A new pipe

Let's create a `funkycase` pipe that changes *"world"* to *"wOrLd"*

```shell
ng generate pipe funkycase
```

Notice that we didn't specify a project name as the `hello` project is used by default.

Run the unit tests with `ng test` - notice the new test that was created for our "funkycase pipe"

Try applying our new pipe:

```handlebars
<h1>Hello {{ name | funkycase }}!</h1>
```

We only get *Hello !*

The template expression has produced and empty result- i.e. `funkycase("world")` hasn't returned a valid value. Let's open the generated `into-angular/projects/hello/src/app/funkycase.pipe.ts` and fix this:

```typescript
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'funkycase',
})
export class FunkycasePipe implements PipeTransform {
  transform(value: unknown, ...args: unknown[]): unknown {
    if (typeof value === 'string') {
      return [...value]
        .map((character, index) =>
          index % 2 === 0
            ? String(character).toLowerCase()
            : String(character).toUpperCase()
        )
        .join('');
    }

    return value;
  }
}
```

- **[Line 3-5]** The `@Pipe` decorator marks our `FunkycasePipe` class as a pipe
- **[Line 6-7]** Our class implements `transform()` from the  `PipeTransform` interface.
- **[Line 9]** First we use the `...` syntax to spread out the string valued input into an array of strings
- **[Line 10-13]** Then we map over this array collecting the "lowercase version if the index is even" and the "uppercase version otherwise" to a new array
- **[Line 15]** Finally we join this new array to form the end result
- **[Line 18]** If the input value is not a string just return it back

Let's add some more unit tests for this in `app.component.spec.ts`

```typescript
it('handles a string', () => {
  const pipe = new FunkycasePipe();
  expect(pipe.transform('apple')).toEqual('aPpLe');
});

it('handles a number by passing it through unchanged', () => {
  const pipe = new FunkycasePipe();
  expect(pipe.transform(42)).toEqual(42);
});
```

Run `ng test` to see if all is still well.

You may get an error regarding `funkycase` not found from the AppComponnt unit tests. This is because the "Test Bed" used for running the AppComponent unit tests doesn't declare the `FunkyTestPipe`. 

We can fix this in the `app.component.spec.ts` file, in the `beforeEach` call,  change:

```diff
+- declarations: [AppComponent, FunkycasePipe],
-- declarations: [AppComponent],
```

Why did you not get this error when running the app? This is because the `ng generate pipe` command updated the `app.module.ts` and added the ``FunkyTestPipe`` to the declarations array in the module metadata object.

Back to [TOC](#toc).

<a name="angular_components_creating"></a>

## A new component

Let's create a new component:

```shell
ng g component foobar -st
```

This time, we've used the shorthand syntax:

- `g` is short for `generate`
- `-s` is short for `--inline-styles`
- `-t` is short for `--inline-templates`

Two files are generated:

- `into-angular/projects/hello/src/app/foobar/foobar.component.ts`
- `into-angular/projects/hello/src/app/foobar/foobar.component.spec.ts`

And the `AppModule` definition in  `app.module.ts` file is updated to add the `FoobarComponent` to its list of [declarations](https://angular.io/guide/bootstrapping#the-declarations-array). 

```typescript
@NgModule({
  declarations: [AppComponent, FunkycasePipe, FoobarComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

What this means is, that the `FoobarComponent` belongs to the `AppModule` .

As we can see, we also have `FunkycasePipe` in the declarations metadata field. Also, an Angular [component](https://angular.io/api/core/Component) is really a special case of what is known as a "[directive](https://angular.io/api/core/Directive)". Components, pipes, and directives are known as [declarables](https://angular.io/guide/glossary#declarable).

Every component, pipe, and directive should belong to ***one and only one*** Angular module. This is because Angular uses [NgModules](https://angular.io/guide/ngmodules) to group those three types of things and allow those to refer to each other.

So, since both the `AppComponent` and `FunkycasePipe` belong to the `AppModule`, we can reference the `FunkycasePipe`  as  `funkycase` pipe from inside the HTML template of the `AppComponent`. 

Open the `app.component.ts` file. There is no import statement for `FunkycasePipe` in that file, but the HTML template has the expression `{{ name | funkycase }}`.  And it works!

Remove `FunkycasePipe` from the AppModule's `declrations` field and see what happens. 

```
ERROR in projects/hello/src/app/app.component.ts:8:20 - error NG8004: No pipe found with name 'funkycase'.

8       <p>{{ name | funkycase }}</p>
```

Revert that change to fix.

Let's use our new component inside the AppComponent. Because the `selector` metadata field for `FoobarComponent` is set to *app-foobar*, we only need to append the following to the `AppComponent` template:

```html
<app-foobar></app-foobar>
```

Run `ng test` and `ng e2e`, just in case we broke anything. Everything should check out.

Back to [TOC](#toc).

<a name="angular_components_ngfor"></a>

## Repeating UI elements

Right now, our `FoobarComponent` just display the text "foobar works!". Let's go work on it.

Change the template to:

```html
<ul>
  <li *ngFor="let item of items">{{ item }}</li>
</ul>
```

And add the `items` property:

```typescript
items = ['foo', 'bar'];
```

The [ngFor](https://angular.io/api/common/NgForOf) is a structural directive. It is called an structural directive because it affect the structure of the view by creating or removing elements. 

Back to [TOC](#toc).

<a name="angular_components_conditionally_rendering_elements"></a>

## Conditionally rendering elements

Another structural directive is [ngIf](https://angular.io/api/common/NgIf), let's see it in action by replacing he `FoobarComponent` template with:

```html
<div>
  <h3>Your options</h3>
  <ul>
    <li *ngFor="let item of items">{{ item }}</li>
  </ul>

  <div *ngIf="selectedItem == ''">
    Please type-in one of the options.
  </div>
  <div *ngIf="items.includes(selectedItem)">
    Selected option is: <strong>{{ selectedItem }}</strong>
  </div>
</div>
```

And adding the `selectedItem` property:

```typescript
selectedItem = '';
```

At this point, our app should look like this:

![App screenshot with Foobar component in view](https://dev-to-uploads.s3.amazonaws.com/i/bjilfqnv1c0b10vlz0bd.png)

Back to [TOC](#toc).

<a name="angular_components_binding"></a>

## Binding our component to the DOM element

Let's add an input element where we can type-in our selection. Add this HTML to the end of the FoobarComponent template (between line 12 and 13 of previous code listing):

```html
<div>
  <input
    #txt
    type="text"
    [value]="selectedItem"
    [attr.aria-label]="ariaLabel"
    [class]="customClasses"
    [style]="customStyles"
  />
  <button (click)="handleClick(txt.value)">Select</button>
  <p *ngIf="msg != ''">{{ msg }}</p>
</div>
```

Also add these properties in our component:

```typescript
ariaLabel = 'Option to select';
customClasses="class1 class2";
customStyles = 'width: 200px; background-color: lightblue';
```

We have a bunch of new concepts in this one short code snippet:

1. The`#txt` syntax declares a [template reference variable](https://angular.io/guide/template-reference-variables) with the name `txt`, which we can use to refer to the corresponding DOM element, as done at line 10 above.
1. The `[value]="selectedItem"` is a [property binding](https://angular.io/guide/property-binding). This binds the `selectedItem` property on our component with the `value` property on the DOM element. The binding is one-way. When the `selectedItem` property changes, the DOM property is updated. But when the `value` DOM property is updated as a result of user interaction on the web page, that change does NOT automatically updates the `selectedItem` property on the component.
1. The `[attr.aria-label]="ariaLabel"` is an [attribute binding](https://angular.io/guide/attribute-binding#attribute-binding). The reason for having this type of binding is that there are some **HTML element attributes** that do not have corresponding **DOM properties**. Like. for example, the `aria-label` attribute.
1. The `[class]="customClasses"` is a [class binding](https://angular.io/guide/attribute-binding#class-binding). This binds the `customClasses` property of the component, which in this example is a space seprated list of CSS classes, to the classes that are actually attached to the DOM element.
1. The `[style]="customStyles"` is a [style binding](https://angular.io/guide/attribute-binding#style-binding). This binds the `customStyles` property of the component, which contains a semicolon seprated list of CSS styles, to the DOM element.
1. The `(click)="handleClick(txt.value)"` is an [event binding](https://angular.io/guide/event-binding). This binds the  `handleClick` method on the component with the `click` event on the DOM element. So every time the click event fires, our `handleClick` method gets invoked. Notice how we have used the template reference variable `txt` to access the DOM element here.

Back to [TOC](#toc).

<a name="angular_components_input_output"></a>

## Passing data between components

Like we discussed before, a component is responsible for a chunk of our applications' user interface. For better mantainability, components keep largely to themselves and has no idea what's going on outside themselves. But when we compose our application out of a hierarchy of components, we need a way to send data into a component and get data out of that component. For this, Angular provides the [Input](https://angular.io/api/core/Input) and [Output](https://angular.io/api/core/Output) decorators:

![Components take input and fires output events](https://dev-to-uploads.s3.amazonaws.com/i/txbqqmrwnmp8n9zu252q.png)

Let's create a new component to understand how this works in practice:

```shell
ng g component baz -st
```

Replace the generated Baz component code in `baz.component.ts` with this:

```typescript
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-baz',
  template: `
    <div>
      <p>{{ msg }}</p>
      <input #txt type="text" />
      <button (click)="handleClick(txt.value)">Ok</button>
    </div>
  `,
  styles: [
    'div{margin: 10px; padding: 5px; width: 200px; border: 1px solid grey}',
  ],
})
export class BazComponent {
  @Input() msg: string = 'Default prompt';
  @Output() nameSelected = new EventEmitter<string>();

  handleClick(val: string) {
    this.nameSelected.emit(val);
  }
}
```

And use the Baz component from inside the App component like this:

```html
<app-baz [msg]="msgIn" (nameSelected)="onNameSelected($event)"></app-baz>
<p>
  Name received from the baz component:<br />
  <code>{{ nameOut }}</code>
</p>
```

You need to append the above HTML code to the template in the `app.component.ts` file, and the below TypeScript code to the class body.

```typescript
msgIn = 'Please enter your name';
nameOut = '<No Ourput from child component yet>';

onNameSelected(name: string) {
  this.nameOut = name;
}
```

The Baz component displays a customisable message, reads user text input, and provides an Ok button that when clicked emits a custom event with what the user entered in the text box. Let's see it in action:

![The baz component takes an input and produces an output](https://dev-to-uploads.s3.amazonaws.com/i/cv8ie6z5qc8k2hwfr0tz.png)

The displayed message is customisable by the parent component.

```typescript
@Input() msg: string = 'Default prompt'
```

The **Input** decorator marks `msg` as an input property.

To the parent component, `msg` is "bindable" just like a DOM property:

```html
<app-baz [msg]="msgIn"></app-baz>
```

The child component fires output events and the parent component binds to these events using the familiar event binding syntax.

```html
<app-baz (nameSelected)="onNameSelected($event)"></app-baz>
```

The **Output** decorator is used on a field in the child component to declare that field as an output event.

```typescript
  @Output() nameSelected = new EventEmitter<string>();
```

When the child component has data it needs to ouput, it uses `nameSelected` and emit an event with that data as the payload:

```typescript
this.nameSelected.emit(val);
```

Back to [TOC](#toc).



<a name="next"></a>

## What's next

The code for our workspace as at the end of this post can be found under the `p02` tag in the [nalaka/into-angular](https://github.com/nalaka/into-angular) repository.

{% github nalaka/into-angular no-readme %}

In the next post we will see how to create ***services*** that interact with the world outside our component and how to ***inject*** them where they are needed.

Following this, we will continue our tour by looking at routing, forms, material styling, animation, internationalization, accessibility, testing, and keeping our applications up-to date as Angular itself evovles.

Back to [TOC](#toc).