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

Creating a directive to calculate the read time for articles

In this recipe, you'll create an attribute directive to calculate the read time of an article, just like Medium. The code for this recipe is highly inspired by my existing repository on GitHub, which you can view at the following link: https://github.com/AhsanAyaz/ngx-read-time.

Getting ready

The project for this recipe resides in chapter02/start_here/ng-read-time-directive:

  1. Open the project in 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.2 – ng-read-time-directive app running on http://localhost:4200

Figure 2.2 – ng-read-time-directive app running on http://localhost:4200

How to do it…

Right now, we have a paragraph in our app.component.html file for which we need to calculate the read time in minutes. Let's get started:

  1. First, we'll create an attribute directive named read-time. To do that, run the following command:
    ng g directive directives/read-time
  2. The preceding command created an appReadTime directive. We'll first apply this directive to div inside the app.component.html file with the id property set to mainContent, as follows:
    ...
    <div class="content" role="main" id="mainContent" appReadTime>
    ...
    </div>
  3. Now, we'll create a configuration object for our appReadTime directive. This configuration will contain a wordsPerMinute value, on the basis of which we'll calculate the read time. Let's create an input inside the read-time.directive.ts file with a ReadTimeConfig exported interface for the configuration, as follows:
    import { Directive, Input } from '@angular/core';
    export interface ReadTimeConfig {
      wordsPerMinute: number;
    }
    @Directive({
      selector: '[appReadTime]'
    })
    export class ReadTimeDirective {
      @Input() configuration: ReadTimeConfig = {
        wordsPerMinute: 200
      }
      constructor() { }
    }
  4. We can now move on to getting the text to calculate the read time. For this, we'll use the ElementRef service to retrieve the textContent property of the element. We'll extract the textContent property and assign it to a local variable named text in the ngOnInit life cycle hook, as follows:
    import { Directive, Input, ElementRef, OnInit } from '@angular/core';
    ...
    export class ReadTimeDirective implements OnInit {
      @Input() configuration: ReadTimeConfig = {
        wordsPerMinute: 200
      }
      constructor(private el: ElementRef) { }
      ngOnInit() {
        const text = this.el.nativeElement.textContent;
      }
    }
  5. Now that we have our text variable filled up with the element's entire text content, we can calculate the time to read this text. For this, we'll create a method named calculateReadTime by passing the text property to it, as follows:
    ...
    export class ReadTimeDirective implements OnInit {
      ...
      ngOnInit() {
        const text = this.el.nativeElement.textContent;
        const time = this.calculateReadTime(text);
      }
      calculateReadTime(text: string) {
        const wordsCount = text.split(/\s+/g).length;
        const minutes = wordsCount / this.configuration.    wordsPerMinute;
        return Math.ceil(minutes);
      }
    }
  6. We've got the time now in minutes, but it's not in a user-readable format at the moment since it is just a number. We need to show it in a way that is understandable for the end user. To do so, we'll do some minor calculations and create an appropriate string to show on the user interface (UI). The code is shown here:
    ...
    @Directive({
      selector: '[appReadTime]'
    })
    export class ReadTimeDirective implements OnInit {
    ...
      ngOnInit() {
        const text = this.el.nativeElement.textContent;
        const time = this.calculateReadTime(text);
        const timeStr = this.createTimeString(time);
        console.log(timeStr);
      }
    ...
      createTimeString(timeInMinutes) {
        if (timeInMinutes === 1) {
          return '1 minute';
        } else if (timeInMinutes < 1) {
          return '< 1 minute';
        } else {
          return `${timeInMinutes} minutes`;
        }
      }
    }

    Note that with the code so far, you should be able to see the minutes on the console when you refresh the application.

  7. Now, let's add an @Output() to the directive so that we can get the read time in the parent component and display it on the UI. Let's add it as follows in the read-time.directive.ts file:
    import { Directive, Input, ElementRef, OnInit, Output, EventEmitter } from '@angular/core';
    ...
    export class ReadTimeDirective implements OnInit {
      @Input() configuration: ReadTimeConfig = {
        wordsPerMinute: 200
      }
      @Output() readTimeCalculated = new   EventEmitter<string>();
      constructor(private el: ElementRef) { }
    ...
    }
  8. Let's use the readTimeCalculated output to emit the value of the timeStr variable from the ngOnInit() method when we've calculated the read time:
    ...
    export class ReadTimeDirective {
    ...
      ngOnInit() {
        const text = this.el.nativeElement.textContent;
        const time = this.calculateReadTime(text);
        const timeStr = this.createTimeString(time);
        this.readTimeCalculated.emit(timeStr);
      }
    ...
    }
  9. Since we emit the read-time value using the readTimeCalculated output, we have to listen to this output's event in the app.component.html file and assign it to a property of the AppComponent class so that we can show this on the view. But before that, we'll create a local property in the app.component.ts file to store the output event's value, and we'll also create a method to be called upon when the output event is triggered. The code is shown here:
    ...
    export class AppComponent {
      readTime: string;
      onReadTimeCalculated(readTimeStr: string) {
        this.readTime = readTimeStr;
      } 
    }
  10. We can now listen to the output event in the app.component.html file, and we can then call the onReadTimeCalculated method when the readTimeCalculated output event is triggered:
    ...
    <div class="content" role="main" id="mainContent" appReadTime (readTimeCalculated)="onReadTimeCalculated($event)">
    ...
    </div>
  11. Now, we can finally show the read time in the app.component.html file, as follows:
    <div class="content" role="main" id="mainContent" appReadTime (readTimeCalculated)="onReadTimeCalculated($event)">
      <h4>Read time = {{readTime}}</h4>
      <p class="text-content">
        Silent sir say desire fat him letter. Whatever     settling goodness too and honoured she building     answered her. ...
      </p>
    ...
    </div>

How it works…

The appReadTime directive is at the heart of this recipe. We use the ElementRef service inside the directive to get the native element that the directive is attached to, then we take out its text content. The only thing that remains then is to perform the calculation. We first split the entire text content into words by using the /\s+/g regular expression (regex), and thus we count the total words in the text content. Then, we divide the word count by the wordsPerMinute value we have in the configuration to calculate how many minutes it would take to read the entire text. Easy peasy, lemon squeezy.

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 €18.99/month. Cancel anytime