Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Angular Cookbook

You're reading from   Angular Cookbook Over 80 actionable recipes every Angular developer should know

Arrow left icon
Product type Paperback
Published in Aug 2021
Publisher Packt
ISBN-13 9781838989439
Length 652 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Muhammad Ahsan Ayaz Muhammad Ahsan Ayaz
Author Profile Icon Muhammad Ahsan Ayaz
Muhammad Ahsan Ayaz
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Preface 1. Chapter 1: Winning Components Communication 2. Chapter 2: Understanding and Using Angular Directives FREE CHAPTER 3. Chapter 3: The Magic of Dependency Injection in Angular 4. Chapter 4: Understanding Angular Animations 5. Chapter 5: Angular and RxJS – Awesomeness Combined 6. Chapter 6: Reactive State Management with NgRx 7. Chapter 7: Understanding Angular Navigation and Routing 8. Chapter 8: Mastering Angular Forms 9. Chapter 9: Angular and the Angular CDK 10. Chapter 10: Writing Unit Tests in Angular with Jest 11. Chapter 11: E2E Tests in Angular with Cypress 12. Chapter 12: Performance Optimization in Angular 13. Chapter 13: Building PWAs with Angular 14. Other Books You May Enjoy

Using attribute directives to handle the appearance of elements

In this recipe, you'll work with an Angular attribute directive named highlight. With this directive, you'll be able to search words and phrases within a paragraph and highlight them on the go. The whole paragraph's container background will also be changed when we have a search in action.

Getting ready

The project we are going to work with resides in chapter02/start_here/ad-attribute-directive, inside the cloned repository:

  1. Open the project in Visual Studio Code (VS Code).
  2. Open the terminal, and run npm install to install the dependencies of the project.
  3. Once done, run ng serve -o.

    This should open the app in a new browser tab, and you should see something like this:

Figure 2.1 – ad-attribute-directives app running on http://localhost:4200

Figure 2.1 – ad-attribute-directives app running on http://localhost:4200

How to do it…

So far, the app has a search input box and a paragraph text. We need to be able to type a search query into the search box so that we can highlight the matching text in the paragraph. Here are the steps on how we achieve this:

  1. We'll create a property named searchText in the app.component.ts file that we'll use as a model for the search-text input:
    ...
    export class AppComponent {
      title = 'ad-attribute-directive';
      searchText = '';
    }
  2. Then, we use this searchText property in the app.component.html file with the search input as a ngModel, as follows:
    …
    <div class="content" role="main">
      ...
        <input [(ngModel)]="searchText" type="text"     class="form-control" placeholder="Search Text"     aria-label="Username" aria-describedby=    "basic-addon1">
      </div>

    Important note

    Notice that ngModel doesn't work without FormsModule, and so we've already imported FormsModule into our app.module.ts file.

  3. Now, we'll create an attribute directive named highlight by using the following command inside our ad-attributes-directive project:
     ng g d directives/highlight
  4. The preceding command generated a directive that has a selector called appHighlight. See the How it works… section for why that happens. Now that we have the directive in place, we'll create two inputs for the directive to be passed from AppComponent (from app.component.html)—one for the search text and another for the highlight color. The code should look like this in the highlight.directive.ts file:
     import { Directive, Input } from '@angular/core';
    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective {
      @Input() highlightText = '';
      @Input() highlightColor = 'yellow';
      constructor() { }
    }
  5. Since we have the inputs in place now, let's use the appHighlight directive in app.component.html and pass the searchText model from there to the appHighlight directive:
    <div class="content" role="main">
      ...
      <p class="text-content" appHighlight   [highlightText]="searchText">
        ...
      </p>
    </div>
  6. We'll listen to the input changes now for the searchText input, using ngOnChanges. Please see the Using ngOnChanges to intercept input property changes recipe in Chapter 1, Winning Components Communication, for how to listen to input changes. For now, we'll only do a console.log when the input changes:
    import { Directive, Input, SimpleChanges, OnChanges } from '@angular/core';
    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective implements OnChanges {
      ...
      ngOnChanges(changes: SimpleChanges) {
        if (changes.highlightText.firstChange) {
          return;
        }
        const { currentValue } = changes.highlightText;
        console.log(currentValue);
      }
    }
  7. Now, we'll write some logic for what to do when we actually have something to search for. For this, we'll first import the ElementRef service so that we can get access to the template element on which our directive is applied. Here's how we'll do this:
    import { Directive, Input, SimpleChanges, OnChanges, ElementRef } from '@angular/core';
    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective implements OnChanges {
      @Input() highlightText = '';
      @Input() highlightColor = 'yellow';
      constructor(private el: ElementRef) { }
      ...
    }
  8. Now, we'll replace every matching text in our el element with a custom <span> tag with some hardcoded styles. Update your ngOnChanges code in highlight.directive.ts as follows, and see the result:
    ngOnChanges(changes: SimpleChanges) {
        if (changes.highlightText.firstChange) {
          return;
        }
        const { currentValue } = changes.highlightText;
        if (currentValue) {
          const regExp = new RegExp(`(${currentValue})`,       'gi')
          this.el.nativeElement.innerHTML =       this.el.nativeElement.innerHTML.replace       (regExp, `<span style="background-color:       ${this.highlightColor}">\$1</span>`)
        }
     }

    Tip

    You'll notice that if you type a word, it will still just show only one letter highlighted. That's because whenever we replace the innerHTML property, we end up changing the original text. Let's fix that in the next step.

  9. To keep the original text intact, let's create a property name of originalHTML and assign an initial value to it on the first change. We'll also use the originalHTML property while replacing the values:
    ...
    export class HighlightDirective implements OnChanges {
      @Input() highlightText = '';
      @Input() highlightColor = 'yellow';
      originalHTML = '';
      constructor(private el: ElementRef) { }
      ngOnChanges(changes: SimpleChanges) {
        if (changes.highlightText.firstChange) {
          this.originalHTML = this.el.nativeElement.      innerHTML;
          return;
        }
        const { currentValue } = changes.highlightText;
        if (currentValue) {
          const regExp = new RegExp(`(${currentValue})`,       'gi')
          this.el.nativeElement.innerHTML =       this.originalHTML.replace(regExp, `<span       style="background-color: ${this.      highlightColor}">\$1</span>`)
        }
      }
    }
  10. Now, we'll write some logic to reset everything back to the originalHTML property when we remove our search query (when the search text is empty). In order to do so, let's add an else condition, as follows:
    ...
    export class HighlightDirective implements OnChanges {
      ...
      ngOnChanges(changes: SimpleChanges) {
       ...
        if (currentValue) {
          const regExp = new RegExp(`(${currentValue})`,       'gi')
          this.el.nativeElement.innerHTML = this.      originalHTML.replace(regExp, `<span       style="background-color: ${this.      highlightColor}">\$1</span>`)
        } else {
          this.el.nativeElement.innerHTML =       this.originalHTML;
        }
      }
    }

How it works…

We create an attribute directive that takes the highlightText and highlightColor inputs and then listens to the input changes for the highlightText input using the SimpleChanges application programming interface (API) and the ngOnChanges life cycle hook.

First, we make sure to save the original content of the target element by getting the attached element using the ElementRef service, using the .nativeElement.innerHTML on the element, and then saving it to originalHTML property of the directive. Then, whenever the input changes, we replace the text with an additional HTML element (a <span> element) and add the background color to this span element. We then replace the innerHTML property of the target element with this modified version of the content. That's all the magic!

See also

You have been reading a chapter from
Angular Cookbook
Published in: Aug 2021
Publisher: Packt
ISBN-13: 9781838989439
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime