Skip to content

Commit b4806b5

Browse files
authored
Merge pull request #6481 from IgniteUI/dpetev/combo-control-touched
Combo - properly update FormControl touched
2 parents ebce1fc + 5432423 commit b4806b5

File tree

4 files changed

+100
-5
lines changed

4 files changed

+100
-5
lines changed

projects/igniteui-angular/src/lib/combo/combo.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
<ng-content select="igx-hint, [igxHint]"></ng-content>
1010
</ng-container>
1111
<input igxInput #comboInput name="comboInput" type="text" [value]="value" readonly [attr.placeholder]="placeholder"
12-
[disabled]="disabled" (blur)="onBlur()" />
12+
[disabled]="disabled" (blur)="onBlur()" (focus)="onFocus()"/>
1313
<ng-container ngProjectAs="igx-suffix">
1414
<ng-content select="igx-suffix"></ng-content>
1515
</ng-container>
16-
<igx-suffix *ngIf="value.length" aria-label="Clear Selection" class="igx-combo__clear-button" igxRipple
16+
<igx-suffix *ngIf="value.length" aria-label="Clear Selection" class="igx-combo__clear-button"
1717
(click)="handleClearItems($event)">
1818
<ng-container *ngIf="clearIconTemplate">
1919
<ng-container *ngTemplateOutlet="clearIconTemplate"></ng-container>

projects/igniteui-angular/src/lib/combo/combo.component.spec.ts

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { IgxToggleModule } from '../directives/toggle/toggle.directive';
66
import { IgxComboItemComponent } from './combo-item.component';
77
import { IgxComboComponent, IgxComboModule, IComboSelectionChangeEventArgs, IgxComboState } from './combo.component';
88
import { IgxComboDropDownComponent } from './combo-dropdown.component';
9-
import { FormGroup, FormControl, Validators, FormBuilder, ReactiveFormsModule, FormsModule } from '@angular/forms';
9+
import { FormGroup, FormControl, Validators, FormBuilder, ReactiveFormsModule, FormsModule, NgControl } from '@angular/forms';
1010
import { IForOfState } from '../directives/for-of/for_of.directive';
1111
import { BehaviorSubject, Observable } from 'rxjs';
1212
import { take } from 'rxjs/operators';
@@ -3250,6 +3250,25 @@ describe('igxCombo', () => {
32503250
fixture.detectChanges();
32513251
expect(component.selectedItems).toEqual([combo.data[0], combo.data[2], combo.data[4]]);
32523252
}));
3253+
3254+
it('Should have correctly bound focus and blur handlers', () => {
3255+
const fixture = TestBed.createComponent(IgxComboInTemplatedFormComponent);
3256+
fixture.detectChanges();
3257+
const combo = fixture.componentInstance.testCombo;
3258+
const input = fixture.debugElement.query(By.css(`${CSS_CLASS_INPUTGROUP} input`));
3259+
3260+
spyOn(combo, 'onFocus');
3261+
spyOn(combo, 'onBlur');
3262+
3263+
3264+
input.triggerEventHandler('focus', {});
3265+
expect(combo.onFocus).toHaveBeenCalled();
3266+
expect(combo.onFocus).toHaveBeenCalledWith();
3267+
3268+
input.triggerEventHandler('blur', {});
3269+
expect(combo.onBlur).toHaveBeenCalled();
3270+
expect(combo.onFocus).toHaveBeenCalledWith();
3271+
});
32533272
});
32543273

32553274
describe('Combo - Display Density', () => {
@@ -3322,6 +3341,66 @@ describe('igxCombo', () => {
33223341
});
33233342
});
33243343

3344+
describe('Combo ControlValueAccessor Unit', () => {
3345+
let combo: IgxComboComponent;
3346+
it('should correctly implement interface methods', () => {
3347+
const mockSelection: {
3348+
[key: string]: jasmine.Spy
3349+
} = jasmine.createSpyObj('IgxSelectionAPIService', ['get', 'set', 'add_items', 'select_items']);
3350+
const mockCdr = jasmine.createSpyObj('ChangeDetectorRef', ['markForCheck']);
3351+
const mockComboService = jasmine.createSpyObj('IgxComboAPIService', ['register']);
3352+
const mockNgControl = jasmine.createSpyObj('NgControl', ['registerOnChangeCb', 'registerOnTouchedCb']);
3353+
const mockInjector = jasmine.createSpyObj('Injector', {
3354+
'get': mockNgControl
3355+
});
3356+
mockSelection.get.and.returnValue(new Set([]));
3357+
3358+
// init
3359+
combo = new IgxComboComponent({ nativeElement: null }, mockCdr, mockSelection as any, mockComboService, null, mockInjector);
3360+
combo.ngOnInit();
3361+
expect(mockInjector.get).toHaveBeenCalledWith(NgControl, null);
3362+
combo.registerOnChange(mockNgControl.registerOnChangeCb);
3363+
combo.registerOnTouched(mockNgControl.registerOnTouchedCb);
3364+
3365+
// writeValue
3366+
expect(combo.value).toBe('');
3367+
mockSelection.add_items.and.returnValue(new Set(['test']));
3368+
spyOnProperty(combo, 'isRemote').and.returnValue(false);
3369+
combo.writeValue(['test']);
3370+
// TODO: Uncomment after fix for write value going through entire selection process
3371+
// expect(mockNgControl.registerOnChangeCb).not.toHaveBeenCalled();
3372+
expect(mockSelection.add_items).toHaveBeenCalledWith(combo.id, ['test'], true);
3373+
expect(mockSelection.select_items).toHaveBeenCalledWith(combo.id, ['test'], true);
3374+
expect(combo.value).toBe('test');
3375+
3376+
// setDisabledState
3377+
combo.setDisabledState(true);
3378+
expect(combo.disabled).toBe(true);
3379+
combo.setDisabledState(false);
3380+
expect(combo.disabled).toBe(false);
3381+
3382+
// OnChange callback
3383+
mockSelection.add_items.and.returnValue(new Set(['simpleValue']));
3384+
combo.selectItems(['simpleValue']);
3385+
expect(mockSelection.add_items).toHaveBeenCalledWith(combo.id, ['simpleValue'], undefined);
3386+
expect(mockSelection.select_items).toHaveBeenCalledWith(combo.id, ['simpleValue'], true);
3387+
expect(mockNgControl.registerOnChangeCb).toHaveBeenCalledWith(['simpleValue']);
3388+
3389+
// OnTouched callback
3390+
spyOnProperty(combo, 'collapsed').and.returnValue(true);
3391+
spyOnProperty(combo, 'valid', 'set');
3392+
3393+
combo.onFocus();
3394+
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(1);
3395+
3396+
combo.onBlur();
3397+
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(2);
3398+
});
3399+
3400+
it('should correctly handle ngControl validity', () => {
3401+
pending('Convert existing form test here');
3402+
});
3403+
});
33253404
@Component({
33263405
template: `
33273406
<igx-combo #combo

projects/igniteui-angular/src/lib/combo/combo.component.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
151151
private _itemsMaxHeight = null;
152152
private _remoteSelection = {};
153153
private _onChangeCallback: (_: any) => void = noop;
154+
private _onTouchedCallback: () => void = noop;
154155
private _overlaySettings: OverlaySettings = {
155156
scrollStrategy: new AbsoluteScrollStrategy(),
156157
positionStrategy: new ConnectedPositioningStrategy(),
@@ -1124,6 +1125,7 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
11241125
*/
11251126
public onBlur() {
11261127
if (this.collapsed) {
1128+
this._onTouchedCallback();
11271129
if (this.ngControl && !this.ngControl.valid) {
11281130
this.valid = IgxComboState.INVALID;
11291131
} else {
@@ -1132,6 +1134,13 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
11321134
}
11331135
}
11341136

1137+
/** @hidden @internal */
1138+
public onFocus() {
1139+
if (this.collapsed) {
1140+
this._onTouchedCallback();
1141+
}
1142+
}
1143+
11351144
/**
11361145
* @hidden @internal
11371146
*/
@@ -1185,7 +1194,9 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
11851194
/**
11861195
* @hidden @internal
11871196
*/
1188-
public registerOnTouched(fn: any): void { }
1197+
public registerOnTouched(fn: any): void {
1198+
this._onTouchedCallback = fn;
1199+
}
11891200

11901201
/**
11911202
* @hidden @internal
@@ -1229,6 +1240,11 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
12291240
*/
12301241
public handleClearItems(event: Event): void {
12311242
this.deselectAllItems(true, event);
1243+
if (this.collapsed) {
1244+
this.getEditElement().focus();
1245+
} else {
1246+
this.focusSearchInput(true);
1247+
}
12321248
event.stopPropagation();
12331249
}
12341250

src/app/combo/combo.sample.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
<h5 class="sample-title">Template Form</h5>
7171
<form>
7272
<igx-combo class="input-container" [placeholder]="'Locations'"
73-
name="anyName" required [(ngModel)]="values1"
73+
name="anyName" required [(ngModel)]="values1" [ngModelOptions]="{ updateOn: 'blur' }" #comboModel="ngModel"
7474
[data]="items" [filterable]="filterableFlag"
7575
[displayKey]="valueKeyVar" [valueKey]="valueKeyVar"
7676
[groupKey]="valueKeyVar ? 'region' : ''" [width]="'100%'">

0 commit comments

Comments
 (0)