Angular ~ Component - rohit120582sharma/Documentation GitHub Wiki

Table of contents

  • Introduction
    • Create a view which can manage it's own state and behaviour
  • Create a component
    • Simple class with @Component decorator
    • Configuration
      • selector
      • template/ templateUrl
      • styles/ styleUrls
      • encapsulation
  • Register a component into ngModule
    • Add to declarations list
    • Add to bootstrap list, if it's root level component
  • Use a component
    • Use component's selector as custom-tag in the template
    • It will create a new component's instance and an instance of template which would have access to component's instance
  • Data-binding
    • Property binding to change DOM element property
    • Event binding to listen to an event by registering an event handler
    • Two-way data binding
  • @Input and @Output property decorator
    • EventEmitter & emit() method
  • Life-cycle hooks
  • Template Access
    • local template variable (#)
    • @ViewChild & @ViewChildren
    • @ContentChild & @ContentChildren

Introduction

In Angular, We create new custom tags with their own look and behaviour. An Angular application is therefore just a set of custom tags that interact with each other, we call these tags Components.

Components are the fundamental building block of Angular applications. Every component has an associated template, stylesheet, and a class with logic.

Components are composable, we can build larger Components from smaller ones.

An Angular application is therefore architected as a tree of Components stemming from one root Component. When each Component renders, it recursively renders its children Components.

When we bootstrap an Angular application we are telling the browser to render that top-level root Component which renders it’s child Components and so on.

Component

Architecting with Components

Architecting an Angular application means understanding that it’s just a tree of Components, each Component has some inputs and outputs and should have a clear responsibility with respect to the rest of the application.

Reference


Configuration

The way you make a class a component is by importing the Component member from the @angular/core library and decorating it using @Component.

Within the component decoration, you have a variety of configuration properties that help define this given component.

  • selector - This is the name of the tag that the component is applied to.
  • templateUrl & styleUrls - These define the HTML template and stylesheets associated with this component. You can also use template and styles properties to define inline HTML and CSS.
  • encapsulation - This property is used to apply shadow DOM feature on component DOM.

Within the component's class, the various properties and methods are defined. Any properties and methods defined here are accessible from the template. And likewise, events that occur in the template are accessible within the component. It's also where dependency injection occurs within a constructor, which gives you access to various services.

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
}

Decorators

  • A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.
  • Decorator is used to attaching metadata
  • Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members
  • Decorators are an experimental feature that may change in future releases. To enable experimental support for decorators, you must enable the experimentalDecorators compiler option in your tsconfig.json.

Metadata

  • Metadata describes how to process the class
  • Metadata tells how a particular component is created and what are the functionality and features of a particular component
  • Metadata defines how a particular component should behave

View Encapsulation

Angular is inspired from Web Components, a core feature of which is the shadow DOM.

The shadow DOM lets us include styles into Web Components without letting them leak outside the component’s scope.

Angular also provides this feature for Components and we can control it with the encapsulation property.

The valid values for this config property are:

  • ViewEncapsulation.Native - Any styles we set on a component do not leak outside of the scope of the components with ViewEncapsulation.Native. However with ViewEncapsulation.Native our component is isolated from the global styles we’ve defined for our application.

  • ViewEncapsulation.Emulated - Any styles we set on a component do not leak outside of the scope of the components but with ViewEncapsulation.Emulated our component still inherits global styles from twitter bootstrap.

  • ViewEncapsulation.None - We are not encapsulating anything, the style we defined in our card form component has leaked out and started affecting the other components.

The default value is ViewEncapsulation.Emulated. Angular adds a unique attribute to the generated components HTML element and then targets the CSS specifically to that attribute for ViewEncapsulation.Native and ViewEncapsulation.Emulated.

Angular moves even closer to the Web Components spec through the use of the :host {} selector, both with Native or Emulated styles. The :host selector targets the declared element, not any of the Component’s children (such as the template).

:host {
  display: block;
}

Lifecycle Hooks

A component in Angular has a life-cycle, a number of different phases it goes through from birth to death.

We can hook into those different phases to get some pretty fine-grained control of our application.

To do this we add some specific methods to our component class which get called during each of these life-cycle phases, we call those methods hooks.

Each of these lifecycle hooks has an associated typescript interface of the same name. So ngOnChanges has an interface called OnChanges.

The hooks are executed in this order:

  • constructor - This is invoked when Angular creates a component or directive by calling new on the class.

  • ngOnChanges - Invoked every time there is a change in one of the input properties of the component.

  • ngOnInit - Invoked when given component has been initialized. This hook is only called once after the first ngOnChanges.

  • ngDoCheck - Invoked when the change detector of the given component is invoked. It allows us to implement our own change detection algorithm for the given component. ngDoCheck and ngOnChanges should not be implemented together on the same component.

  • ngAfterContentInit - Invoked after Angular performs any content projection into the components view.

  • ngAfterContentChecked - Invoked each time the content of the given component has been checked by the change detection mechanism of Angular.

  • ngAfterViewInit - Invoked when binding of component's template is complete. The parent component is initialized first and if it has children, this callback is called after all children are ready.

  • ngAfterViewChecked - Invoked when change-detection mechanism checks whether there are any changes in the component template's bindings. This callback may be called more than once as a result of modifications in this or other components.

  • ngOnDestroy - This method will be invoked just before Angular destroys the component which means a component is removed from DOM (i.e. structural directives or router navigation). Use this hook to unsubscribe observables and detach event handlers to avoid memory leaks.


Data Binding

Data binding plays an important role in communication between a template and its component.

Templating

In Angular, your views are defined within HTML templates. You're also able to display data defined within the component through interpolation, as well as use various conditionals within the template.

  • Interpolation - It is just a fancy word for displaying data in the template. Interpolation works by wrapping double curly brackets around a template expression. A template expression can represent a component property or even mathematical equations.
  • *ngFor -
  • *ngIf -

Property Binding

  • It simply means you're passing data from the component class and setting the value of a given element in the view.
  • It allows you to define element attribute values from the component class.
  • It is one-way data binding, as you can only set data from the component to the view.
  • There are 3 ways to define a property binding in Angular:
    • You can use interpolation to define the value, as long as the value you're defining is a string. If it's not, you must use method 2 or 3 below.
    • The most common method to define property binding is by wrapping brackets around an element property and binding it to a component property.
    • Adding bind- before the element property also achieves the same thing.
<img src="{{ angularLogo }}">
<img [src]="angularLogo">
<img bind-src="angularLogo">

Event Binding

  • When a user interacts with your app, it's sometimes necessary to know when this happens. A click, hover, or a keyboard action are all events that you can use to call component logic within Angular.
  • It's one-way data binding, in that it sends information from the view to the component class.
  • Whenever you wish to capture an event from the view, you do so by wrapping the event in question with ( ) parenthesis.
  • Angular provides you with a variety of events from which you can call component logic.
  • References
<button (click)="myEvent($event)">My Button</button>
export class AppComponent{
	myEvent(event){
		console.log(event);
	}
}

Two-way Binding

  • Angular doesn’t come with built-in two-way data binding anymore, but with APIs that allow to implement this type of binding using property and event bindings.
  • In Angular, ngModel directive implements two-way data binding. ngModel comes as a built-in directive as part of the FormsModule to implement two-way data binding and should be preferred when building components that serve as custom form controls.
  • It really just boils down to property binding and event binding.
<input [(ngModel)]="username">
<input [ngModel]="username" (ngModelChange)="username=$event">

<p>Hello {{username}}!</p>
  • Without the ngModel directive, we could easily implement two-way data binding just like this:
<input [value]="username" (input)="username = $event.target.value">

<p>Hello {{username}}!</p>
  • Let’s take a closer look at what’s going on here:
    • [value]="username" - Binds the expression username to the input element’s value property
    • (input)="expression" - Is a declarative way of binding an expression to the input element’s input event (yes there’s such event)
    • username = $event.target.value - The expression that gets executed when the input event is fired
    • $event - Is an expression exposed in event bindings by Angular, which has the value of the event’s payload

Canonical syntax

Every syntax has a canonical syntax. This is mainly useful if your server side templating system is having trouble with the [] or () syntax.

/* Property binding */
<ns-pony [name]="pony.name"></ns-pony>
<ns-pony bind-name="pony.name"></ns-pony>

/* Event binding */
<button (click)="onButtonClick()">Click me!</button>
<button on-click="onButtonClick()">Click me!</button>

/* Local variables */
<input type="text" #name>
<input type="text" ref-name>
<button on-click="name.focus()">Focus the input</button>

Inter-component communication

A component can interact with its parent/children through @Input and @Output bindings but sometimes that's not enough. What happens when you need to communicate between siblings or nested routes? What if you need to send a message to multiple components at once or just need more control?

Communicate between parent to child

Inputs provide a mechanism for a parent component to bind the properties that a child component have access to. The parent component pushes the properties to the child component.

In the child component, import Input from @angular/code. Then use @Input decorator to define the inputs and ngOnChanges() to handle changes

import { Component, Input } from '@angular/core';
//...
export class ChildComponent {
	@Input() param:string;

	ngOnChanges(value){
	}
}

In the parent component’s template, simply define property bindings in the child’s selector to pass the data. The binding should point to properties available in the parent component’s class on the right side of the equal sign that you want to make available to the child component.

<child [param]="myVar"></child>
export class ParentComponent {
	myVar: string = 'The Fox Without a Tail';
}

When @Input() doesn't work, inject a @ViewChild()

Communicate between child to parent with Outputs

Outputs provide a mechanism for a child component to emit events up to its parent component.

In the child component, import Output and EventEmitter from @angular/code. Then use @Output decorator to define the outputs and EventEmitter to push the event up to the parent component by invoking emit() method.

import { Component, Output, EventEmitter } from '@angular/core';
//...
export class ChildComponent {
	@Output() doThing:EventEmitter<string> = new EventEmitter<string>();

	onDo(){
		this.doThing.emit('some event');
	}
}

In the parent component’s template, define event bindings as part of the child component’s selector. A binding should point to a method defined in the parent component’s class that takes action on the data received from the child. $event contains the payload emitted from the child:

<child (doThing)="handleThing($event)"></child>
export class ParentComponent {
	handleThing(event:string){
		console.log(event);
	}
}

If you can't use an @Output() binding, you can inject the parent component directly into the child

Communicate between siblings

  • Use a service to communicate between siblings
  • Try to avoid sharing mutable state
  • Use observables to push events out from a service to components
private state$ = new BehaviorSubject<>({});
doSomething(thing){
	this.state$.next(thing);
}

Communicate between nested routes

Communicate with multiple components at once


Content projection

Sometimes the nature of a component means that the consumer would like to customise the view, the presentation of data, in a unique way for each use case.

Rather than trying to predict all the different configuration properties to support all the use cases we can instead use content projection. Giving the consumer the power to configure the presentation of the component as they want.

If we add the tag <ng-content></ng-content> anywhere in our template HTML for our component. The inner contents of the tags that define our component are then projected into this space.

<!-- Parent component template -->
<child>
	<p intro>intro content...</p>
	<p description>description content...</p>
</child>
<!-- Child component template -->
<h1>Child Component</h1>
<p>Something good...</p>
<ng-content select="[intro]"></ng-content>
<ng-content select="[description]"></ng-content>
⚠️ **GitHub.com Fallback** ⚠️