User interface
Angular Components Decorators RxJS SPA Typescript Visual Studio Code

How to Build a Single Selection Group Component in Angular

Welcome to today’s post.

In today’s post I will show how I build a basic yet useful single-selection group component using the Angular Material Chip control. In previous posts I showed how to use Angular Material controls to implement some basic, yet useful components, such as a pagination component, a checkbox group component, and material date pickers.

The component I will be implementing is a single selection control that has a list of available choices. When a choice is made, the current value is retrievable as a getter property within the component. The available choices are configurable by a setter property. The visual for the component is shown below:

The first task is to define the HTML template, which is what I will show in the next section.

Defining the Component HTML Template

The component HTML template consists of a mat-chip-list control, which consists of two parts: a selected item, which is rendered as an inverted chip, and the unselected items, which are chips with light backgrounds.

Any of the items can be selected with a click event. In addition, the click event can be accessed from outside of the component for when we wish to set items manually (from possibly an alternative input mode).

The HTML template for the control is shown below:

<mat-chip-list #chiplist aria-label="Single Selection">
    <mat-chip *ngIf="selectedItem" 
        name="selectionItem" 
        (click)="onSelect(item)">  
        {{selectedItem}}
    </mat-chip>
    <mat-chip name="selectionItems" 
      *ngFor="let item of selectableItems"
      (click)="onSelect(item)">  
      {{item}}
    </mat-chip>
</mat-chip-list>

In the next section, I will implement the component class.

Implementation of the Component Typescript Class

The component typescript consists of imports for the material chip control including chip event change observable:

import { Input, QueryList, ViewChild } from '@angular/core';
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { MatChip, MatChipSelectionChange } from '@angular/material';      

@Component({
    selector: 'app-single-select-group',
    templateUrl: './single-select-group.component.html',
    styleUrls: ['./single-select-group.component.scss']
})
export class SingleSelectGroupComponent implements OnInit {

To interact with the chips, we declare a child view which consists of a list of MatChips:

@ViewChild("chiplist", {static: false}) chipList = new QueryList<MatChip>();

For our component to be able to emit changes in chip selections we provide an output decorator which consists of an EventEmitter for the MatChipSelectionChange event object:

@Output() selectionChange: EventEmitter<MatChipSelectionChange> = 
    new EventEmitter(); 

Next, our properties require obtaining or setting the value of the selected chip value. We use an input decorator to provision a getter and setter for the currently selected value:

public _selectedItem: string = "";

@Input('selectedItem')
set selectedItem(value: string)
{
    if (!value)
        return;

    this._selectedItem = value;
}
get selectedItem()
{
    return this._selectedItem;
}

To configure the available items that can be selected within our control we use an input decorator to provision a getter and setter as shown:

private _availableItems: string[] = [];

@Input('availableItems') 
set availableItems (value: string[])
{
    if (!value)
        return;      
    
    this._availableItems = value;
    this._selectedItem = value[0];
}

get availableItems()
{
    return this._availableItems;
}

The selectable items within our control are those items which are not currently selected. The function to retrieve these items is shown below:

get selectableItems()
{
    return this._availableItems.filter(i => i.valueOf() !== this.selectedItem);
}

The event to select an item based on the currently selected value from the HTML template click() event is shown below:

onSelect(value: any)
{
    this.selectionChange.emit(value);
    this._selectedItem = value;
    const firstChip = this.chipList['chips'].first;
    firstChip.select();
    firstChip.focus();
}

A useful method that is used to set the selected item, select, and set the focus on the Nth chip, and emit the selected value to the selectionChange event emitter in the list is shown below:

selectNthItem(item: number)
{
    const firstChip = this.chipList['chips']._results[item];
    if (!firstChip)
        return;
    
    firstChip.select();
    firstChip.focus();
    this.selectionChange.emit(firstChip.value.trim());
    this.selectedItem = firstChip.value.trim();
}

Now that the component is completed, I will show how to use it within a parent component.

Using the Selection Component within a Parent Component

To use the component from within a parent host component, we declare the selection component within the HTML template of the parent component as shown:

<div>
    <label class="boldedLabel" for="selectGenre1">
        Genre: {{selectedGenre}}
    </label>
    
    <br />

    <app-single-select-group #singleSelect
        [availableItems]="genres" 
        [selectedItem]="selectedGenre" 
        (selectionChange)="eventSelection($event)">
    </app-single-select-group>
</div>

The selectedGenre component property holds the currently selected genre. The genres array holds the list of genres that is assigned to the availableItems of the selection control.

genres = [];
selectedGenre: string;

To retrieve the genres, we subscribe to a service which populates the genres component property. The default selected genre, selectedGenre is set to the first item in the list of available genres.

genre$: Subscription;

…

ngOnInit() {
    this.selectedGenre = "Fiction";
        
    this.genre$ = this.lookupService.getGenres().subscribe(
        genres => {
            if (!genres)
                return;
            console.log("number of genres  = " + genres.length);
      
            this.genres = genres.map(g => g.genre);
            this.selectedGenre = this.genres[0];
            this.selectedGenre = this.selectedGenre.charAt(0).
            toUpperCase() + this.selectedGenre.slice(1);
            this.selectedGenre$.next(this.selectedGenre);
        }
    );
	…
}

When a selection change is emitted from the control, the event handler in the parent control assigns the selected genre then sets the next value in the selectedGenre$ multicast observable:

selectedGenre$ = new Subject<string>();

…

eventSelection(value: any){
    this.selectedGenre = value;
    this.selectedGenre$.next(value);
}

Within our component we subscribe to the selectedGenre$ observable and then pass the value to a query to produce output for our grid.

We have seen how to create a basic single selection control using the Angular Material Chips control.

We have also seen how to implement the following features of a component:

  1. Creating getters and setters using the @Input and @Output decorators.
  2. Using EventEmitter to emit custom events.
  3. Applying changes to the Material Chips control using the MatChipSelectionChange event emitter.
  4. Setting up, configuring, and interacting with the custom selection component within a parent host component.

More details on the Material Chip control including its rich API are described on the Angular site.

That is all for today’s post.

I hope you have found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial