/* Search Dropdown Component*/ /* The Whole Application uses only this drop down for uniqueness It Consists of a input with no pointer events that populates the selected value, the dropDown menu is a card with overlay(z-index) included, menu options are taken as the input from the parent which is consuming search dropDown component using 'options' Input, options can be of 2 types: 1. object Array 2. string Array. when the options are of type object array, then "textAttribute" input needs to be mandatorily provided. and "valueAttribute" Input can be provided. "textAttribute" , "valueAttribute" Inputs takes value as a string. which should be the property name in the JSON. default value can be set in from parent form by initializing the formControl while creation, that value is displayed in the input(dropDown selected option display), once an item is selected from the dropdown, formControl value is updated automatically by angular (similar to other angular form Controls) and a change event can also be generated by having an Input 'returnOptionObj' = true which emits an output event "selectedDDOptionObject" on selection of an option in dropdown menu. event outputs the option object dropdown menu list is expanded(shown) when user clicks on the span or caret-down icon, and collapses(hides) when an item is selected from doropdown menu. using sddDisable property we can disable the dropdown. uisng sddPlaceHolder Input placeholder for dropdown can be set from parent component . */ import { Component, OnInit, Input, ViewEncapsulation, Output, EventEmitter, SimpleChanges, OnChanges, HostListener, ElementRef } from '@angular/core'; import { FormControl } from '@angular/forms'; @Component({ selector: 'app-search-dropdown', templateUrl: './search-dropdown.component.html', styleUrls: ['./search-dropdown.component.css'], encapsulation: ViewEncapsulation.None, }) export class SearchDropdownComponent implements OnInit , OnChanges { @Input() customSearchDropdownStyle: {}; // formControl from parent form consuming app-search-dropdown. @Input() control: FormControl; @Input() returnOptionObj = false; /* should we display the search input in the dropdown menu or not; by default true */ @Input() withSearch = true; @Input() options: any[]; // disables the dropdown @Input() sddDisable; //REVIEW : valueExpr @Input() valueAttribute: string; @Input() textAttribute: string; @Input() sddPlaceHolder =''; @Input() hasError = false; // That's the reuseable control @Output() selectedDDOptionObject = new EventEmitter(); expandDropDown = false; displayOptionsList : string[] filteredOptions: string[] = [] ; disableDropDown = false; searchInputCntrl: FormControl = new FormControl(); controlText: any = ''; clickedcount = 0; constructor(private eRef: ElementRef) {} ngOnInit() { if(this.options instanceof Array && ( this.textAttribute || typeof this.options[0] === 'string')) { this.displayOptionsList = this.options; } this.filteredOptions = this.displayOptionsList; if(this.control && this.control.value && this.options) { if(this.valueAttribute && this.textAttribute) { let option = this.options.find((option) => { return option[this.valueAttribute] === this.control.value }); if(option) this.controlText = option[this.textAttribute]; } else if ( this.textAttribute && this.options ) { let option = this.options.find((option) => { return option[this.textAttribute] === this.control.value }); if(option) this.controlText = option[this.textAttribute]; } else if(this.options instanceof Array) { if (typeof this.options[0] === 'string') { let optionText = this.options.find((option) => { return option === this.control.value }); if (optionText) this.controlText = optionText; } } } if (this.sddDisable == null || this.sddDisable == undefined) { if (this.control.enabled ){ this.disableDropDown = false; } else { this.disableDropDown = true; } } else { this.disableDropDown = this.sddDisable; } this.searchInputCntrl.valueChanges.subscribe(filter => { if( this.textAttribute) { this.filteredOptions = this.displayOptionsList.filter(option => option[this.textAttribute].toLowerCase().indexOf(filter.toLowerCase()) !== -1); } else { this.filteredOptions = this.displayOptionsList.filter(option => option.toLowerCase().indexOf(filter.toLowerCase()) !== -1); } }); this.control.valueChanges.subscribe((controlValue) => { if(this.valueAttribute && this.textAttribute && this.options) { let option = this.options.find((option) => { return option[this.valueAttribute] === controlValue }); if(option) this.controlText = option[this.textAttribute]; else this.controlText = null; } else if ( this.textAttribute && this.options) { let option = this.options.find((option) => { return option[this.textAttribute] === controlValue }); if(option) this.controlText = option[this.textAttribute]; else this.controlText = null; } else if(this.options instanceof Array) { if (typeof this.options[0] === 'string') { let optionText = this.options.find((option) => { return option === controlValue }); if (optionText) this.controlText = optionText; else this.controlText = null; } } else { this.controlText = null; } if (this.sddDisable == null || this.sddDisable == undefined) { if (this.control.enabled ){ this.disableDropDown = false; } else { this.disableDropDown = true; } } }) } ngOnChanges(changes: SimpleChanges): void { if ( changes.sddDisable ) { this.disableDropDown = this.sddDisable; } if(changes.options) { if ( this.textAttribute && this.options ) { this.displayOptionsList = this.options; } else if(this.options instanceof Array){ if (typeof this.options[0] === 'string') { this.displayOptionsList = this.options; } } this.filteredOptions = this.displayOptionsList; } if(this.control) { if ( !this.control.touched && this.clickedcount != 0 ) { console.warn("ClickedCount set to 0 by marking to untouch"); this.clickedcount = 0; } else if ( this.control.touched && this.clickedcount != 0) { console.warn("ClickedCount set to 0 by marking to touch"); this.clickedcount = 0; } if ( this.control.value && this.options) { if(this.valueAttribute && this.textAttribute) { let option = this.options.find((option) => { return option[this.valueAttribute] === this.control.value }); if(option) this.controlText = option[this.textAttribute] } else if ( this.textAttribute ) { let option = this.options.find((option) => { return option[this.textAttribute] === this.control.value }); if(option) this.controlText = option[this.textAttribute] } else if(this.options instanceof Array) { if (typeof this.options[0] === 'string') { let optionText = this.options.find((option) => { return option === this.control.value }); if (optionText) this.controlText = optionText; } } } if (this.sddDisable == null || this.sddDisable == undefined) { if (this.control.enabled ){ this.disableDropDown = false; } else { this.disableDropDown = true; } } } this.searchInputCntrl.valueChanges.subscribe(filter => { if (this.textAttribute ) { this.filteredOptions = this.displayOptionsList.filter(option => option[this.textAttribute].toLowerCase().indexOf(filter.toLowerCase()) !== -1); } else { this.filteredOptions = this.displayOptionsList.filter(option => option.toLowerCase().indexOf(filter.toLowerCase()) !== -1); } }); } expandDropDownMenu() { if(!this.disableDropDown) { this.clickedcount++; if ( this.clickedcount >=2 ) { this.control.markAsTouched(); this.clickedcount = 0; } this.expandDropDown = !this.expandDropDown } } selectedOptionIndex( selectedOption ) { this.clickedcount = 0; if (this.withSearch) this.searchInputCntrl.setValue(''); if (this.valueAttribute && this.textAttribute ) { this.control.patchValue(selectedOption[this.valueAttribute]); this.controlText = selectedOption[this.textAttribute] ; } else if ( this.textAttribute ) { this.control.patchValue(selectedOption[this.textAttribute]); this.controlText = selectedOption[this.textAttribute]; } else { this.control.patchValue(selectedOption); this.controlText = selectedOption; } if (this.returnOptionObj) { this.selectedDDOptionObject.emit(selectedOption); } this.expandDropDown = false; } @HostListener('document:click', ['$event']) clickout(event) { if(!this.eRef.nativeElement.contains(event.target)) { if (this.expandDropDown) { if(!this.disableDropDown) { this.clickedcount++; if ( this.clickedcount >=2 ) this.control.markAsTouched(); this.clickedcount = 0; } } this.expandDropDown = false; // this.expandDropDownMenu(); } } }