Learn how to easily truncate and append an ellipsis to content using an Angular directive.
Goals
The goals for our ellipsis directive are:
- A simple attribute directive that can be applied to an element with a specified fixed height.
- Avoid overflowing the text content beyond the specified fixed height.
- If the text is overflowing, remove the necessary text and append an ellipsis until the text fits within the specified height.
ElementRef
The first concept we need to understand is Angular’s ElementRef instance when using an attribute directive.
We can access this via dependency injection in our attribute directive’s constructor() function.
The nativeElement property on the ElementRef class instance enables us to access the native document object model (DOM) element in the context of the browser.
Once we have access to the element, we can determine if the element’s scrollHeight property value exceeds the element’s clientHeight property value.
Platform Specific
It’s important to note that the strategy that we’ll implement here will be platform specific. To clarify, Angular is not just a framework for building single-page applications in the context of a browser. Rather, Angular can be used for building applications in any context, but is most often used in the context of a browser, Node.js, electron for building desktop applications, and either NativeScript or Ionic for building hybrid mobile applications.
In summary, we’ll use the isPlatformBrowser() function that is available in the @angular/common module for verifying that the application is indeed executing in the context of the browser.
The EllipsisDirective
Here is the full source code for the Angular attribute directive:
@Directive({
selector: '[appEllipsis]'
})
export class EllipsisDirective implements AfterViewInit {
/** The native HTMLElement. */
private get el(): HTMLElement {
return this.elementRef.nativeElement;
}
/** The original innerText; */
private innerText: string;
constructor(
private readonly elementRef: ElementRef,
@Inject(PLATFORM_ID) private readonly platformId
) {}
public ngAfterViewInit(): void {
this.truncate();
}
@HostListener('window:resize')
private onWindowResize() {
this.truncate();
}
private truncate(): void {
// verify execution context is the browser platform
if (!isPlatformBrowser(this.platformId)) {
return;
}
// store the original innerText
if (this.innerText === undefined) {
this.innerText = this.el.innerText.trim();
}
// reset the innerText
this.el.innerText = this.innerText;
// truncate the text and append the ellipsis
const text = this.innerText.split(' ');
while (text.length > 0 && this.el.scrollHeight > this.el.clientHeight) {
text.pop();
this.el.innerText = `${text.join(' ')}…`;
}
}
}
Let’s dive into the the implementation of the EllipsisDirective:
- First, we have a handy
elproperty accessor for accessing thenativeElementproperty on the injectedElementRefclass instance. Note that the return type for this is a genericHTMLElement. This is necessary so we have intellisense and type checking when accessing theinnerTextproperty of the element. - Next, the
innerTextclass property is simple astringin which we will store the original text within the element that the attribute directive is applied to. - In the
constructor()function we are injecting both theElementRefclass instance as well as theplatformIdusing thePLATFORM_IDinjection token. - The directive implements the
AfterViewInitlifecycle interface and it’s associatedngAfterViewInit()lifecycle method. Once the view has been initialized, we can truncate the text based on the element’sscrollHeightandclientHeightvalues. We’ll see this below in thetruncatemethod. - We’ll invoke the
truncate()method both after the view has been initialized as well as when the browser window has been resized. - Using the
HostListener()decorator we can listen for theresizeevent to occur on the global window object, and then truncate the text as necessary. This is especially important if the width of the element is not fixed. - Finally, within the
truncate()method we’ll first verify that we are executing in the context of a browser. Then, we’ll store the initial value of theinnerTextproperty so that if we need to truncate the text later based on the window being resized we can do so. We then truncate the text and append the ellipsis by comparing the value of the element’sscrollHeightto theclientHeight. We’ll continue to remove words until the content fits within the specified height of the element.
Demo
Check out the following stackblitz with an example of the EllipsisDirective: