26
Custom Directives

As with many other features of Angular, you can extend functionality by creating your own custom directives. Custom directives allow you to extend the functionality of HTML by implementing the behavior of elements yourself. If you have code that needs to manipulate the DOM, it should be done inside a custom directive.

You implement a custom directive by calling the @directive class, much the same way you define a component. The @directive class metadata should include the selector of the directive to be used in the HTML. The Directive export class is where the logic for the directive will reside. For example, the following is a basic definition for a directive:

import { Directive } from '@angular/core';
@Directive({
    selector: '[myDirective]'
})
export class myDirective { }

Creating a Custom Attribute Directive

You can define a limitless number of types of custom directives, which makes Angular incredibly extensible. Custom directives are the most complex portion of Angular to explain. The best way to get you started is to show you an example of custom directives to give you a feel for how they can be implemented and interact with each other.

This section shows how to implement a custom attribute directive. The zoom directive created in this example is designed to add custom functionality to whatever image it is applied to. With this directive applied, you can scroll over an image with the mouse wheel to make the element grow or shrink in size.

Listing 26.1 shows the zoom component, which displays a list of images. These images have the zoom directive applied to them, allowing the mouse scroll event to increase or decrease the size of each image.

Listing 26.2 shows the zoom directive. This directive has the selector zoom. This directive imports Directive, ElementRef, HostListener, Input, and Renderer from @angular/core to provide the functionality this directive needs.

Lines 10 through 12 of Listing 26.2 watch for the mouse cursor to enter the element, and when it does, it applies a border to the element with the border() function to let the user know the directive is active.

Lines 14 through 16 remove the border when the cursor leaves the element to tell the user the directive is no longer active.

Lines 17 through 26 listen for the mouse wheel to be activated. Depending on which direction the wheel is scrolled, the element’s size is adjusted with the changeSize() function.

Lines 27 through 31 define the border() function. This function takes in three parameters and then applies those parameters to style the host element.

Lines 32 through 36 define the changeSize() function, which changes the size of the host element.

Listing 26.3 shows the HTML file for zoom.component.ts. It creates a row of images and applies the zoom directive to those images.

Listing 26.4 shows the styles for zoom.component.ts. It sets the height of the images to 200px initially so they aren’t rendered huge if they have a high resolution.

The web page that results from Listings 26.126.4 is shown in Figure 26.1.

Listing 26.1 zoom.component.ts: A Structural Directive

01 import { Component } from '@angular/core';
02
03 @Component({
04   selector: 'app-root',
05   templateUrl: './app.component.html',
06   styleUrls: ['./app.component.css']
07 })
08 export class AppComponent {
09   images: string[] = [
10     '../assets/images/jump.jpg',
11     '../assets/images/flower2.jpg',
12     '../assets/images/cliff.jpg'
13   ]
14 }

Listing 26.2 zoom.directive.ts: A Custom Attribute Directive

01 import { Directive, ElementRef, HostListener, Input, Renderer }
02   from '@angular/core';
03 @Directive({
04     selector: '[zoom]'
05 })
06
07 export class ZoomDirective {
08     constructor(private el: ElementRef, private renderer: Renderer) { }
09
10     @HostListener('mouseenter') onMouseEnter() {
11         this.border('lime', 'solid', '5px');
12     }
13
14     @HostListener('mouseleave') onMouseLeave() {
15         this.border();
16     }
17     @HostListener('wheel', ['$event']) onWheel(event: any) {
18         event.preventDefault();
19         if(event.deltaY > 0){
20             this.changeSize(-25);
21         }
22         if(event.deltaY < 0){
23             this.changeSize(25);
24         }
25     }
26     private border(
27       color: string = null,
28       type: string = null,
29       width: string = null
30       ){
31         this.renderer.setElementStyle(
32             this.el.nativeElement, 'border-color', color);
33         this.renderer.setElementStyle(
34             this.el.nativeElement, 'border-style', type);
35         this.renderer.setElementStyle(
36             this.el.nativeElement, 'border-width', width);
37     }
38     private changeSize(sizechange: any){
39         let height: any = this.el.nativeElement.offsetHeight;
40         let newHeight: any = height + sizechange;
41         this.renderer.setElementStyle(
42             this.el.nativeElement, 'height', newHeight + 'px');
43     }
44 }

Listing 26.3 app.component.html: An HTML File That Uses the Zoom Directive

01 <h1>
02   Attribute Directive
03 </h1>
04 <span *ngFor="let image of images">
05   <img zoom src="{{image}}" />
06 </span>

Listing 26.4 app.component.css: A CSS File that Sets the Image Height

01 img {
02   height: 200px;
03 }
A screenshot shows two CSS files.

Figure 26.1 Applying a custom attribute directive

Creating a Custom Directive with a Component

Angular components are also a type of directive. What distinguishes a component from a directive is that components use an HTML template to generate a view. But because a component underneath is just a directive, it can be applied to HTML elements to add custom functionality in some really cool ways.

Angular offers a built-in directive called ng-content. This directive allows Angular to take existing HTML from between two element tags that use a directive and use that HTML within the components template. The syntax of ng-content is as follows:

<ng-content></ng-content>

The example in this section shows how to use a component as a custom directive to change how the element looks with a containing template.

This example implements a custom directive container that is designed to add a surrounding “container” HTML template to the element it is being applied to. This directive has two inputs title and description that can be used to give the host element a description and title.

Listing 26.5 shows the root component, which displays various HTML elements. These elements have the container directive applied, which adds a header with an optional title, a footer with an optional description, and borders to each element.

Listing 26.5 app.component.ts: The Root Component

01 import { Component } from '@angular/core';
02
03 @Component({
04   selector: 'app-root',
05   templateUrl: './app.component.html',
06   styleUrls: ['./app.component.css'],
07 })
08 export class AppComponent {
09
10   images: any = [
11     {
12       src: "../assets/images/angelsLanding.jpg",
13       title: "Angels Landing",
14       description: "A natural wonder in Zion National Park Utah, USA"
15     },
16     {
17       src: "../assets/images/pyramid.JPG",
18       title: "Tikal",
19       description: "Mayan Ruins, Tikal Guatemala"
20     },
21     {
22       src: "../assets/images/sunset.JPG"
23     },
24   ]
25 }

Listing 26.6 shows the HTML for the root component. The code creates several elements of different types, such as image, div, and p, and applies the container directive to them.

Listing 26.6 app.component.html: HTML for the Root Component

01 <span *ngFor="let image of images" container title="{{image.title}}"
02   description="{{image.description}}">
03   <img src="{{image.src}}"  />
04 </span>
05 <span container>
06   <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit,
07     sed do eiusmod tempor incididunt ut labore </p>
08 </span>
09 <span container>
10   <div class="diver">
11   </div>
12 </span>

Listing 26.7 shows the CSS for the root component. It sets a max image height to keep the size of the image smaller. It also sets some default styling for the class diver so that it is visible to the user.

Listing 26.7 app.component.css: CSS for the Root Component

01 img{ height: 300px; }
02 p{ color: red }
03 .diver{
04   background-color: forestgreen;
05   height: 300px;
06   width: 300px;
07 }

Listing 26.8 shows the container directive. This directive has the selector container and the inputs title and description. This directive imports Directive, Input, and Output from @angular/core to provide the functionality this directive needs.

Listing 26.8 container.component.ts: A Component that Defines the Container

01 import { Component, Input, Output } from '@angular/core';
02
03 @Component({
04   selector: '[container]',
05   templateUrl: './container.component.html',
06   styleUrls: ['./container.component.css']
07 })
08 export class ContainerComponent {
09   @Input() title: string;
10   @Input() description: string;
11 }

Listing 26.9 shows the HTML for the container directive. Lines 2 through 4 create the title bar for the container. Lines 5 through 7 apply the content attribute directive. ng-content acts as a placeholder and will be replaced with the template from the container component shown in Listing 26.8. Lines 8 through 10 create the description bar for the container component.

Listing 26.9 container.component.html: HTML for the Container Component

01 <div class="sticky">
02     <div class="title" >
03         {{ title }}
04     </div>
05     <div class="content">
06         <ng-content></ng-content>
07     </div>
08     <div class="description">
09         {{ description }}
10     </div>
11 </div>

Listing 26.10 shows the CSS for the container component. This file sets the CSS to give the container component borders, a title bar, and a description bar.

Listing 26.10 container.component.css: CSS for the Container Component

01 .title {
02     color: white;
03     background-color: dimgrey;
04     padding: 10px;
05 }
06 .content {
07     text-align: center;
08     margin: 0px;
09 }
10 .description {
11     color: red;
12     background-color: lightgray;
13     margin-top: -4px;
14     padding: 10px;
15 }
16 .sticky {
17     display: inline-block;
18     padding: 0px;
19     margin: 15px;
20     border-left: dimgrey 3px solid;
21     border-right: dimgrey 3px solid;
22 }

The web page that results from Listings 26.5 through 26.10 is shown in Figure 26.2.

A screenshot shows results of a web page.

Figure 26.2 Custom component directive

Summary

Angular directives extend the behavior of HTML. Directives can be applied to Angular templates as HTML elements, attributes, and classes. The functionality of directives is defined in the @directive class. Angular provides several built-in directives that interact with form elements, bind data, and interact with browser events. For example, ngModel binds the value of a form element directly to the component. When the component value changes, so does the value displayed by the element and vice versa.

One of the most powerful features of Angular is the ability to create your own custom directives. Implementing a custom directive in code is simple, using the @directive class. However, directives can also be very complex because of the myriad ways they can be implemented.

Next

In the next chapter you will learn how to use events and observables to handle change detection in your Angular components. You will also learn how to create, emit, and handle your own custom events.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.78.179