diff --git a/CHANGELOG.md b/CHANGELOG.md index db4196e3639..9d11c6d3398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes for each version of this project will be documented in this file. +## 21.2.0 + +### New Features + +- `IgxCombo`, `IgxSimpleCombo` + - Introduced the `selectionChanged` event for both components. The event is not cancelable and is emitted after the selection is committed and the component state is updated. + ## 21.1.0 ### New Features diff --git a/projects/igniteui-angular/combo/README.md b/projects/igniteui-angular/combo/README.md index d98765fcc4c..dd3b91ff3ea 100644 --- a/projects/igniteui-angular/combo/README.md +++ b/projects/igniteui-angular/combo/README.md @@ -86,6 +86,16 @@ export class MyCombo { } ``` +### Selection Events + +The `igx-combo` exposes both `selectionChanging` and `selectionChanged`. + +- `selectionChanging` is emitted **before** a new selection state is committed and can be canceled. +- If `selectionChanging` is not canceled, the component commits the final selection state and then emits `selectionChanged`. +- `selectionChanged` is emitted **after** the selection completes and the component state is updated. +- When the combo is used with `ngModel` or Angular forms, `selectionChanged` is emitted after the value change callback is invoked. +- `selectionChanged` is not cancelable and reports the final committed selection state. + ### Value Binding If we want to use a two-way data-binding, we could just use `ngModel` like this: @@ -330,7 +340,8 @@ When igxCombo is opened, allow custom values are enabled and add item button is | Name | Description | Cancelable | Emitted with | |---------------------|-------------------------------------------------------------------------|--------------|-----------------------------------| -| `selectionChanging` | Emitted when item selection is changing, before the selection completes | true | `IComboSelectionChangingEventArgs` | +| `selectionChanging` | Emitted when item selection is changing, before the selection completes | true | `IComboSelectionChangingEventArgs` | +| `selectionChanged` | Emitted after the selection completes and the component state has been updated | false | `IComboSelectionChangedEventArgs` | | `searchInputUpdate` | Emitted when an the search input's input event is triggered | true | `IComboSearchInputEventArgs` | | `addition` | Emitted when an item is being added to the data collection | true | `IComboItemAdditionEvent` | | `dataPreLoad` | Emitted when new chunk of data is loaded from the virtualization | false | `IForOfState` | diff --git a/projects/igniteui-angular/combo/src/combo/combo.common.ts b/projects/igniteui-angular/combo/src/combo/combo.common.ts index 83c9a2c1903..a9a6bade24a 100644 --- a/projects/igniteui-angular/combo/src/combo/combo.common.ts +++ b/projects/igniteui-angular/combo/src/combo/combo.common.ts @@ -982,6 +982,7 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh public abstract dropdown: IgxComboDropDownComponent; public abstract selectionChanging: EventEmitter; + public abstract selectionChanged: EventEmitter; constructor() { onResourceChangeHandle(this.destroy$, () => { diff --git a/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts b/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts index f226828ec82..1c9bdf60f19 100644 --- a/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts +++ b/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts @@ -7,7 +7,6 @@ import { import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { BehaviorSubject, Observable, firstValueFrom } from 'rxjs'; -import { take } from 'rxjs/operators'; import { IgxSelectionAPIService } from 'igniteui-angular/core'; import { IBaseCancelableBrowserEventArgs } from 'igniteui-angular/core'; import { SortingDirection } from '../../../core/src/data-operations/sorting-strategy'; @@ -20,7 +19,7 @@ import { IgxComboDropDownComponent } from './combo-dropdown.component'; import { IgxComboItemComponent } from './combo-item.component'; import { IComboFilteringOptions, IGX_COMBO_COMPONENT } from './combo.common'; import { - IComboItemAdditionEvent, IComboSearchInputEventArgs, IComboSelectionChangingEventArgs, IgxComboComponent + IComboItemAdditionEvent, IComboSearchInputEventArgs, IComboSelectionChangedEventArgs, IComboSelectionChangingEventArgs, IgxComboComponent } from './combo.component'; import { IgxComboFooterDirective, IgxComboHeaderDirective, IgxComboItemDirective } from './combo.directives'; import { IgxComboFilteringPipe, comboIgnoreDiacriticsFilter } from './combo.pipes'; @@ -658,6 +657,83 @@ describe('igxCombo', () => { }); expect(selectionSpy).toHaveBeenCalledWith(expectedResults); }); + it('should emit selectionChanged after selectionChanging with the committed state', () => { + const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); + combo.ngOnInit(); + combo.data = data; + combo.dropdown = dropdown; + spyOnProperty(combo, 'totalItemCount').and.returnValue(combo.data.length); + + const callOrder: string[] = []; + spyOn(combo.selectionChanging, 'emit').and.callFake(() => callOrder.push('changing')); + spyOn(combo.selectionChanged, 'emit').and.callFake(() => callOrder.push('changed')); + + const selectedItems = [combo.data[1], combo.data[5]]; + combo.select(selectedItems); + + expect(callOrder).toEqual(['changing', 'changed']); + expect(combo.selectionChanged.emit).toHaveBeenCalledTimes(1); + expect(combo.selectionChanged.emit).toHaveBeenCalledWith({ + oldValue: [], + newValue: selectedItems, + oldSelection: [], + newSelection: selectedItems, + added: selectedItems, + removed: [], + event: undefined, + owner: combo, + displayText: selectedItems.join(', ') + }); + }); + it('should not emit selectionChanged when selectionChanging is canceled', () => { + const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); + combo.ngOnInit(); + combo.data = data; + combo.dropdown = dropdown; + spyOnProperty(combo, 'totalItemCount').and.returnValue(combo.data.length); + + spyOn(combo.selectionChanging, 'emit').and.callFake((args: IComboSelectionChangingEventArgs) => { + args.cancel = true; + }); + spyOn(combo.selectionChanged, 'emit'); + + combo.select([combo.data[1], combo.data[5]]); + + expect(combo.selectionChanging.emit).toHaveBeenCalledTimes(1); + expect(combo.selectionChanged.emit).not.toHaveBeenCalled(); + expect(combo.selection).toEqual([]); + expect(combo.value).toEqual([]); + }); + it('should emit selectionChanged with the actual committed state when selectionChanging modifies newValue', () => { + const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); + combo.ngOnInit(); + combo.data = data; + combo.dropdown = dropdown; + spyOnProperty(combo, 'totalItemCount').and.returnValue(combo.data.length); + + spyOn(combo.selectionChanging, 'emit').and.callFake((args: IComboSelectionChangingEventArgs) => { + args.newValue = [combo.data[2], combo.data[4]]; + args.displayText = `${combo.data[2]}, ${combo.data[4]}`; + }); + + spyOn(combo.selectionChanged, 'emit'); + + combo.select([combo.data[0], combo.data[1]]); + + expect(combo.selection).toEqual([combo.data[2], combo.data[4]]); + expect(combo.value).toEqual([combo.data[2], combo.data[4]]); + expect(combo.selectionChanged.emit).toHaveBeenCalledWith({ + oldValue: [], + newValue: [combo.data[2], combo.data[4]], + oldSelection: [], + newSelection: [combo.data[2], combo.data[4]], + added: [combo.data[2], combo.data[4]], + removed: [], + event: undefined, + owner: combo, + displayText: `${combo.data[2]}, ${combo.data[4]}` + }); + }); it('should handle select/deselect ALL items', () => { const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); combo.ngOnInit(); @@ -854,7 +930,8 @@ describe('igxCombo', () => { ComboModelBindingComponent, IgxComboBindingDataAfterInitComponent, IgxComboFormComponent, - IgxComboInTemplatedFormComponent + IgxComboInTemplatedFormComponent, + ComboSelectionChangedNgModelOrderComponent ] }).compileComponents(); })); @@ -1507,6 +1584,23 @@ describe('igxCombo', () => { fixture.detectChanges(); expect(fixture.componentInstance.selectedItems).toEqual([...data].splice(1, 3)); })); + it('should emit selectionChanged after ngModelChange', fakeAsync(() => { + fixture = TestBed.createComponent(ComboSelectionChangedNgModelOrderComponent); + fixture.detectChanges(); + tick(); + + const host = fixture.componentInstance; + combo = host.combo; + + combo.select([1]); + fixture.detectChanges(); + tick(); + + expect(host.eventLog).toEqual(['ngModelChange', 'selectionChanged']); + expect(host.comboValue).toEqual([1]); + expect(host.changedArgs.newValue).toEqual([1]); + expect(host.changedArgs.newSelection).toEqual([host.items[1]]); + })); }); describe('Dropdown tests: ', () => { describe('complex data dropdown: ', () => { @@ -3991,3 +4085,42 @@ export class ComboWithIdComponent { ]; } } + +@Component({ + template: ` + + + `, + imports: [IgxComboComponent, FormsModule] +}) +class ComboSelectionChangedNgModelOrderComponent { + @ViewChild('combo', { read: IgxComboComponent, static: true }) + public combo: IgxComboComponent; + + public items = [ + { id: 0, text: 'One' }, + { id: 1, text: 'Two' }, + { id: 2, text: 'Three' } + ]; + + public comboValue: number[] = []; + public eventLog: string[] = []; + public changedArgs: IComboSelectionChangedEventArgs; + + public onNgModelChange(value: number[]): void { + this.eventLog.push('ngModelChange'); + this.comboValue = value; + } + + public onSelectionChanged(args: IComboSelectionChangedEventArgs): void { + this.eventLog.push('selectionChanged'); + this.changedArgs = args; + } +} diff --git a/projects/igniteui-angular/combo/src/combo/combo.component.ts b/projects/igniteui-angular/combo/src/combo/combo.component.ts index 495dfaedd46..22ced534e34 100644 --- a/projects/igniteui-angular/combo/src/combo/combo.component.ts +++ b/projects/igniteui-angular/combo/src/combo/combo.component.ts @@ -22,26 +22,29 @@ import { IgxInputGroupComponent, IgxInputDirective, IgxReadOnlyInputDirective, I import { IgxIconComponent } from 'igniteui-angular/icon'; import { IgxDropDownItemNavigationDirective } from 'igniteui-angular/drop-down'; -/** Event emitted when an igx-combo's selection is changing */ -export interface IComboSelectionChangingEventArgs extends IBaseCancelableEventArgs { - /** An array containing the values that are currently selected */ +/** Event emitted when an igx-combo's selection has been changed */ +export interface IComboSelectionChangedEventArgs extends IBaseEventArgs { + /** An array containing the values that were previously selected */ oldValue: any[]; - /** An array containing the values that will be selected after this event */ + /** An array containing the values that are currently selected */ newValue: any[]; - /** An array containing the items that are currently selected */ + /** An array containing the items that were previously selected */ oldSelection: any[]; - /** An array containing the items that will be selected after this event */ + /** An array containing the items that are currently selected */ newSelection: any[]; - /** An array containing the items that will be added to the selection (if any) */ + /** An array containing the items that were added to the selection (if any) */ added: any[]; - /** An array containing the items that will be removed from the selection (if any) */ + /** An array containing the items that were removed from the selection (if any) */ removed: any[]; - /** The text that will be displayed in the combo text box */ + /** The text that is displayed in the combo text box */ displayText: string; /** The user interaction that triggered the selection change */ event?: Event; } +/** Event emitted when an igx-combo's selection is changing */ +export interface IComboSelectionChangingEventArgs extends IComboSelectionChangedEventArgs, IBaseCancelableEventArgs {} + /** Event emitted when the igx-combo's search input changes */ export interface IComboSearchInputEventArgs extends IBaseCancelableEventArgs { /** The text that has been typed into the search input */ @@ -125,7 +128,6 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie @Input({ transform: booleanAttribute }) public autoFocusSearch = true; - /** * Defines the placeholder value for the combo dropdown search field * @@ -154,6 +156,16 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie @Output() public selectionChanging = new EventEmitter(); + /** + * Emitted when item selection is changed, after the selection completes + * + * ```html + * + * ``` + */ + @Output() + public selectionChanged = new EventEmitter(); + /** @hidden @internal */ @ViewChild(IgxComboDropDownComponent, { static: true }) public dropdown: IgxComboDropDownComponent; @@ -423,7 +435,19 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie } else { this._displayValue = this.createDisplayText(this.selection, args.oldSelection); } - this._onChangeCallback(args.newValue); + this._onChangeCallback(this.value); + const changedArgs: IComboSelectionChangedEventArgs = { + newValue: this.value, + oldValue, + newSelection: this.selection, + oldSelection, + added: this.convertKeysToItems(diffInSets(new Set(this.value), new Set(oldValue))), + removed: this.convertKeysToItems(diffInSets(new Set(oldValue), new Set(this.value))), + event, + owner: this, + displayText: this._displayValue + }; + this.selectionChanged.emit(changedArgs); } else if (this.isRemote) { this.registerRemoteEntries(diffInSets(selection, currentSelection), false); } diff --git a/projects/igniteui-angular/simple-combo/README.md b/projects/igniteui-angular/simple-combo/README.md index a854fdb969d..b836890173b 100644 --- a/projects/igniteui-angular/simple-combo/README.md +++ b/projects/igniteui-angular/simple-combo/README.md @@ -85,6 +85,15 @@ export class MyCombo { } ``` +### Selection Events + +The `igx-simple-combo` exposes both `selectionChanging` and `selectionChanged`. + +- `selectionChanging` is emitted **before** the selection is committed and can be canceled. +- `selectionChanged` is emitted **after** the selection is committed and the component state is updated. +- When the combo is used with `ngModel` or Angular forms, `selectionChanged` is emitted after the value change callback is invoked. +- `selectionChanged` is not cancelable and always reports the final committed single selection state. + ### Value Binding If we want to use a two-way data-binding, we could just use `ngModel` like this: @@ -305,7 +314,8 @@ When the combo is opened, allow custom values are enabled and add item button is ### Events | Name | Description | Cancelable | Parameters | |------------------ |-------------------------------------------------------------------------|------------- |-----------------------------------------| -| `selectionChanging` | Emitted when item selection is changing, before the selection completes | true | { oldSelection: `any`, newSelection: `any`, displayText: `string`, owner: `IgxSimpleComboComponent` } | +| `selectionChanging` | Emitted when item selection is changing, before the selection completes | true | { oldValue: `any`, newValue: `any`, oldSelection: `any`, newSelection: `any`, displayText: `string`, owner: `IgxSimpleComboComponent` } | +| `selectionChanged` | Emitted after the selection completes and the component state has been updated | false | { oldValue: `any`, newValue: `any`, oldSelection: `any`, newSelection: `any`, displayText: `string`, owner: `IgxSimpleComboComponent` } | | `searchInputUpdate` | Emitted when an the search input's input event is triggered | true | `IComboSearchInputEventArgs` | | `addition` | Emitted when an item is being added to the data collection | false | { oldCollection: `Array`, addedItem: ``, newCollection: `Array`, owner: `IgxSimpleComboComponent` }| | `onDataPreLoad` | Emitted when new chunk of data is loaded from the virtualization | false | `IForOfState` | diff --git a/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.spec.ts b/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.spec.ts index 2c29c9a95d2..73c7bfa723c 100644 --- a/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.spec.ts +++ b/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.spec.ts @@ -10,7 +10,7 @@ import { IgxIconComponent } from 'igniteui-angular/icon'; import { IgxInputState, IgxLabelDirective } from '../../../input-group/src/public_api'; import { AbsoluteScrollStrategy, AutoPositionStrategy, ConnectedPositioningStrategy } from 'igniteui-angular/core'; import { UIInteractions, wait } from '../../../test-utils/ui-interactions.spec'; -import { IgxSimpleComboComponent, ISimpleComboSelectionChangingEventArgs } from './public_api'; +import { IgxSimpleComboComponent, ISimpleComboSelectionChangedEventArgs, ISimpleComboSelectionChangingEventArgs } from './public_api'; import { IGX_GRID_DIRECTIVES, IgxGridComponent } from 'igniteui-angular/grids/grid'; import { IComboSelectionChangingEventArgs, IgxComboAPIService, IgxComboDropDownComponent, IgxComboFooterDirective, IgxComboHeaderDirective, IgxComboItemDirective, IgxComboToggleIconDirective } from 'igniteui-angular/combo'; import { RemoteDataService } from 'igniteui-angular/combo/src/combo/combo.component.spec'; @@ -256,6 +256,92 @@ describe('IgxSimpleCombo', () => { cancel: false }); }); + it('should emit selectionChanged after selectionChanging with the committed state', () => { + const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); + combo.ngOnInit(); + combo.data = data; + combo.dropdown = dropdown; + + const comboInput = jasmine.createSpyObj('IgxInputDirective', ['value']); + comboInput.value = 'test'; + combo.comboInput = comboInput; + + spyOnProperty(combo, 'totalItemCount').and.returnValue(combo.data.length); + + const callOrder = []; + spyOn(combo.selectionChanging, 'emit').and.callFake(() => callOrder.push('changing')); + spyOn(combo.selectionChanged, 'emit').and.callFake(() => callOrder.push('changed')); + + combo.select(combo.data[1]); + + expect(callOrder).toEqual(['changing', 'changed']); + expect(combo.selectionChanged.emit).toHaveBeenCalledTimes(1); + expect(combo.selectionChanged.emit).toHaveBeenCalledWith({ + oldValue: undefined, + newValue: combo.data[1], + oldSelection: undefined, + newSelection: combo.data[1], + owner: combo, + displayText: combo.data[1].trim() + }); + }); + it('should not emit selectionChanged when selectionChanging is canceled', () => { + const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); + combo.ngOnInit(); + combo.data = data; + combo.dropdown = dropdown; + + const comboInput = jasmine.createSpyObj('IgxInputDirective', ['value']); + comboInput.value = 'test'; + combo.comboInput = comboInput; + + spyOnProperty(combo, 'totalItemCount').and.returnValue(combo.data.length); + + spyOn(combo.selectionChanging, 'emit').and.callFake((args: ISimpleComboSelectionChangingEventArgs) => { + args.cancel = true; + }); + spyOn(combo.selectionChanged, 'emit'); + + combo.select(combo.data[1]); + + expect(combo.selectionChanging.emit).toHaveBeenCalledTimes(1); + expect(combo.selectionChanged.emit).not.toHaveBeenCalled(); + expect(combo.selection).toBeUndefined(); + expect(combo.value).toBeUndefined(); + }); + it('should emit selectionChanged with the actual committed state when selectionChanging modifies newValue', () => { + const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); + combo.ngOnInit(); + combo.data = data; + combo.dropdown = dropdown; + + const comboInput = jasmine.createSpyObj('IgxInputDirective', ['value']); + comboInput.value = 'test'; + combo.comboInput = comboInput; + + spyOnProperty(combo, 'totalItemCount').and.returnValue(combo.data.length); + + spyOn(combo.selectionChanging, 'emit').and.callFake((args: ISimpleComboSelectionChangingEventArgs) => { + args.newValue = combo.data[2]; + args.newSelection = combo.data[2]; + args.displayText = combo.data[2]; + }); + + spyOn(combo.selectionChanged, 'emit'); + + combo.select(combo.data[1]); + + expect(combo.selection).toEqual(combo.data[2]); + expect(combo.value).toEqual(combo.data[2]); + expect(combo.selectionChanged.emit).toHaveBeenCalledWith({ + oldValue: undefined, + newValue: combo.data[2], + oldSelection: undefined, + newSelection: combo.data[2], + owner: combo, + displayText: combo.data[2] + }); + }); it('should properly emit added and removed values in change event on single value selection', () => { const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']); combo.ngOnInit(); @@ -438,7 +524,7 @@ describe('IgxSimpleCombo', () => { IgxSimpleComboEmptyComponent, IgxSimpleComboFormControlRequiredComponent, IgxSimpleComboFormWithFormControlComponent, - IgxSimpleComboNgModelComponent + IgxSimpleComboNgModelComponent, ] }).compileComponents(); })); @@ -828,7 +914,8 @@ describe('IgxSimpleCombo', () => { IgxSimpleComboSampleComponent, IgxComboInContainerTestComponent, IgxComboRemoteDataComponent, - ComboModelBindingComponent + ComboModelBindingComponent, + IgxSimpleComboNgModelChangeOrderComponent ] }).compileComponents(); })); @@ -983,6 +1070,23 @@ describe('IgxSimpleCombo', () => { combo.select(combo.data[7][combo.valueKey]); expect(combo.displayValue).toEqual(combo.data[7][combo.displayKey]); })); + it('should emit selectionChanged after ngModelChange', fakeAsync(() => { + fixture = TestBed.createComponent(IgxSimpleComboNgModelChangeOrderComponent); + fixture.detectChanges(); + tick(); + + const host = fixture.componentInstance; + combo = host.combo; + + combo.select('California'); + fixture.detectChanges(); + tick(); + + expect(host.eventLog).toEqual(['ngModelChange', 'selectionChanged']); + expect(host.comboValue).toEqual('California'); + expect(host.changedArgs.newValue).toEqual('California'); + expect(host.changedArgs.newSelection).toEqual(jasmine.objectContaining({ field: 'California' })); + })); }); describe('Keyboard navigation and interactions', () => { @@ -3519,3 +3623,42 @@ export class IgxSimpleComboTabBehaviorTestComponent implements OnInit { ]; } } + +@Component({ + template: ` + + + `, + imports: [IgxSimpleComboComponent, FormsModule] +}) +class IgxSimpleComboNgModelChangeOrderComponent { + @ViewChild('combo', { read: IgxSimpleComboComponent, static: true }) + public combo: IgxSimpleComboComponent; + + public items = [ + { field: 'Arizona' }, + { field: 'California' }, + { field: 'Nevada' } + ]; + + public comboValue = 'Arizona'; + public eventLog: string[] = []; + public changedArgs: ISimpleComboSelectionChangedEventArgs; + + public onNgModelChange(value: any): void { + this.eventLog.push('ngModelChange'); + this.comboValue = value; + } + + public onSelectionChanged(args: ISimpleComboSelectionChangedEventArgs): void { + this.eventLog.push('selectionChanged'); + this.changedArgs = args; + } +} diff --git a/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.ts b/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.ts index aa73c06b7c7..ec02cfd4966 100644 --- a/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.ts +++ b/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.ts @@ -13,20 +13,23 @@ import { IgxIconComponent } from 'igniteui-angular/icon'; import { IGX_COMBO_COMPONENT, IgxComboAddItemComponent, IgxComboAPIService, IgxComboBaseDirective, IgxComboDropDownComponent, IgxComboFilteringPipe, IgxComboGroupingPipe, IgxComboItemComponent } from 'igniteui-angular/combo'; import { IgxDropDownItemNavigationDirective } from 'igniteui-angular/drop-down'; -/** Emitted when an igx-simple-combo's selection is changing. */ -export interface ISimpleComboSelectionChangingEventArgs extends CancelableEventArgs, IBaseEventArgs { - /** An object which represents the value that is currently selected */ +/** Emitted when an igx-simple-combo's selection has been changed. */ +export interface ISimpleComboSelectionChangedEventArgs extends IBaseEventArgs { + /** An object which represents the value that was previously selected */ oldValue: any; - /** An object which represents the value that will be selected after this event */ + /** An object which represents the value that is currently selected */ newValue: any; - /** An object which represents the item that is currently selected */ + /** An object which represents the item that was previously selected */ oldSelection: any; - /** An object which represents the item that will be selected after this event */ + /** An object which represents the item that is currently selected */ newSelection: any; - /** The text that will be displayed in the combo text box */ + /** The text that is displayed in the combo text box */ displayText: string; } +/** Emitted when an igx-simple-combo's selection is changing. */ +export interface ISimpleComboSelectionChangingEventArgs extends ISimpleComboSelectionChangedEventArgs, CancelableEventArgs {} + /** * Represents a drop-down list that provides filtering functionality, allowing users to choose a single option from a predefined list. * @@ -78,6 +81,16 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co @Output() public selectionChanging = new EventEmitter(); + /** + * Emitted when item selection is changed, after the selection completes + * + * ```html + * + * ``` + */ + @Output() + public selectionChanged = new EventEmitter(); + @ViewChild(IgxTextSelectionDirective, { static: true }) private textSelection: IgxTextSelectionDirective; @@ -498,7 +511,8 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co owner: this, cancel: false }; - if (args.newSelection !== args.oldSelection) { + const shouldEmitSelectionEvents = args.newSelection !== args.oldSelection; + if (shouldEmitSelectionEvents) { this.selectionChanging.emit(args); } // TODO: refactor below code as it sets the selection and the display text @@ -514,7 +528,18 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co ? args.displayText : this.createDisplayText(super.selection, [args.oldValue]); } - this._onChangeCallback(args.newValue); + this._onChangeCallback(this.value); + if (shouldEmitSelectionEvents) { + const changedArgs: ISimpleComboSelectionChangedEventArgs = { + newValue: this.value, + oldValue: oldValueAsArray[0], + newSelection: this.selection, + oldSelection: oldItems[0], + displayText: this._displayValue, + owner: this + }; + this.selectionChanged.emit(changedArgs); + } this._updateInput = true; } else if (this.isRemote) { this.registerRemoteEntries(newValueAsArray, false);