-
Book Overview & Buying
-
Table Of Contents
-
Feedback & Rating

Angular Cookbook
By :

In this recipe, you’ll create an attribute directive to calculate the read time of an article, just like Medium (https://medium.com), which is a platform for sharing articles and blog posts. 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.
The app that we are going to work with resides in start/apps/chapter02/ng-read-time-directive
inside the cloned repository:
npm run serve ng-read-time-directive
This should open the app in a new browser tab and you should see the following:
Figure 2.3: ng-read-time-directive app running on http://localhost:4200
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:
read-time
. To do that, run the following command from the project root and select the @nx/angular:directive schematics
when asked:
cd start && nx g directive read-time --directory apps/chapter02/ng-read-time-directive/src/app/directives --standalone=false
If asked, choose the @nx/angular:directive
schematics
and choose the “As provided” action.
Note that we’re using --standalone = false
in the command. That is because we have an NgModule
based application and the AppComponent
is not a standalone component.
ReadTimeDirective
and has appReadTime
as the selector. We’ll apply this directive to the div
that has id
set to mainContent
inside the app.component.html
file as follows:
...
<div class="content" role="main" id="mainContent"
appReadTime>
...
</div>
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() { }
}
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;
}
}
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);
console.log({ readTime: time });
}
calculateReadTime(text: string) {
const wordsCount = text.split(/\s+/g).length;
const minutes = wordsCount / this.configuration.
wordsPerMinute;
return Math.ceil(minutes);
}
}
If you look at the console now, you should see an object containing the readTime
property being logged. The value of readTime
is the time in minutes:
Figure 2.4: Console log showing the time in minutes
...
@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({ readTime: timeStr });
}
...
createTimeString(timeInMinutes: number) {
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.
@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) { }
...
}
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);
}
...
}
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;
}
}
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>
app.component.html
file, as follows:
<div class="content" role="main" id="mainContent" appReadTime
(readTimeCalculated)="onReadTimeCalculated($event)">
<h4 class="text-3xl">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>
If you now go to http://localhost:4200
, you should be able to see the read time in the app, as shown in the following image:
Figure 2.5: Read time being displayed in the app
The appReadTime
directive is at the heart of this recipe. While creating the directive, we create it as a non-standalone directive because the application itself is bootstrapped using an NgModule instead of a standalone AppComponent
. We use the ElementRef
service inside the directive to get the native element that the directive is attached to and 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. Finally, we make it readable in a better way using the createTimeString
method. Easy peasy, lemon squeezy.
ngx-read-time
library: https://github.com/AhsanAyaz/ngx-read-timeChange the font size
Change margin width
Change background colour