Although the Router part of Angular is still changing a lot, the public API for building components is already quite stable. In this post we will go through on how we can build components with this new version of Angular, based on some code examples.
In this post we will learn the essentials of how to build Angular components, but a bit also on how not to build them:
- Angular - where to start?
- Components - the component public API
- Internals of Components - the Controller
- Properties and how bindings work
- Do's and dont's of Properties
- Events
- Do's and dont's of Events
- Conclusions
Where to start - Browser components
Let's start by looking into how browser components work, taking for example the browser native component select
:
This component like all browser components has some interesting properties:
-
we don't need to be aware of its internal structure in order to be able to reason about the component. We only need to know its public API.
-
we don't need to look at other parts of the application to know what this component is doing and how it will behave
Angular allows us to build UI components that are in every way similar to native browser components: encapsulated, reusable, and easy to reason about.
The Angular component API in a nutshell
This is an example of an Angular component: a dropdown component, similar to the select
native component but for example with support for disabled elements and other extra features:
Here is what is going on here:
-
this component has an input property
options
, via which we provide a list of countries. Properties are the input that a component receives, and we use them to pass in an input model into the component. Based on the model the view will be built accordingly. -
the component emits an output event named
selection
when a new option is selected. Events are the output that a component produces. They report to the outside world a relevant change of the component internal state.
The internal structure of a component
Every component has two main internal parts:
- An internal Html/CSS tree that encapsulates how the component view is built
- a Controller class, which coordinates the interaction between the view and the input model
Given this, let's now go through the 2 main component concepts (properties and events) with examples.
Understanding Properties
Let's start with properties, and for that let's introduce the color-me
component, which can be used like this:
And here is what the component looks live:
As we can see the color-me
component is just an input box where we can type the name of a color and see it painted in a sample square.
How properties work
The component looks like a native browser component, with the exception of the [sampleColor]
notation. This means that the string
blue
is passed as an input property to the component.
The input property will be automatically bound to a controller property, so we can use it inside the component.
The Component Controller
This is how the controller of the color-me
component is implemented:
As we can see, the sampleColor
input property is bound to the controller, and renamed to color
. This property is generally available inside the controller via this.color
. It's used for example to start up the view with an initial color.
The component View
This is how the component view is internally implemented in
color-me.html
:
A bit more is going on here:
-
a local variable input is defined using
#input
-
The input property
value
of the input box is being filled in with the color name via[value]="color"
-
The background CSS property is binded to the value of the input box via
[style.background]="input.value"
So here we see that properties are not only a mechanism for passing data inside a component. They can be generally used to write to any valid DOM element property, such as for example the property input.value
, which contains the input text field value, or style.background
which contains the color of the sample rectangle.
When is the color applied
In this case, the color is applied as the user types into the input box. This only happens because there is an event listener hooked inside the component on the keyup
event, which causes change detection to be triggered when the user types.
Try to remove the empty (keyup)
listener, and the color will no longer be applied.
How NOT to use properties
Properties are meant to pass input data to a component, that preferably might change over time.Properties should be avoided in these cases:
-
when passing constant string values to the component, such as for example
[width]="100px"
. For that, use the Attribute annotation instead. -
when trying to pass a command to the component, instructing the component to trigger a given action. This is to be avoided because it creates a tight coupling between the caller of the command and the component itself. Ideally, the component should only receive input data and react to it. The provider of the data should have no information of how the data gets rendered or which actions it triggers.
Events
The second main concept of the component API is Events. Let's start by introducing the scroll-me
component:
And here is how the component looks like:
The component just scrolls a list up and down, when the corresponding buttons are clicked. This is the template of the component, and we can see the click
event being bound on the down button with the (click)="expression"
syntax:
Again in this example, we are binding directly to a DOM property via the [scroll-top]
binding. This binding allows us to write directly to the Javascript scrollTop
property of the selection div
, which is another example of how Angular encourages the direct use of the DOM API.
But did you see it it? the Up button does not have a (click)
binding, but the component still works! Let's take a look at the controller code to see what is going on:
We can see that in the constructor of the component an event listener is being manually added to the up button. There is nothing special about that code, it's just the pure DOM API event subscription API.
The angular syntax for binding events via (click)
is mainly an abbreviated way to do the same as this code.
How does the event get detected then?
The event gets detected via the Angular change detection mechanism, which runs at the end of each virtual machine turn. This mechanism is based on the notion of Zones, see this previous post for more about it, or have a look at this video that describes how Events really work and how they relate to Zones:
Do's and dont's of Events
The event mechanism is easier to misuse than the properties mechanism. With properties, we really have to go out of our way to pass in a command object of some sort to trigger an action inside the object.
With events, it's very easy to fall into the situation of using an event to trigger an action in an external component. The key thing to bear in mind about events, is that in order to keep the event emitter decoupled from the subscriber, the emitter should only report about changes on its internal state: for example, a selection occurred in a dropdown component.
This way the emitting component stays decoupled from the event subscribers and does not have any information on what the event is being used for.
Conclusions
The Angular API is much simpler to learn and use correctly than the Angular 1 API: there are a lot less concepts and they are much simpler to reason about.
The components are better isolated and if the notions of properties and events are well applied, it's simpler to write truly reusable components that can be understood just by looking at an HTML template.
To start trying out the Angular component API, you can find all the running code from this post here, or clone the angular2-seed repository to start with a clean and ready to use project.
If you want to learn more about Angular, have a look at the Angular for Beginners Course:
Other posts on Angular
If you enjoyed this post, here some other popular posts on our blog:
- Angular Router - Extended Guided Tour, Avoid Common Pitfalls
- How to run Angular in Production Today
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular Forms - Template Driven, Model Driven or In-Between
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
- How does Angular Change Detection Really Work?