HostListener And HostBinding

January 03, 2020 0 Comments

HostListener And HostBinding

 

 

The @HostListener decorator is used to set up an event binding on the host element and is applied to a method. The example directive relies on the browser’s DOM API to manipulate its host element, both to add and remove class memberships and to receive the click event. Working with the DOM API in an Angular application is a useful technique, but it does mean that your directive can be used only in applications that are run in a web browser. Angular is intended to be run in a range of different execution environments, and not all of them can be assumed to provide the DOM API.

Even if you are sure that a directive will have access to the DOM, the same results can be achieved in a more elegant way using standard Angular directive features: property and event bindings. Rather than use the DOM to add and remove classes, a class binding can be used on the host element. And rather than use the addEventListener method, an event binding can be used to deal with the mouse click.

Behind the scenes, Angular implements these features using the DOM API when the directive is used in a web browser—or some equivalent mechanism when the directive is used in a different environment.

Bindings on the host element are defined using two decorators, @HostBinding and @HostListener, both of which are defined in the @angular/core module, as shown in the following example.

Creating Host Bindings in the attr.directive.ts File in the src/app Folder

import { Directive, ElementRef, Attribute, Input, SimpleChange, Output, EventEmitter, HostListener, HostBinding } from "@angular/core"; import { Product } from "./product.model"; 
@Directive({ selector: "[pa-attr]" }) export class PaAttrDirective { @Input("pa-attr") @HostBinding("class") bgClass: string; @Input("pa-product") product: Product; @Output("pa-category") click = new EventEmitter<string>(); @HostListener("click") triggerCustomEvent() { if (this.product != null) { this.click.emit(this.product.category); } }

The @HostBinding decorator is used to set up a property binding on the host element and is applied to a directive property. The listing sets up a binding between the class property on the host element and the decorator’s bgClass property.

The @HostListener decorator is used to set up an event binding on the host element and is applied to a method. The listing creates an event binding for the click event that invokes the triggerCustomEvent method when the mouse button is pressed and released. As its name suggests, the triggerCustomEvent method uses the EventEmitter.emit method to dispatch the custom event through the output property.

Using the host element bindings means that the directive constructor can be removed since there is no longer any need to access the HTML element via the ElementRef object. Instead, Angular takes care of setting up the event listener and setting the element’s class membership through the property binding.

Although the directive code is much simpler, the effect of the directive is the same: clicking a table row sets the value of one of the input elements, and adding a new item using the form triggers a change in the background color of the table cells for products that are not part of the Soccer category.

Creating a Two-Way Binding using Hostlistener & HostBinding

Angular provides special support for creating directives that support two-way bindings so they can be used with the banana-in-a-box bracket style that ngModel uses and can bind to a model property in both directions.

The two-way binding feature relies on a naming convention. To demonstrate how it works, the following example adds some new elements and bindings to the template.html file.

Applying a Directive in the template.html File in the src/app Folder

... 
<div class="col-6"> <div class="form-group bg-info text-white p-2"> <label>Name:</label> <input class="bg-primary text-white" [paModel]="newProduct.name" (paModelChange)="newProduct.name=$event" /> </div> <table class="table table-sm table-bordered table-striped"> <tr><th></th><th>Name</th><th>Category</th><th>Price</th></tr> <tr *ngFor="let item of getProducts(); let i = index" [pa-attr]="getProducts().length < 6 ? 'bg-success' : 'bg-warning'" [pa-product]="item" (pa-category)="newProduct.category=$event"> <td>{{i + 1}}</td> <td [pa-attr]="item.category == 'Soccer' ? 'bg-info' : null"> {{item.category}} </td> <td [pa-attr]="'bg-info'">{{item.price}}</td> </tr> </table> </div> ...

I am going to create a directive that supports two one-way bindings. The binding whose target is paModel will be updated when the value of the newProduct.name property changes, which provides a flow of data from the application to the directive and will be used to update the contents of the input element. The custom event, paModelChange, will be triggered when the user changes the contents of the input element and will provide a flow of data from the directive to the rest of the application.

To implement the directive, I added a file called twoway.directive.ts to the src/app folder and used it to define the directive shown in the following example.

The Contents of the twoway.directive.ts File in the src/app Folder

import { Input, Output, EventEmitter, Directive, HostBinding, HostListener, SimpleChange } from "@angular/core";
@Directive({ selector: "input[paModel]"
})
export class PaModel { @Input("paModel") modelProperty: string; @HostBinding("value") fieldValue: string = ""; ngOnChanges(changes: { [property: string]: SimpleChange }) { let change = changes["modelProperty"]; if (change.currentValue != this.fieldValue) { this.fieldValue = changes["modelProperty"].currentValue || ""; } } @Output("paModelChange") update = new EventEmitter<string>(); @HostListener("input", ["$event.target.value"]) updateValue(newValue: string) { this.fieldValue = newValue; this.update.emit(newValue); } 
}

This directive uses features that have been described previously. The selector property for this directive specifies that it will match input elements that have a paModel attribute. The built-in ngModel two-way directive has support for a range of form elements and knows which events and properties each of them uses, but I want to keep this example simple, so I am going to support just input elements, which define a value property that gets and sets the element content.

The paModel binding is implemented using an input property and the ngOnChanges method, which responds to changes in the expression value by updating the contents of the input element through a host binding on the input element’s value property.

The paModelChange event is implemented using a host listener on the input event, which then sends an update through an output property. Notice that the method invoked by the event is able to receive the event
object by specifying an additional argument to the @HostListener decorator, like this:

... 
@HostListener("input", ["$event.target.value"]) updateValue(newValue: string) {
...

The first argument to the @HostListener decorator specifies the name of the event that will be handled by the listener. The second argument is an array that will be used to provide the decorated methods with arguments. In this example, the input event will be handled by the listener, and when the updateValue method is invoked, its newValue argument will be set to the target. value property of the Event object, which is referred to using $event.

To enable the directive, I added it to the Angular module, as shown in the following example.

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser"; 
import { ProductComponent } from "./component";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { PaAttrDirective } from "./attr.directive";
import { PaModel } from "./twoway.directive";
@NgModule({ imports: [BrowserModule, FormsModule, ReactiveFormsModule], declarations: [ProductComponent, PaAttrDirective, PaModel], bootstrap: [ProductComponent] }) export class AppModule { }

When you save the changes and the browser has reloaded, you will see a new input element that responds to changes to a model property and updates the model property if its host element’s content is changed. The expressions in the bindings specify the same model property used by the Name field in the form on the left side of the HTML document, which provides a convenient way to test the relationship between them, as shown in the following image.

hostlistener
hostlistener

The final step is to simplify the bindings and apply the banana-in-a-box style of brackets, as shown in the following example.

... 
<div class="col-6"> <div class="form-group bg-info text-white p-2"> <label>Name:</label> <input class="bg-primary text-white" [(paModel)]="newProduct.name" /> </div> <table class="table table-sm table-bordered table-striped"> <tr><th></th><th>Name</th><th>Category</th><th>Price</th></tr> <tr *ngFor="let item of getProducts(); let i = index" [pa-attr]="getProducts().length < 6 ? 'bg-success' : 'bg-warning'" [pa-product]="item" (pa-category)="newProduct.category=$event"> <td>{{i + 1}}</td> <td>{{item.name}}</td> <td [pa-attr]="item.category == 'Soccer' ? 'bg-info' : null"> {{item.category}} </td> <td [pa-attr]="'bg-info'">{{item.price}}</td> </tr> </table> </div> ...

When Angular encounters the [()] brackets, it expands the binding to match the format used in the above example, targeting the paModel input property and setting up the paModelChange event. As long as a directive exposes these to Angular, it can be targeted using the banana-in-a-box brackets, producing a simpler template syntax.

Reference

https://angular.io/api/core/HostListener

Visit the angular tutorial list. And make strong your angular concept. click here. wuschools.com is always written about the Agular concept for the angular lover. Ang writes about how angular makes your life easy if you are a web site developer.


Tag cloud