Angular: Detecting text overflow

Stephen
3 min readSep 26, 2019

http://g.recordit.co/iHkPeahye1.gif

You probably deal with text overflow many times with css like:

However, it’s the browser to handle the effect when the text becomes overflowed, it will cut out the overflowed text and append … (three dots) in the end.

Imagine that instead of showing …, we would want to show (#count) to indicate the total count of items, which means we will have to “detect” text overflowed.

The concept of text ‘Overflow’ is actually the content in the container element is larger than the container element’s width, and when the content is larger than the container element, the container element will become scrollable, which implies the container element has a scrollbar and its scrollWidth is larger than element’s offsetWidth.

Yeah!

Now we can detect if the text overflow with ngIf=”isTextOverflow(elementId)”, like this:

Sounds great, right? Not necessary, because ExpressionChangedAfterItHasBeenCheckedError is coming to you.

ExpressionChangedAfterItHasBeenCheckedError

ExpressionChangedAfterItHasBeenCheckedError is a classical error when your component tries to ‘change’/’update’ too often.

Before going into deep to look the cause of this error, we shall refresh the memory about Angular’s digest cycle and change detection.

Digest Cycle is a process to examine every node if it has changes (different than the previous examination), if there is no changes at all, that means the nodes are stable, it doesn’t need to update anything to DOM, so it just completes a digest cycle.

If it has changes, these changes suppose to be updated to DOM. However traversing every node and updating DOM could take some time and there is a chance the examined nodes in this digest cycle introduce new changes again. Angular doesn’t want to do the examination over and over, going into a Digest Hell, which will lock down the browser’s resources. Thus it thows this ExpressionChangedAfterItHasBeenCheckedError. It’s the developer’s responsibility to prevent introducing changes right after the change detection has been proceeded on this element.

Now we know what ExpressionChangedAfterItHasBeenCheckedError is, then who is the cause to introduce the improper change?

You probably can see it’s isTextOverflow().

When the component is initializing, the first time change detection is checking isTextOverflow(), and it will return false. Then it updates DOM with all changes, including displaying the team list inside ng-container, and traverses every node again to see if it becomes stable. However when the team list is displayed and it’s larger than its container, that makes isTextOverflow() will return true this time.

Ops, here comes ExpressionChangedAfterItHasBeenCheckedError.

Well, how we are going to solve this issue?

That’s simple, tell Angular DO NOT perform repeatly change detection on my component! Instead, my component will notify Angular when it needs change detection.

Use ChangeDetectionStrategy.OnPush:

ChangeDetectionStrategy.OnPush

With ChangeDetectionStrategy.OnPush, your component will be ignored from the digest cycle’s change detection examination so you won’t see this ExpressionChangedAfterItHasBeenCheckedError anymore.

And the next step is to determine when we should notify Angular to examine our component’s changes.

It’s probably when

“The team list content becomes larger than its container div element”

User (resized) callback:

Angular will trigger the callback onTeamResized() whenever the container <div> is resizing.

Inside onTeamResized(), we just notify Angular by changeDetectorRef.markForCheck().

That’s it. Feel free to share any feedback! Thanks.

--

--