Fri. Apr 19th, 2024

Angular Material offers a powerful set of features for building your Angular application. It’s time to take them to the next level. A project I’m working on needed a search field that would have different purposes throughout the application.

SearchFieldComponent

I came up with a list of requirements for this field that would be common across the application:

  • Wanted to use the mat-form-field with a string input.
  • A hint would sometimes be necessary.
  • Needed a label and placeholder.
  • Had to respond to click the search icon, or hitting Enter in the field.
  • Needed to show an error message if it did not pass validation.

For this solution I did not want to use the base class I had used for my Form-Field components. So started from scratch, kinda.

import {Component, EventEmitter, Input, OnDestroy, Output} from '@angular/core';
import {Subscription} from "rxjs";
import {LanguageService} from "../../../service/language.service";

@Component({
  selector: 'mymiller-search-field',
  templateUrl: './search-field.component.html',
  styleUrls: ['./search-field.component.scss']
})
export class SearchFieldComponent implements OnDestroy {
  @Input("label") label: string = "Search";
  @Input("placeholder") placeHolder: string = "Search";
  @Input("hint") hint: string = "Enter text to search";

  searchText: string = '';
  errorMessage: string = '';

  searchEmitted: string = ''
  searchLabel: string = "search";
  searchSub: Subscription;

  @Output("onSearch") onSearch: EventEmitter<string> = new EventEmitter<string>();
  constructor(private languageService:LanguageService) {
    this.searchLabel = this.languageService.searchLabel;
    this.searchSub = this.languageService.searchSub.subscribe( value => this.searchLabel = value);
  }

  ngOnDestroy() {
    this.searchSub.unsubscribe();
  }

  public search() {
    if(this.searchEmitted !== this.searchText) {
      this.errorMessage = '';
      this.searchEmitted = this.searchText
      this.onSearch.emit(this.searchText);
    }
  }

  public invalid(message:string) {
    this.errorMessage = message;
  }

  keyDownFunction(event:any) {
    if (event.code === "Enter") {
      this.search();
    }
  }
}

Began with three inputs to set the [label], [placeholder], and [hint] for the field. Then needed an (onSearch) emitter to send value when the search icon or Enter key was hit. I added a public method invalid(message:string) to report an error if needed.

Since this is not based on the AbstractFieldComponent, I directly access the LanguageService to get the text for the search label.

The html for this component is relatively simple:

<mat-form-field appearance="legacy" class="full-width">
  <mat-label>{{label}}</mat-label>
  <input matInput [placeholder]="placeHolder" [(ngModel)]="searchText" (keydown)="keyDownFunction($event)">
  <mat-icon matSuffix (click)="search()">search</mat-icon>
  <mat-hint *ngIf="hint !== ''">{{hint}}</mat-hint>
  <mat-error *ngIf="errorMessage !== ''">{{errorMessage}}</mat-error>
</mat-form-field><mat-form-field appearance="legacy" class="full-width">
  <mat-label>{{label}}</mat-label>
  <input matInput [placeholder]="placeHolder" [(ngModel)]="searchText" (keydown)="keyDownFunction($event)" (blur)="search()" >
  <mymiller-icon-button matSuffix icon="search" (click)="search()" [tooltip]="searchLabel"></mymiller-icon-button>
  <mat-hint *ngIf="hint !== ''">{{hint}}</mat-hint>
  <mat-error *ngIf="errorMessage !== ''">{{errorMessage}}</mat-error>
</mat-form-field><mat-form-field appearance="legacy" class="full-width">
  <mat-label>{{label}}</mat-label>
  <input matInput [placeholder]="placeHolder" [(ngModel)]="searchText" (keydown)="keyDownFunction($event)">
  <mat-icon matSuffix (click)="search()">search</mat-icon>
  <mat-hint *ngIf="hint !== ''">{{hint}}</mat-hint>
  <mat-error *ngIf="errorMessage !== ''">{{errorMessage}}</mat-error>
</mat-form-field>

Added a listener for the (keydown) event to check for the Enter key. If the hint was not empty then display it, and the same for the errorMessage. Very simple HTML.

Take note once again I added the full-width class to this.

.full-width {
  width: 100%;
}

This is what the field will look like:

By Jeffery Miller

I am known for being able to quickly decipher difficult problems to assist development teams in producing a solution. I have been called upon to be the Team Lead for multiple large-scale projects. I have a keen interest in learning new technologies, always ready for a new challenge.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.