This is a beginner level tutorial to ease you into Angular (v2+), although there are many resources online to creating components, these articles exist as part of a series. This article will guide you through creating your first Angular component.
Table of contents
Series
- Bootstrapping your first Angular app
- Creating your first Angular component
- Passing data into Angular components with @Input
- Component events with EventEmitter and @Output in Angular
Introduction
For the purposes of this tutorial, we’ll be creating a “counter” component that allows for incrementing and decrementing of values via buttons, which then change the value of an ``.
Before creating your first component, you’ll need to learn how to bootstrap an Angular app before continuing.
Creating an ES6/TypeScript class
All components in Angular are classes, and to tell Angular they’re a component we use a special decorator which we’ll move onto in the next section, however for now, let’s create a class:
class AppComponent {
}
Inside this class, we can add properties, such as a message:
class AppComponent {
message: string = 'Hello world!';
}
If you’re new to TypeScript, you may be more familiar with this approach:
class AppComponent {
constructor() {
this.message = 'Hello world!';
}
}
These are essentially the same thing, but using TypeScript we can declare the types of properties we’re using, for instance I’m saying message: string
, denoting that it will be of type “string”. I’ve also given it a default value of “Hello world!” as well, which may be done dynamically inside a real world application.
From here, we need to somehow render this message into the component, which is where we need to create a template to enable us to bind the message to the component.
Using the @Component decorator
To tell Angular that our class is a component, we need to import the component decorator and use it on our class.
Decorators are just functions, you can read my in-depth guide to Angular decorators once you’re more familiar with using them.
To import the component decorator, we simply grab it from the Angular core
module:
// app.component.ts
import { Component } from '@angular/core';
export class AppComponent {
message: string = 'Hello world!';
}
Now Component
is imported, we simply add it above our class (which is called decorating a class):
// app.component.ts
import { Component } from '@angular/core';
@Component()
export class AppComponent {
message: string = 'Hello world!';
}
There’s an official TC39 proposal for decorators, currently at Stage-2, so expect decorators to become a core language feature soon in JavaScript as well.
The next two things we need are configuration properties selector
and template
:
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="app">
{{ message }}
</div>
`
})
export class AppComponent {
message: string = 'Hello world!';
}
You can see we’re using {{ message }}
to interpolate the values of the class in which they correspond with the template, this will then render “Hello world!” dynamically out for us in the browser.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
It’s fairly obvious what template
does here, but what does selector
mean? The selector
property creates a component under the name of the string you just passed in, to use it like so:
<app-root>
Loading...
</app-root>
We’ve simply put some “Loading…“ text inside of here, which you can customise if you’d like, to display as the client-side application is loading.
Creating a counter component
So let’s move onto a more complex example and create a CounterComponent
we mentioned in the introduction.
Component definition
By now we should understand how to do this based off the above explanation:
// counter.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'counter',
template: `
<div class="counter">
{{ count }}
</div>
`
})
export class CounterComponent {
count: number = 0;
}
Property binding to an
To bind our count
to an ``, we need to use something called property binding, whereby we bind to a specific property on a DOM element (we can also bind to components, which we’ll learn in the next tutorial).
So, which property do we need to bind to? The value
property! You’ve likely done something like this before:
<input type="text" value="Hello">
This creates an <input>
with a default value of Hello
. To bind a value from our component class to the template, we need to do this:
@Component({
selector: 'counter',
template: `
<div class="counter">
<input type="text" [value]="count">
</div>
`
})
export class CounterComponent {
count: number = 0;
}
The []
square bracket notation here signifies a property binding, which as you build out Angular apps will become more clear and visually help you with what types of bindings you are making.
To think about property binding in a simpler way, take element.value
for example. The value
is a property on the DOM Node object, and we can do this as well to look it up:
element['value'] = 'Hello';
It’s an easier trick to remember that you are essentially asking for a built-in JavaScript property when using Angular syntax. It will set your property with the value supplied, in our case we are supplying a dynamic count
value, which is subject to change.
Component methods
So to increment and decrement our count, we can create two methods on our class:
export class CounterComponent {
count: number = 0;
increment() {
this.count++;
}
decrement() {
this.count--;
}
}
These methods directly manipulate the count
property inside our component, which will automatically be reflected in our template due to the property binding we have made. We need to add buttons for the user to increment and decrement the value:
@Component({
selector: 'counter',
template: `
<div class="counter">
<button>
Decrement
</button>
<input type="text" [value]="count">
<button>
Increment
</button>
</div>
`
})
export class CounterComponent {
// ...
}
Now that we have buttons for the user to click, we need to bind a click
event to each button. This is done through event binding, which uses rounded brackets ()
instead of square brackets []
. Inside the rounded brackets, we need to specify the name of the event that we want to listen for:
@Component({
selector: 'counter',
template: `
<div class="counter">
<button>
Decrement
</button>
<button>
Increment
</button>
</div>
`
})
export class CounterComponent {
// ...
}
We pass the callback method as the value of the added attribute. You can think of it like we are calling addEventListener()
on an element
Node:
element.addEventListener('click', increment);
Styling the component
We’ll introduce one more concept, which is styling. To do this we can add a styles
property to our @Component
decorator and pass an array of strings:
@Component({
selector: 'counter',
styles: [`
.counter {
position: relative;
}
input {
border: 0;
border-radius: 3px;
height: 30px;
max-width: 100px;
text-align: center;
}
button {
outline: 0;
cursor: pointer;
height: 30px;
border: 0;
border-radius: 3px;
background: #0088cc;
color: #fff;
}
`],
template: `
<div class="counter">
<button (click)="decrement()">
Decrement
</button>
<input type="text" [value]="count">
<button (click)="increment()">
Increment
</button>
</div>
`
})
export class CounterComponent {
// ...
}
Angular supports multiple style declarations per component but most of the time we’ll only need to pass one in. This is useful if you have shared styles between components, you can create a file that both components use that contain their styles. An alternative is using styleUrls
instead, which allows us to use external styles and have them compiled through a preprocessor such as Sass or Less:
@Component({
selector: 'counter',
styleUrls: ['counter.component.scss'],
template: `
<div class="counter">
<button (click)="decrement()">
Decrement
</button>
<input type="text" [value]="count">
<button (click)="increment()">
Increment
</button>
</div>
`
})
export class CounterComponent {
// ...
}
Angular also supports an external template for a component should you wish to separate them out into individual files. You can specify this via
templateUrl
and point to the file.
@NgModule registration
Now we’ve created our new Angular component, we must add it to our @NgModule
definition:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
// import our new component!
import { CounterComponent } from './counter.component.ts';
import { AppComponent } from './app.component.ts';
@NgModule({
declarations: [
AppComponent,
// register it inside the declarations array
CounterComponent
],
imports: [
BrowserModule
],
bootstrap: [
AppComponent
]
})
export class AppModule {}
This important step allows us to use the CounterComponent
in our application, such as ``.
Plunker
Everything we’ve done here is readily available in a Plunker for you to have a look through:
Next steps
Now we’ve learned how to do the basics, let’s move on and learn how to pass data into Angular Components with @Input.