A Beginner’s Guide to Components in Angular: Understanding Directives, Communication, and Best Practices.

Component – what it is (Made of)

Components are the building blocks of an Angular application. They encapsulate a part of the user interface (UI) along with its associated behaviour and data. Angular applications are essentially a tree of components, with each component being responsible for a specific part of the UI.

An Angular component consists of three parts:

  • Template: The template is the HTML code that represents the component’s UI structure. It uses Angular-specific syntax for data binding, directives, and event handling.
  • Class: The component’s TypeScript class contains its properties and methods. The class is decorated with the @Component() decorator, which provides metadata to Angular, such as the selector and template location.
  • Styles: The component’s styles are used to apply CSS to the template. These styles can be defined inline or in a separate file, and are typically scoped to the component to avoid global style conflicts.

How components BIND data

Angular enables seamless communication between the component’s class and its template through data binding. There are four types of data binding:

  • Interpolation: {{property}} – displays a component property value in the template.
<!-- app.component.html -->
<!-- this is an example of interpolation. -->
<h1>{{title}}</h1>
  • Property binding: [property]=”value” – binds a DOM property to a component property.
<!-- app.component.html -->
<!-- this is an example of property binding. -->
<button [disabled]="isDisabled">Click Me!</button>
  • Event binding: (event)=”handler()” – binds a DOM event to a component method.
<!-- app.component.html -->
<!-- this is an example of event binding -->
<button (click)="onClick()">Click Me!</button>
  • Two-way binding: [(ngModel)]=”property” – combines property and event binding for bidirectional data flow.
<!-- app.component.html -->
<!-- this is an example of two-way binding -->
<input [(ngModel)]="name" />

how components influence

Angular directives extend the functionality of HTML elements, components, or other directives. There are three types of directives:

  • Attribute directives: Modify the appearance or behaviour of an element, component, or another directive. These are custom directives that change the appearance or behaviour of a DOM element. They look like regular HTML attributes in the template. Some built-in examples include ngStyle and ngClass.
//highlight.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {

  constructor(private el: ElementRef) { }

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight('red');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}

In the example above, the HighlightDirective is an attribute directive that is defined using the @Directive decorator. The @HostListener decorator is used to listen for mouse enter and mouse leave events on the element. When the user hovers over the element, the highlight() method is called to set the background colour to red.

<!-- app.component.html -->
<div appHighlight>
  Hover over me to see the highlight effect!
</div>

  • Structural directives: Manipulate the DOM by adding, removing, or modifying elements. They often use an asterisk (*) syntax, like *ngIf or *ngFor. The *ngIf directive is used to conditionally render elements, while *ngFor is used to loop through and display a list of items.
<!-- app.component.html -->
<div *ngIf="showElement">
  This element is only displayed if showElement is true.
</div>

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

In the example above, the *ngIf directive displays the div element if the showElement in the component is true.
Then, we’ve got the *ngFor directive that loops through the items array in the component and display a list item for each element.

NOTE: can you spot interpolation in the same code snippet? Good for you! 😉

  • Component directives: Define the custom components themselves that can be used in other components or templates.
//custom.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-custom',
  template: '<div>{{ message }}</div>'
})
export class CustomComponent {
  @Input() message: string;
}

In the example above, the template property in the CustomComponent specifies the HTML template for the component, which includes a div element that displays the value of the message property.

<!-- custom.component.html -->
<app-custom message="Hello, world!"></app-custom>

How components communicate

Angular automatically detects when data changes in components and updates the DOM accordingly. Change detection occurs whenever an event is triggered (e.g., user input, timer, or HTTP response). By default, Angular uses a zone to track async operations and run change detection when they are finished. You can also manually trigger change detection or optimise it by using strategies like ChangeDetectionStrategy.OnPush.

There are various communications mechanisms for components to use with each other:

  • Input/Output properties: Parent-to-child and child-to-parent communication via property and event binding. To enable parent-to-child communication, use the @Input() decorator in the child component to bind a parent component’s property. For child-to-parent communication, use the @Output() decorator along with an EventEmitter to emit events to the parent component.
  • Content projection: Content projection allows you to insert content from a parent component into a specific location within a child component’s template. You achieve this by using the <ng-content> tag in the child component’s template.
  • Template variables: Accessing a child component’s properties and methods from a parent component.
  • ViewChildren/ContentChildren: Querying child components in the component tree. ViewChild and ContentChild are decorators used to access child components or DOM elements within a parent component. ViewChild is used for elements within the parent’s template, while ContentChild is used for elements projected from the parent into the child component.
  • Services and dependency injection: Sharing data and functionality across components. Angular uses dependency injection as a design pattern to provide instances of classes (services) to components, making it easier to manage and test code. The DI system in Angular consists of injectors, providers, and tokens.
    Services are classes that encapsulate related functions or data that can be shared across multiple components. To create a service, use the Angular CLI command ng generate service service-name. Services can be injected into components using the constructor and are usually marked with the @Injectable() decorator.

Components – other features

Components lifecycle is managed by Angular through lifecycle hooks. These hooks are methods that get called at specific points during a component’s life, such as ngOnChanges, ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, and ngOnDestroy.

Angular has a mechanism called Pipes. Pipes are simple functions that transform data before displaying it in the template. They can be used to format data, like currency or dates, or to filter lists. Angular provides built-in pipes like date, uppercase, lowercase, and currency, and you can also create custom pipes using the @Pipe() decorator.

Angular supports unit testing for components using testing frameworks like Jasmine and test runners like Karma. When you generate a component using the Angular CLI, it automatically creates a test file (spec file) with a basic test suite. TestBed is a utility provided by Angular to help set up and configure the testing environment for components.


Component best practices

  • Keep components small and focused on a single responsibility.
  • Use the Angular CLI to generate components and follow the recommended file structure.
  • Encapsulate component-specific logic and data in services.
  • Use lifecycle hooks to perform tasks at specific stages in the component lifecycle.
  • Write unit tests for components and keep them up-to-date.

lego blocks

So there you have it, peops! With this basic understanding of components, data binding, and directives, you’re well on your way to building amazing Angular applications.

It was some serious meat (not to mention that it’s on the boring side compared to the actual life coding!) so I’m pretty impressed you’re still here with me. But trust me, it’s oh, so worth it!

Remember to keep your code clean, your tests up-to-date, and your love for Angular strong.

/*
Until next time,
Happy coding! 🚀❤️
eMs
*/

Leave a comment