File

src/app/modules/ontology-exploration/ontology-search/ontology-search.component.ts

Description

Componenet for searching the Ontology nodes.

Implements

OnInit

Metadata

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(ontologyService: OntologySearchService, ga: GoogleAnalyticsService)

Creates an instance of ontology search component.

Parameters :
Name Type Optional Description
ontologyService OntologySearchService No

instance of searchService which provides all the search functionality

ga GoogleAnalyticsService No

Analytics service

Inputs

placeholderText
Type : string

Outputs

selected
Type : EventEmitter

Output event-emitter which emits the id of the OntologyTreeNode whose label was selected by the user in the search-results

Methods

displayFormatter
displayFormatter(option?: SearchResult)

A formatter function to enable different display and selected value

Parameters :
Name Type Optional Description
option SearchResult Yes

a search result entry

Returns : string

a part of the search result entry to be displayed as a display value

onSelect
onSelect(event: MatAutocompleteSelectedEvent)

Callback function triggered when the user selects a value from search results

Parameters :
Name Type Optional Description
event MatAutocompleteSelectedEvent No

instance of MatAutocompleteSelectedEvent

Returns : void
sortBySynonymResult
sortBySynonymResult(this: void, entry: SearchResult)

Sorts by results which have synonyms

Parameters :
Name Type Optional Description
this void No
entry SearchResult No

search result entry

Returns : number

1 or -1

sortLexically
sortLexically(this: void, entry: SearchResult)

Sorts lexically

Parameters :
Name Type Optional Description
this void No
entry SearchResult No

search result entry

Returns : string

lower case value of node label

Properties

autoCompleteOpen
Default value : false

Determines if autocomplete is open or close.

filteredResults$
Type : Observable<SearchResult[]>

Observable which provides the filtered search results

formControl
Default value : new UntypedFormControl('')

Instance of FormControl - tracks the value and validation status of an individual form control

Public ontologyService
Type : OntologySearchService
instance of searchService which provides all the search functionality
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { OntologyTreeNode } from 'ccf-database';
import { get, sortBy } from 'lodash';
import { GoogleAnalyticsService } from 'ngx-google-analytics';
import { Observable } from 'rxjs';
import { filter, map, startWith, switchMap } from 'rxjs/operators';

import { OntologySearchService, SearchResult } from '../../../core/services/ontology-search/ontology-search.service';

/**
 * Componenet for searching the Ontology nodes.
 */
@Component({
  selector: 'ccf-ontology-search',
  templateUrl: './ontology-search.component.html',
  styleUrls: ['./ontology-search.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OntologySearchComponent implements OnInit {
  @Input() placeholderText!: string;

  /**
   * Output event-emitter which emits the id of the OntologyTreeNode whose label was
   * selected by the user in the search-results
   */
  @Output() readonly selected = new EventEmitter<OntologyTreeNode>();

  /**
   * Instance of FormControl - tracks the value and validation status of an individual form control
   */
  formControl = new UntypedFormControl('');

  /**
   * Determines if autocomplete is open or close.
   */
  autoCompleteOpen = false;

  /**
   * Observable which provides the filtered search results
   */
  filteredResults$!: Observable<SearchResult[]>;

  /**
   * Creates an instance of ontology search component.
   *
   * @param ontologyService instance of searchService which provides all the search functionality
   * @param ga Analytics service
   */
  constructor(
    public ontologyService: OntologySearchService,
    private readonly ga: GoogleAnalyticsService,
  ) {}

  /**
   * on-init lifecycle hook for this component -
   * gets the searched value from the view, sends it to the filter function in the OntologyService,
   * and gets the search results from the service
   */
  ngOnInit(): void {
    const valueChanges = this.formControl.valueChanges as Observable<string>;
    this.filteredResults$ = valueChanges.pipe(
      filter((value) => typeof value === 'string'),
      startWith(''),
      switchMap((value) => this.ontologyService.filter(value)),
      map((searchResults) => sortBy(searchResults, [this.sortBySynonymResult, 'index', this.sortLexically])),
    );
  }

  /**
   * A formatter function to enable different display and selected value
   *
   * @param option a search result entry
   * @returns a part of the search result entry to be displayed as a display value
   */
  displayFormatter(option?: SearchResult): string {
    return (option?.displayLabel ?? []).join('');
  }

  /**
   * Sorts by results which have synonyms
   *
   * @param entry search result entry
   * @returns 1 or -1
   */
  sortBySynonymResult(this: void, entry: SearchResult): number {
    return entry.displayLabel.join().includes('(') ? 1 : -1;
  }

  /**
   * Sorts lexically
   *
   * @param entry search result entry
   * @returns lower case value of node label
   */
  sortLexically(this: void, entry: SearchResult): string {
    return entry.node.label.toLowerCase();
  }

  /**
   * Callback function triggered when the user selects a value from search results
   *
   * @param event instance of MatAutocompleteSelectedEvent
   */
  onSelect(event: MatAutocompleteSelectedEvent): void {
    const node = get(event, ['option', 'value', 'node']) as OntologyTreeNode;
    if (node) {
      this.ga.event('search', 'ontology_search', node.id);
      this.selected.emit(node);
      this.formControl.reset();
    }
  }
}
<form class="ccf-ontology-search" [class.autocomplete-open]="autoCompleteOpen">
  <mat-form-field appearance="outline" class="field" subscriptSizing="dynamic">
    <mat-icon matPrefix color="primary" class="search">search</mat-icon>
    <input
      class="input"
      type="text"
      [placeholder]="placeholderText"
      matInput
      [formControl]="formControl"
      [matAutocomplete]="autocomplete"
    />

    <mat-autocomplete
      class="ccf-ontology-search detached autocomplete"
      [displayWith]="displayFormatter"
      (optionSelected)="onSelect($event)"
      (opened)="autoCompleteOpen = true"
      (closed)="autoCompleteOpen = false"
      #autocomplete
    >
      <div class="results-container">
        <mat-option *ngFor="let option of filteredResults$ | async" [value]="option">
          <span class="prefix">{{ option.displayLabel[0] }}</span>
          <span class="search-term">{{ option.displayLabel[1] }}</span>
          <span class="suffix">{{ option.displayLabel[2] }}</span>
        </mat-option>
      </div>
    </mat-autocomplete>
  </mat-form-field>
</form>

./ontology-search.component.scss

.ccf-ontology-search {
  width: 100%;

  .field {
    width: 100%;
    padding-bottom: 1.34375em;
    font-size: inherit;
    line-height: 1.125;
    letter-spacing: normal;

    ::ng-deep .mat-mdc-text-field-wrapper {
      margin: 0;
      height: 2.75rem;

      .mat-mdc-form-field-flex {
        align-items: center;
        height: 2.75rem;

        .mat-mdc-form-field-infix {
          padding: 1rem 0;

          input {
            margin-left: 0.4rem;
          }
        }

        .mat-mdc-form-field-icon-prefix {
          padding: 0;
        }

        mat-icon {
          padding-right: 0;
        }
      }
    }
  }
}

::ng-deep .ccf-ontology-search.detached {
  border: 0.125rem solid;
  border-top: none;
  border-radius: 0 0 0.25rem 0.25rem;
  max-height: 18rem;
  padding: 0;
  box-shadow: none;

  .results-container {
    width: 23.75rem;
    margin-bottom: 1rem;
    overflow: auto;
    max-height: 15rem;
    scrollbar-width: thin;

    mat-option {
      min-height: 1.5rem;
      font-size: 1rem;
      font-weight: 500;

      .search-term {
        text-decoration: underline;
      }
    }
  }
}

.autocomplete-open {
  ::ng-deep .mdc-notched-outline {
    .mdc-notched-outline__leading {
      border-radius: 0.25rem 0 0 0;
      border-bottom: none;
    }

    .mdc-notched-outline__trailing {
      border-radius: 0 0.25rem 0 0;
      border-bottom: none;
    }
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""