Complete Angular 2 Guide: Components in Depth

Ashish Singh
Jan 6, 2017 · 13 min read

This article covers Angular components in depth

Complete Angular 2 Guide series

Components are the Swiss army knife in Angular framework

When I started working in angular version 2.x it was pretty evident that components are the basic building block of the whole framework and yet I could not find any article which covered all the details of Components. So I decided to write this post.

Note: This is a LONG post. I’ve added pictures to make it simple but it also makes the post appear even longer.

My hope is that once you read this, you’ll be able to better understand and hopefully write better angular 2.x applications.

In this blog, I’ll take a simple example and go over various scenarios to give you an idea as to how they actually work. Specifically, I’ll go over:

  1. Basic idea of components
  2. Dissecting the Component API
  3. Deep dive Components(Angular source code)
  4. Component Interactions(Parent & child components communication)
  5. Component lifecycle hooks
  6. Classifying Components(Smart & dumb components😎)
  7. Component inheritance(ES6 Inheritance👏🏽)

Yes almost everything related to Components!!!

The app

Components: Basic Idea

Component hierarchy one way graph

Top level component is the first node in the above illustration and it has further child components, these child components can further have child components. Its like a tree.

However, I also want to highlight that at any point in this graph a component can have further components as well as directives(more on directives in the future section of this series).

In fact components are the most important directives in angular > 2.x. Folks familiar with Angular 1.x would remember that we used directives with templates to structure our applications, so it made more sense to extract that under a new name(Component) and since the WebComponents took the developer community by storm so angular team used the web component idea and gave existing angular 1.x directives a new name Components.

However, directives can only be present at the leaf nodes since they don’t have templates to accommodate any more components or directives.

Components are intended to be self-contained because we want to encapsulate our component functions and we don’t want other code to arbitrarily reach into our components to read or change properties. Also, we don’t want our component to affect another component written by someone else. An obvious example is CSS — if we set CSS for one component we don’t want our CSS to “bleed out” to another components just as we don’t want other CSS to “bleed in” to our component.


Dissecting the Component API

Components are directives with a special decorator(@Component) and templates. This decorator hijacks the ES6 class and its constructor and adds its own magic such as initialisation params inside the constructor and hiding some details for us.

You also export the class so that it can be imported in other files and used throughout your application.

Let’s look at a simple component.

@Component({
selector: 'my-app',
styles: [`
.h2 {
margin-left: 10px;
}
`],
template: `
<h2>My App</h2>
`
})
export class AppComponent {
public myValue:number = 2;
// Via event binding
myValueChange($event) {
this.myValue = $event.value;
}
}

This component can be used anywhere in the template like this <my-app></my-app> . Template and styles can be specified inline or by passing url.

A component can have properties which can then be accessed in the views like this {{myValue}}. These properties can be accessed using this.propertyName anywhere in the component.


Deep dive Component source code

Code here shows that a component extends from the Directive. Exactly what I’ve been saying above.

Directive decorator has a lot of properties but template, templateUrl, styles, styleUrls and others but these four are important here to highlight the fact that component adds template and styling and under the hood is a directive only.

TIP: You can override the default angular interpolation {{ }} to something you like using this api for component decorators.

/**
* Overrides the default encapsulation start and end delimiters (respectively `{{` and `}}`)
*/
interpolation?: [string, string];

However, there are many options which can be passed to the component decorator. You can check it out here.


Component Interactions

Input

In the context of the counter application I wish to set the initial value from the AppComponent. This is where Input fits in.

Here is how it can be done in code.

<!-- Parent component -->
<counter [counterValue]="myValue"></counter>
// myValue is property of ParentComponent
<!-- Child component -->
export class CounterComponent {
@Input() counterValue = 0;
// this is input from the parent component
}

In the above example counterValue is enclosed between square brackets. This is a syntax for property binding. myValue is bound and passed to CounterComponent now its upto CounterComponent to accept the input which it does in the above example. Now the value which is set in the AppComponent is bound to counterValue in the CounterComponent.

TIP: Please note that this is a unidirectional flow. Properties from the parent component can only flow down the hierarchy not up. This is also represented in the flow chart above.

Now we have a one way communication setup in between our components but we also need to be able to communicate upwards. That is where Output comes into picture.

Output

In the context of counter application when the value of counter is changed it also needs to get reflected in the AppComponent(parent component). This is where Output comes into picture.

Here is how it can be done in code.

<!-- Parent component -->
// Template
<counter #counter [counterValue]="myValue" (counterChange)="myValueChange($event);"></counter>
// Component
myValueChange($event) {
this.myValue = $event.value;
}
<!-- Child component -->
// Template
<button (click)="decrement();">
// Component
@Output() counterChange = new EventEmitter();
decrement() {
this.counterValue--;
this.counterChange.emit({
value: this.counterValue
})
}

In the example above we have wrapped counterChange in round brackets. This is the syntax for event binding in angular. Whenever it receives an emission from an EventEmitter it will run myValueChange function and the event would be caught in $event variable. This $event is not something you can choose at your will. This has to be $event only.

In CounterComponent we have created an Output which is an EventEmitter. We emit an event on decrement with the new value. This event gets caught in the parent component.

“Input” specifies which properties you can set on a component whereas “Outputs” identifies the events a component can fire to send information up the hierarchy to its parent.

Other ways of communication between parent and child component is by directly accessing child component from the parent component. There are 2 ways of doing this.

LocalRef

Here is how it can be done in code.

<!-- Parent component -->// Template
<div class="counter__container">
<button (click)="counter.decrement()">-</button>
<input type="text" class="counter_input" [value]="myValue">
<button (click)="counter.increment()">+</button>
</div>
<counter #counter [counterValue]="myValue" (counterChange)="myValueChange($event);"></counter><!-- Notice how #counter is a local reference -->

In the example above #counter holds the reference to (CounterComponent)child component and using that we are accessing counter.decrement() method.

ViewChild

Here is how it can be done in code.

<!-- Parent component -->
import {ViewChild} from ‘@angular/core’;
// Template
<div class="counter__container">
<button (click)="decrement()">-</button>
<input type="text" class="counter_input" [value]="myValue">
<button (click)="increment()">+</button>
</div>
// Component
@ViewChild(CounterComponent)
private counterComponent: CounterComponent;
// Via ViewChild accessor
increment() {
this.counterComponent.increment();
}
decrement() {
this.counterComponent.decrement();
}

In the above example we are accessing the CounterComponent using a ViewChild.

Child components in our view can be accessed from our parent component easily with @ViewChild. To get access to a component and its methods, we can use the @ViewChild decorator.

We are just calling child component methods using our parent component methods which we have bound to click events on the buttons in the parent component.

Other than these two methods, components which are not directly linked can also use services to communicate. Services can then be injected into the components. This way component not directly linked but not limited to that can communicate. I am leaving service for another post in future.


Component lifecycle hooks

Hooks in real life

A component has a lifecycle managed by Angular itself. Angular creates it, renders it, creates and renders its children, checks it when its data-bound properties change, and destroys it before removing it from the DOM. Angular offers lifecycle hooks that provide visibility into these key life moments and the ability to act when they occur.

If you’ve ever dealt with angular 1.x where we needed to use ngCloak to hide the empty {{}} interpolation when the data was not initialised in the models, then you can totally related to use case of these lifecycle hooks.

Imagine if we rely on child components data then we can use ngOnInit hook which ensures and waits that all the children components are initialised first and then the parent component ngOnOnit runs, so the dependant data should be set in this method.

Angular guide is pretty elaborate for lifecycle hooks and I highly recommend reading it thoroughly. There is no point in covering each one here in this article, it would defeat the purpose of documentation.

However I want to cover two hooks in general which are very useful, AfterView & AfterContent.

AfterView concerns withViewChildren. ViewChildren are all the views of all the children components.

AfterContent concerns ContentChildren. ContentChildren are all the external content. These are components passed directly into the component from outside. Angular 1.x transclusion feature.

Both these hooks are really important especially AfterView.


Structuring Components [Smart & dumb components]

Smart & Dumb components

This is a totally opinionated topic so please feel free to say what you feel and give your feedback. Also a lot of this comes directly from the React world.

Core Idea: To make angular apps more maintainable, predictive and reusable by organising our components in a better way.

To organise components we have to classify components into Container(Smart) & Presentation(Dumb) components.

The definition of these components is not well defined and is driven by sheer opinion.

Before we talk more about classification and dive into nitty gritty of these components let’s first see what are the benefits of this approach.

  • Better separation of concerns such as UI and App code. You understand your app better this way.
  • Better reusability. Presentation components can be reused with different state source.
  • Forces better component design(IMO this is not very true as without this approach we can achieve this).

Now that we know some benefits of this approach let’s look at the characteristics of these two types of components so that we know how to identify and separate them when we are modelling our components.

Characteristics of Container components

  • Mostly holds the logic of the application.
  • Facilitates data by fetching from redux-store and behaviour(stateful) for presentation components.
  • Takes actions which change the overall state of the application.
  • Always has the scope of encapsulating some other minimal component inside of it. for eg. FollowersSidebarContainer can further have SidebarComponent*.

Characteristics of Presentation components

  • Mostly holds the looks of a component.
  • Can be reused as this can be a generic component in nature.
  • Usually have DOM markup and styling.
  • Does no dispatch any actions or access states directly(stateless) or lead to any state changes*.
  • Generally accepts properties from the container components.
  • Does not have much scope for further encapsulation of components inside it.*

* Not a strict rule.

In all design decisions, it’s good to have some quick guidelines in mind when making decision. So, here’s a rule of thumb.

  • If a component is not using a lot of properties and merely passing it down the chain, then probably it should be a container component.
  • If a component is accessing redux store or dispatching actions then probably it should be a container component.

Component classification and separation is an ongoing process of refactoring so don’t try to get it right the first time. As you experiment with this pattern, you will develop an intuitive sense for when it’s time to extract some containers.

Real world example

StoreLogMonitorComponent(Container/smart component)

Notice the top level component StoreLogMonitorComponent has only inputs that’s coming in from the user. This component is the top level component it passes down the hierarchy chain to other components. This can be classified as Container component.

DockMonitorComponent(Container/Smart component)

Notice this mid level component receives inputs from Parent component but it does a lot of redux state fetch(using StoreDevtools) and other vars related to presentation. Notice that these are again fed to the child components ngrx-commander & ngrx-dock. This can also be classified as Container component.

DockComponent(Presentation/Dumb component)

Notice how this component has more divs and deals with overall looks and stylesheets. It is also receiving inputs from the parent components. This is IMO nothing but a presentation component.

Best practices for creating Reusable components

  • If a part of your UI is used several times (Button, Panel, notification), or is complex enough on its own (App, TravelStory, Comment), it is a good candidate to be a reusable component.

Component inheritance

Inheritance

Yuss!!! it’s possible with components. 🤘🏼

I’d first like to illustrate its benefits before diving into the details of it. Let’s say we want to use Primeng in our project, but we have our designer who wants to design the view stuff himself. There are ways to do this for example we could override the whole css or we could use Component inheritance. IMO later is a far better approach for many reasons. And it’s not only about CSS what if we want to change only bits of the library then we can totally customise it like never before.

Component Inheritance is basically ES6 inheritance. Let’s look at the classic polygon & square example here. Its pretty self explanatory. I’d leave that an an exercise for the reader.

Component inheritance covers Metadata(Decorators), constructor & life cycle hooks with an exception of Animation support but that is coming very soon.

Let’s look at an plunkr example how this works in code.

Vehicle is the class being inherited so all the properties of that class would be inherited including properties, bindings, listeners, constructor etc except animations for now. Car Truck inherit most of these from Vehicle.

Notice how I have overridden the on click event in both components and the colour of truck component.

This gives us some awesome OO powers and to create more awesome applications.

With this article on angular components I have tried to cover almost everything related to components. Please reach out to me if you have any suggestions or if you think I have missed something related to components.


AVIACOMMERCE: OPEN SOURCE E-COMMERCE

click the image to know more or visit https://aviacommerce.org

If you liked this article, click the 💚 below so that it reaches more people her on medium.

For more musings about programming, follow me here or on twitter so you’ll get notified when I write new posts.

Aviabird

Aviabird Technologies provides enterprise software consulting in Angular, Rails, Elixir, Golang, React, Flutter and other technologies.

Ashish Singh

Written by

Sky is the Limit

Aviabird

Aviabird

Aviabird Technologies provides enterprise software consulting in Angular, Rails, Elixir, Golang, React, Flutter and other technologies.