Refreshing angular ag-grid data - json

I have an ag-grid which is rendering table from a .json file and an external Quick filter that is searching through ag-grid on key input on the filter. After someone searches the search term is displayed in the form of angular material chip with a "X" sign to close the chip with remove function. I want to reload the ag-grid to its default state once someone cancel/close the chip and also to include multiple filters in it using the chip. Here is my sample code, but I'm struggling with setting it up.
Html-
<div class="container">
<mat-form-field class="demo-chip-list" *ngIf="gridApi">
<mat-chip-list #chipList>
<div style="width:100%; margin-left:10%;"><label><span class="search-button">Search Funds</span></label>.
<input class="search-input"
[ngModel]="filterText"
(ngModelChange)=
"gridApi.setQuickFilter
($event)"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)" />.
</div><br/><div style="width:100%; margin-left:10%;"><mat-chip *ngFor="let fruit of fruits"
[selectable]="selectable"
[removable]="removable"
(click)="onGridReady(params)"
(remove)="remove(fruit)">
{{fruit.name}}
<mat-icon matChipRemove *ngIf="removable" ><sup>x</sup></mat-icon></mat-chip></div></mat-chip-list>.
</mat-form-field>
<div class="header" style="display:inline"></div><div> <ag-grid-angular
style="position:absolute;padding-left:5%; bottom:0px;width: 90%; height: 350px;" #agGrid id="myGrid" class="ag-fresh" [columnDefs]="columnDefs"
[animateRows]="true"
[enableRangeSelection]="true"
[enableSorting]="true"
[enableFilter]="true"
[pagination]="true"
(gridReady)="onGridReady($event)">
</ag-grid-angular></div></div>
Component-
#Component({
selector:
'app-funds-table',
templateUrl:
'./funds-table.component.html',
styleUrls:
['./funds-table.component.css']
})
export class
FundsTableComponent
implements OnInit {
visible: boolean = true;
selectable: boolean = true;
removable: boolean = true;
addOnBlur: boolean = true;
// Enter, comma
separatorKeysCodes = [ENTER, COMMA];
fruits = [
{ name: 'ABC' }
];
add(event: MatChipInputEvent): void
{
let input = event.input;
let value = event.value;
// Add our fruit
if ((value || '').trim()) {
this.fruits.push({ name:
value.trim() });
}
// Reset the input value
if (input) {
input.value = '';
}
}
remove(fruit: any): void {
let index =
this.fruits.indexOf(fruit);
if (index >= 0) {
this.fruits.splice(index, 1);
}
}
private gridApi;
private gridColumnApi;
private columnDefs;
private filterText = "";
ngOnInit() {}
constructor(private http:
HttpClient ){
this.columnDefs = [{headerName:
"Ticker", field: "Ticker"},
{headerName: "Id", field: "Id"},
{headerName: "Utilities", field:
"Utilities"}
];
}
onGridReady(params) {
this.gridApi = params.api;
this.gridColumnApi =
params.columnApi;
this.http.get
("/fundsData/fund_info.json". )
.subscribe
(data =>
{this.gridApi.setRowData(data);
});
}
}

According doc:
You can reset filter via direct api call
api.setQuickFilter(''); - empty for reset filter

Related

Angular Material Autocomplete value is always empty and onSelection is never fired

So, I have a component only for the autocomplete by angular material and a parent component which has a form and uses this autocomplete once.
When I click on a value to choose from the autocomplete, it doesn't get saved anywhere and is empty. Also, I would expect the onSelection event to fire, but it doesnt. I tried several ways to get the current value from the form field, but whatever way I try, its always empty.
Here is the ts file for the autocomplete:
interface GroupOption {
group: string;
items: OptGroupItem[];
}
#Component({
selector: 'sh-ui-forms-mat-autocomplete-optgroup-control;',
templateUrl: './mat-autocomplete-optgroup-control.component.html',
styleUrls: ['./mat-autocomplete-optgroup-control.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MatAutocompleteOptgroupControlComponent),
multi: true,
},
],
})
export class MatAutocompleteOptgroupControlComponent
implements OnInit, OnChanges, ControlValueAccessor
{
#Input()
label = '';
/** Liste der Roh-Optionen */
#Input()
groupOptions: OptGroupItem[] = [];
/** Observable für die GUI */
options$?: Observable<GroupOption[]>;
/** interne Liste der Optionen zum Filtern, Prüfen etc. von Items */
private options: GroupOption[] = [];
private emptyOption: OptGroupItem = {
group: '',
name: '',
id: '',
};
control = new FormControl('');
ngOnInit(): void {
this.options$ = this.control.valueChanges.pipe(
startWith(''),
map(value => this.createGroupOptions(value))
);
}
ngOnChanges(c: SimpleChanges): void {
this.options = this.groupByGroup(this.groupOptions);
}
/** Hilfsfunktion zum Umwandeln der Rohoptionen */
private groupByGroup(options: OptGroupItem[]): GroupOption[] {
const groups = groupBy(options, 'group');
return Object.entries(groups).map(([group, items]) => ({
group,
items,
}));
}
/** Filtern und erstellen der Optionen für die GUI */
private createGroupOptions(filter: any): GroupOption[] {
let options = this.groupOptions;
if (filter != null) {
const searchValue = (filter.name || filter).toString().toLowerCase();
options = this.groupOptions.filter(option =>
option.name.toString().toLowerCase().includes(searchValue)
);
}
const groups = this.groupByGroup(options);
return groups;
}
/** Liefert ein Roh-Item aus der Liste der Optionen. */
private getOptionFromId(options: GroupOption[], id: string): OptGroupItem {
let retVal = this.emptyOption;
options.forEach(o => {
const item = o.items.find(i => i.id === id);
if (item != null) {
retVal = item;
}
});
return retVal;
}
onChange = (_: string) => {};
onTouched = () => {};
registerOnChange(fn: (_: string) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
// Behandelt eine Control-Wertänderung von "außen".
writeValue(value: string) {
let currentValue = this.getOptionFromId(this.options, value);
if (currentValue === null) {
currentValue = this.emptyOption;
}
this.control.setValue(currentValue);
}
//Auswahl im autocomplete
onDropdownChange(e: MatAutocompleteSelectedEvent) {
const selectedValue = e.option.value.id;
console.log('onDropdownchange! => selectedValue:');
console.log(selectedValue);
this.onChange(selectedValue);
}
displayName(option: any): string {
return option ? option.name : '';
}
/** Auswahl im Input-Feld */
onValueChange(event: any): void {
event?.preventDefault();
const value = this.control.value;
if (!value) {
this.control.setValue(this.emptyOption);
this.onChange(this.emptyOption.id);
}
}
}
Thats the html file for the autocomplete:
<mat-form-field appearance="outline">
<mat-label>{{ label }}</mat-label>
<ng-container>
<input
type="text"
matInput
[formControl]="control"
[matAutocomplete]="auto"
(blur)="onValueChange($event)"
(keyup.enter)="onValueChange($event)"
/>
<mat-autocomplete
#auto="matAutocomplete"
(optionSelected)="OnDropdownChange($event)"
[displayWith]="displayName"
>
<mat-optgroup
*ngFor="let option of options$ | async"
[label]="option.group"
>
<mat-option *ngFor="let item of option.items" [value]="item">
{{ item.name }}
</mat-option>
</mat-optgroup>
</mat-autocomplete>
</ng-container>
</mat-form-field>
And thats how I call the component from the parent form component:
<sh-ui-forms-mat-autocomplete-optgroup-control
[label]="'Einsatz'"
[groupOptions]="openEinsatzListe$ | async"
formControlName="einsatzId"
></sh-ui-forms-mat-autocomplete-optgroup-control>
openEinsatzListe$ is an observable.
In the picture you can see how the data from the observable/the optionGroups/options for the autocomplete looks like
Question: Why is no value saved when I select a value?
(Btw, there is another bug if you are interested. If you click in the form field, no values are shown. You have to type a space and remove it again to see the values. Maybe you know the solution for that aswell?)
Your form control implementation is wrong, Please check this manual: https://blog.angular-university.io/angular-custom-form-controls/
Basic implementation check here: https://stackblitz.com/edit/angular-ivy-cdvhyz?file=src/app/app.component.ts

Angular: ExpressionChangedAfterItHasBeenCheckedError when trying to disable button

I use mat-dialog to edit details of my profile page. I'm getting an ExpressionChangedAfterItHasBeenCheckedError when I click the 'Edit age' button and the dialog window pops up.
I decided to extract the styling of all edit dialogs into a single edit.component:
edit.component.html
<div class="navigation-control">
<mat-icon (click)="onCancelButtonClicked()"
class="close-button">close</mat-icon>
</div>
<div class="content-main">
<ng-content select=".content-main"></ng-content>
</div>
<div class="content-bot">
<button mat-raised-button
(click)="onCancelButtonClicked()">Cancel</button>
<button mat-raised-button
(click)="onActionButtonClicked()"
[lnDisableButton]="actionButtonDisabled">{{actionButtonValue}}</button>
</div>
edit.component.ts
#Component({ selector: 'ln-edit', ... })
export class EditComponent {
#Input() actionButtonValue: string;
#Input() actionButtonDisabled: boolean;
#Output() cancelButtonClicked = new EventEmitter<void>();
#Output() actionButtonClicked = new EventEmitter<void>();
onCancelButtonClicked() {
this.cancelButtonClicked.emit();
}
onActionButtonClicked() {
this.actionButtonClicked.emit();
}
}
To avoid the ExpressionChangedAfterItHasBeenCheckedError when trying to disable buttons and controls, I used this snippet. But that didn't solve this issue.
disable-button.directive.ts
#Directive({ selector: '[lnDisableButton]' })
export class DisableButtonDirective {
#Input('lnDisableButton') isDisabled = false;
#HostBinding('attr.disabled')
get disabled() { return this.isDisabled; }
}
The following is the contents of a mat-dialog window. This gets instantiated when I click the 'Edit age' button. When I remove the [actionButtonDisabled]="actionButtonDisabled", the error goes away, but obivously I need that line to make the functionality disable the button.
age-edit.component.html
<ln-edit [actionButtonValue]="actionButtonValue"
[actionButtonDisabled]="actionButtonDisabled"
(cancelButtonClicked)="onCancelButtonClicked()"
(actionButtonClicked)="onActionButtonClicked()">
<form [formGroup]="ageForm"
class="content-main">
<ln-datepicker formControlName="birthday"
[appearance]="'standard'"
[label]="'Birthday'"
class="form-field">
</ln-datepicker>
</form>
</ln-edit>
I handle the disabling/enabling the button in the 'ts' part of the mat-dialog popup.
age-edit.component.ts
#Component({ selector: 'ln-age-edit', ... })
export class AgeEditComponent implements OnInit, OnDestroy {
ageForm: FormGroup;
private initialFormValue: any;
actionButtonDisabled = true;
private unsubscribe = new Subject<void>();
constructor(
private editPhotoDialogRef: MatDialogRef<AgeEditComponent>,
private fb: FormBuilder,
#Inject(MAT_DIALOG_DATA) public dialogData: Date) { }
ngOnInit() {
this.initializeAgeForm();
this.loadDataToAgeForm(this.dialogData);
this.trackFormDistinct();
}
private initializeAgeForm(): void {
this.ageForm = this.fb.group({
birthday: null,
});
}
loadDataToAgeForm(birthday: Date | null): void {
if (!birthday) { return; }
this.ageForm.setValue({ birthday });
this.initialFormValue = this.ageForm.value;
}
get birthdayAC() { return this.ageForm.get('birthday') as AbstractControl; }
get actionButtonValue(): string {
return this.birthdayAC.value ? 'Update age' : 'Add age';
}
onCancelButtonClicked(): void {
this.editPhotoDialogRef.close();
}
onActionButtonClicked(): void {
this.editPhotoDialogRef.close({ ... });
}
trackFormDistinct(): void {
this.ageForm.valueChanges.pipe(
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(val => {
(this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
|| this.birthdayAC.value === null)
? this.actionButtonDisabled = true
: this.actionButtonDisabled = false;
});
}
ngOnDestroy() { ... }
}
I suspect this has something to do with content projection, but I'm not sure.
(...or perhaps with my custom 'ln-datepicker'?)
Any ideas?
Thanks.
From what I can tell, the problem resides in trackFormDistinct() method:
trackFormDistinct(): void {
this.ageForm.valueChanges.pipe(
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(val => {
(this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
|| this.birthdayAC.value === null)
? this.actionButtonDisabled = true
: this.actionButtonDisabled = false;
});
}
Looks like because of this.ageForm.valueChanges, will have different values in the 2 change detection cycles. I think this.ageForm.valueChanges emits due to <ln-datepicker>.
In a tree of form controls, if one node calls setValue, all its ancestors will have to be updated. I've written more about how Angular Forms work in this article.
I'm thinking of 2 alternatives:
skip the first emission of ageForm since it indicates the initialization of the form control tree, so this is irrelevant to the logic inside subscribe's callback.
this.ageForm.valueChanges.pipe(
skip(1),
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(/* .... */)
initialize actionButtonDisabled with false, since the error complains that it switched from true to false
actionButtonDisabled = false;

How can I filter my datasource using checkboxes using Angular Material?

I am trying to filter my datatable by clicking a checkbox. Checking one checkbox filters it properly, but checking multiple is the problem.
The Angular Material documentation is very vague regarding proper filtering where a lot of elements are involved. It has something to do with filterPredicate, but there is almost no (or just awfully vague) online documentation.
<mat-form-field appearance="legacy">
<mat-label>Select Province(s)</mat-label>
<mat-select placeholder='Provinces' formControlName="provinceSelector" multiple>
<mat-option *ngFor="let p of provinces" [value]='p.provinceName' (click)='addfilter()'>
{{p.provinceName}} ({{p.adsInProvince}})
</mat-option>
</mat-select>
</mat-form-field>
this.registeredUserService.GetAllAdverts().subscribe(val => {
this.dataSource = new MatTableDataSource<Card>(val);
this.dataSource.paginator = this.paginator;
const myPredicate = (myObject:IProvince,filterString:any)=>
{
let filterObj:IProvince = JSON.parse(filterString);
if(!filterObj.provinceName.includes((obj)=>obj=myObject.provinceName))
{
return false;
}
else
{return true;}
}
this.dataSource.filterPredicate=myPredicate;
myFilter:IProvince={
provinceName:[]
}
addfilter() {
this.myFilter.provinceName=this.search.value;
this.dataSource.filter = JSON.stringify(this.myFilter);
}
export interface Card {
advertDate: any;
advertDescription: any;
advertID: any;
cityName: any;
provinceName: any;
sellerID: any;
sellingPrice: any;
vehicleColor: any;
vehicleMake: any;
vehicleMileage: any;
vehicleModel: any;
vehicleYear: any;
}
export interface IProvince{
provinceName:any[];
}
it should just filter through the selected values...
it does not do it properly.
You are right with the filter predicate. You can define how to filter you dataSource. It is a function which returns true(if filter matches) or false.
const myPredicate = (myObject, filterString) => {
let filterObj: MyFilterObj = JSON.parse(filterString);
if(!filterObj.attributeA.find((obj) => obj == myObject.name) ||myObject.attributeB != filterObject.attributeB) { // <-- edit includes to find
return false;
} else {
return true;
}
}
Put the following code after generating your MatTableDataSource:
this.dataSource.filterPredicate = this.myPredicate.
Use the following method for setting your filter. I always give a type string for determining which filter I want to set.
myFilter: MyFilterObject = { // <-- define this variable in your ngOnInit method
attributeA: [], // <-- this is the place where you can put your selected options
attributeB: null
}
applyFilter(value: any, type: string) {
switch(type) {
case "attributeA":
this.myFilter.attributeA = value;
break;
case "attributeB":
this.myFilter.attributeB = value;
break;
default:
break;
}
this.dataSource.filter = JSON.stringify(this.myFilter);
}

Adding/removing HTML elements dynamically

I have created an angular 8 app that allows a user to choose a date from a angular material date picker control. The app then sends a request a Mongodb using NodeJS to return a list of available daily sessions. This part of my app is working fine. The returned daily sessions are dynamically displayed as buttons in the html. My code iterates through the list of available sessions and creates a button in the html using a angular *ngFor loop. My problem is that when the user then wants to choose another date from the date picker the new list of sessions is returned and the new list of available dates creates more buttons in the html using the *ngFor loop i.e. the new list of buttons is appended to the existing list of buttons. What I actually want is the previously displayed button in the HTML to be deleted from the DOM and the new list of buttons for the newly selected date to be displayed in their place. Does anybody know how to do this? Thanks!
Below is my component ts file:-
import { Component, OnInit, ViewChild, ElementRef, Renderer2 } from '#angular/core';
import { Tank } from '../../models/tank.model';
import { FormGroup, FormControl } from '#angular/forms';
import * as moment from 'moment';
import { TankService } from 'src/app/services/tank.service';
#Component({
selector: 'app-booking',
templateUrl: './booking.component.html',
styleUrls: ['./booking.component.css']
})
export class BookingComponent implements OnInit {
selectedDate: any;
convertedDate: string;
tankAvailability: Tank[];
dateSelectionButtonPressed = false;
tankOneAvailability: string[] = [];
bookingForm: FormGroup;
constructor(private tankService: TankService) { }
ngOnInit(): void {
this.bookingForm = new FormGroup({
enteredDate: new FormControl(),
});
}
onSelect(event) {
this.selectedDate = event;
this.convertedDate = moment(event).format('DD-MM-YYYY');
this.bookingForm.patchValue({
enteredDate: this.convertedDate
});
}
getTankAvailabilityByDate() {
this.dateSelectionButtonPressed = false;
this.tankAvailability = [];
if (this.convertedDate) {
this.tankService.getTankAvailabilityByDate(this.convertedDate)
.subscribe(tanks => {
this.tankAvailability = tanks.tanks;
this.populateAvailableSessions();
});
}
}
populateAvailableSessions() {
this.dateSelectionButtonPressed = true;
for(let i = 0; i < this.tankAvailability.length; i++) {
if (this.tankAvailability[i].tankNumber === 1) {
console.log(this.tankAvailability[i]);
if (this.tankAvailability[i].sessionOne === false) {
this.tankOneAvailability.push('07:00');
}
if (this.tankAvailability[i].sessionTwo === false) {
this.tankOneAvailability.push('09:00');
}
if (this.tankAvailability[i].sessionThree === false) {
this.tankOneAvailability.push('11:00');
}
if (this.tankAvailability[i].sessionFour === false) {
this.tankOneAvailability.push('13:00');
}
if (this.tankAvailability[i].sessionFive === false) {
this.tankOneAvailability.push('15:00');
}
console.log(this.tankOneAvailability);
}
}
}
}
Below is my component HTML file:-
<mat-card>
<div class="center-section">
<h1>Book a float</h1>
<p>
Select a date on the calendar below and we will display all the available float times
</p>
<form (submit)="getTankAvailabilityByDate()" [formGroup]="bookingForm">
<div class="calendar-wrapper">
<mat-calendar [selected]="selectedDate" (selectedChange)="onSelect($event)"></mat-calendar>
</div>
<mat-form-field class="invisible-field">
<input matInput formControlName="enteredDate">
</mat-form-field>
<button mat-raised-button color="primary" type="submit">Get availability</button>
</form>
</div>
</mat-card>
<mat-card *ngIf="dateSelectionButtonPressed">
<mat-card-header>
<mat-card-title>Tank 1</mat-card-title>
</mat-card-header>
<mat-card-content *ngFor="let session of tankOneAvailability">
<button mat-raised-button color="primary">{{session}}</button>
</mat-card-content>
</mat-card>
You would need to empty the array to show new list of buttons. Below code needs to be changed
populateAvailableSessions() {
this.dateSelectionButtonPressed = true;
// always create new array so new values are pushed
this.tankOneAvailability = [];
for(let i = 0; i < this.tankAvailability.length; i++) {
if (this.tankAvailability[i].tankNumber === 1) {
console.log(this.tankAvailability[i]);
if (this.tankAvailability[i].sessionOne === false) {
this.tankOneAvailability.push('07:00');
}
if (this.tankAvailability[i].sessionTwo === false) {
this.tankOneAvailability.push('09:00');
}
if (this.tankAvailability[i].sessionThree === false) {
this.tankOneAvailability.push('11:00');
}
if (this.tankAvailability[i].sessionFour === false) {
this.tankOneAvailability.push('13:00');
}
if (this.tankAvailability[i].sessionFive === false) {
this.tankOneAvailability.push('15:00');
}
console.log(this.tankOneAvailability);
}
}
}
Hope this helps.
You need to reinitialise the tankOneAvailability array each time you run a new query like you do for the tankAvailability (currently you keep pushing more values into it)
i.e.
getTankAvailabilityByDate() {
this.dateSelectionButtonPressed = false;
this.tankAvailability = [];
this.tankOneAvailability = []

Show me the selected toggle button in Angular 4 and the other slice

I have made toggle buttons like component in Angular so I can use everywhere I need, but I am trying to solve something and I need your help.
I need only to show me the selected toggle button so the other toggle button don't show.
When I click in the selected toggle button then show me the other toggle buttons for example like expand and collapse, if I click the selected toggle button than show me the everything what is in that array.
The selected toggle button comes from another component with ngModel which tells by the component which is selected
I have tried slice but didn't work.
This is the component of toggle button.
<div id="toggle-button" fxLayout="row" fxLayoutAlign="start end">
<label [style.width]="labelWidth" [style.paddingRight]="label.length > 0 ? '10px' : '0'">
{{label}}
</label>
<div *ngFor="let option of options | slice:0:1; let first = first; let last = last" [ngClass]="{'first': first, 'last': last, 'selected': option.value === value, 'divider' : !last, 'clickable': !readonly, 'not-selectable': readonly}"
[style.width]="optionWidth" (click)="select(option.value)" fxLayout="row" fxLayoutAlign="center center">
<span (click)="options.length">{{option.text}}</span>
</div>
</div>
export class ToggleButtonComponent implements OnInit, ControlValueAccessor {
#Input() options: ToggleOption[] = []
#Input() label = ""
#Input() value: any
#Input() labelWidth = ""
#Input() optionWidth = ""
#Input() readonly = false
#Output() toggle = new EventEmitter<any>()
onChangeCallback: (selected: any) => void = () => { }
onTouchedCallback: (selected: any) => void = () => { }
constructor() {
}
ngOnInit() {
console.log(this.value)
}
writeValue(selected: any): void {
this.value = selected
}
registerOnChange(callback: (selected: any) => void): void {
this.onChangeCallback = callback
}
registerOnTouched(callback: (selected: any) => void): void {
this.onTouchedCallback = callback
}
select(selected: any) {
if (!this.readonly) {
this.value = selected
this.onChangeCallback(selected)
this.onTouchedCallback(selected)
this.toggle.emit(selected)
}
}
}
export interface ToggleOption {
text: string
value: any
}
And this is another component where I declare the toggle buttons.
readonly categoryOptions: ToggleOption[] = [
{ text: "BUS", value: 0 },
{ text: "BOS", value: 1 },
{ text: "BIS", value: 2 }
]
<app-toggle-button label="Category" labelWidth="75px" [options]="categoryOptions" [(ngModel)]="valueItem.category"></app-toggle-button>
Aim
The aim to have multiple toggle buttons with multiple options.
Solution
You need to have toggleState variable to show/hide other buttons.
A variable value to check for current selected buttons which.
You just need to tweak in your ts and html file as -
ts
Add new variable called toggleState to hold toggle state and change the state whenever select function is called.
toggleState = false;
select(selected: any) {
if (!this.readonly) {
this.value = selected
this.onChangeCallback(selected)
this.onTouchedCallback(selected)
this.toggle.emit(selected)
this.toggleState = !this.toggleState; //<-- toggle state here
}
}
html
Just check for current value and toggle state using the syntax *ngIf="option.value == value || toggleState
"
<div id="toggle-button" fxLayout="row" fxLayoutAlign="start end">
<label [style.width]="labelWidth" [style.paddingRight]="label.length > 0 ? '10px' : '0'">
{{label}}
</label>
<div *ngFor="let option of options; let first = first; let last = last" [ngClass]="{'first': first, 'last': last, 'selected': option.value === value, 'divider' : !last, 'clickable': !readonly, 'not-selectable': readonly}"
[style.width]="optionWidth" (click)="select(option.value)" fxLayout="row" fxLayoutAlign="center center"
*ngIf="option.value == value || toggleState">
<span (click)="options.length">{{option.text}}</span>
</div>
</div>
Toggle false on click anywhere else
You can use the HostListener to handle this -
constructor(private elementRef: ElementRef) {}
#HostListener('document:click', ['$event'])
public documentClick(event: MouseEvent): void {
const targetElement = event.target as HTMLElement;
// Check if the click was outside the element
if (targetElement && !this.elementRef.nativeElement.contains(targetElement)) {
this.toggleState = false; //<-- you can emit if required.
}
}
Note : Ideally this kind scenario should be handle through Directive.
I would use a Pipe, so like this?
https://stackblitz.com/edit/angular-5u5wn1