EASY DOING

Angular Material extension

Multiple select with an auto-badge

Dirk Arendt

--

The Angular Material UI component library offers many high-quality components, such as an easy-to-use select with multiple selection element.

It’s nice but if many items are already selected you can’t directly see how many exactly [1a]. This article aims to solve this problem by adding a badge in the top right corner when the selected items are displayed with three dots cut off.

[1] original (a) and extented component (b)

First we add a small CSS enhancement [3] to place the pop-up below [2b] the component.

[2] original (a) and CSS-fix component (b)
[3] positioning the overlay at the bottom of the component

A quick and easy auto-badge functionality [4] is possible with the following code:

<mat-form-field appearance="fill"
[matBadge]="toppings.value?.length"
[matBadgeHidden]="toppings.value?.length===0"
>
[4] quick and easy auto-badge functionality

It’s nice but I would like to see the badge only if the three dots are visible. To check if an html-element is truncated we can use following code:

// Check if an element is truncated.
isTruncated(el: any) {
return el.scrollWidth > el.clientWidth;
}

Now it will be a little bit difficult! The <mat-select>-element has a (selectionChange)-event, but the three points are only rendered after the event has been called.

<mat-select (selectionChange)="onSelectionChange($event)"

We can handle this problem with a dom-changed event. Allen Kim wrote a cool article how to monitor DOM changes in Angular by using a directive. After adding this directive into your project you have only to make it usable via adding it to the declarations of the main.ts file:

import { DomChangedDirective } from './app/watch-dom-tree';@NgModule({
declarations: [DomChangedDirective]
})

Now we have to update the [matBadgeHidden]-property and customize the trigger label by adding a <mat-select-trigger>-element:

<mat-form-field appearance="fill" [matBadge]="toppings.value?.length" [matBadgeHidden]="!showBadge">
<mat-label>Toppings</mat-label>
<mat-select [formControl]="toppings" multiple>
<mat-option *ngFor="let topping of toppingList" [value]="topping">{{topping}}</mat-option>
<mat-select-trigger>
<span watchDomTree (dom-changed)="handleDomChange($event)" [innerText]="toppings.value"></span>
</mat-select-trigger>

</mat-select>
</mat-form-field>

In the associated typescript file we need following code:

showBadge: boolean = false;handleDomChange(data: Event) {
var SPAN_watchdomtree = data.target as HTMLSpanElement;
var MAT_SELECT_TRIGGER = SPAN_watchdomtree.parentNode as HTMLElement;
var SPAN_matSelectValueText = MAT_SELECT_TRIGGER.parentNode as HTMLSpanElement;
if (SPAN_matSelectValueText === null) {
return;
}
var DIV_matSelectValue = SPAN_matSelectValueText.parentNode as HTMLDivElement;if (DIV_matSelectValue === null) {
this.showBadge = false;
return;
}
this.showBadge = this.isTruncated(DIV_matSelectValue);
}
// Check if an element is truncated.
isTruncated(el: any) {
return el.scrollWidth > el.clientWidth;
}

Et voilà it’s working…

Here is the demo

Hope you like this. Leave a comment if you have any questions, notes or suggestions for improvement.

Thanks For Reading, Follow Me For More

--

--