Skip to content
5 changes: 5 additions & 0 deletions projects/igniteui-angular/src/lib/combo/combo.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,11 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh
this.manageRequiredAsterisk();
};

/** @hidden @internal */
protected externalValidate(): IgxInputState {
return this._valid;
}

private updateValidity() {
if (this.ngControl && this.ngControl.invalid) {
this.valid = IgxInputState.INVALID;
Expand Down
2 changes: 2 additions & 0 deletions projects/igniteui-angular/src/lib/combo/combo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
<ng-content select="igx-hint, [igxHint]"></ng-content>
</ng-container>
<input igxInput #comboInput name="comboInput" type="text" [value]="displayValue" readonly
[required]="required" [attr.aria-required]="required"
[externalValidate]="externalValidate"
[attr.placeholder]="placeholder" [disabled]="disabled"
role="combobox" aria-haspopup="listbox"
[attr.aria-expanded]="!dropdown.collapsed" [attr.aria-controls]="dropdown.listId"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3436,8 +3436,11 @@ describe('igxCombo', () => {
it('should add/remove asterisk when setting validators dynamically', () => {
let inputGroupIsRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUTGROUP_REQUIRED));
let asterisk = window.getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUTGROUP_LABEL)).nativeElement, ':after').content;
input = fixture.debugElement.query(By.css(`.${CSS_CLASS_COMBO_INPUTGROUP}`));
expect(asterisk).toBe('"*"');
expect(inputGroupIsRequiredClass).toBeDefined();
console.log(input.nativeElement);
expect(input.nativeElement.getAttribute('aria-required')).toMatch('true');

fixture.componentInstance.reactiveForm.controls.townCombo.clearValidators();
fixture.componentInstance.reactiveForm.controls.townCombo.updateValueAndValidity();
Expand All @@ -3446,6 +3449,7 @@ describe('igxCombo', () => {
asterisk = window.getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUTGROUP_LABEL)).nativeElement, ':after').content;
expect(asterisk).toBe('none');
expect(inputGroupIsRequiredClass).toBeNull();
expect(input.nativeElement.getAttribute('aria-required')).toMatch('false');

fixture.componentInstance.reactiveForm.controls.townCombo.setValidators(Validators.required);
fixture.componentInstance.reactiveForm.controls.townCombo.updateValueAndValidity();
Expand All @@ -3454,6 +3458,7 @@ describe('igxCombo', () => {
asterisk = window.getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUTGROUP_LABEL)).nativeElement, ':after').content;
expect(asterisk).toBe('"*"');
expect(inputGroupIsRequiredClass).toBeDefined();
expect(input.nativeElement.getAttribute('aria-required')).toMatch('true');
});

it('Should update validity state when programmatically setting errors on reactive form controls', fakeAsync(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
}

<input class="igx-date-picker__input-date" [displayValuePipe]="formatter ? displayValue : null" igxInput
[required]="required" [attr.aria-required]="required"
[igxDateTimeEditor]="inputFormat" [displayFormat]="displayFormat"
[minValue]="minValue" [maxValue]="maxValue" [spinDelta]="spinDelta" [spinLoop]="spinLoop"
[disabled]="disabled" [placeholder]="placeholder" [readonly]="!isDropdown || readOnly"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,18 +231,22 @@ describe('IgxDatePicker', () => {

expect(datePicker).toBeDefined();
expect(inputGroup.isRequired).toBeTruthy();
expect((datePicker as any).inputDirective.nativeElement.getAttribute('aria-required')).toEqual('true');
});

it('should update inputGroup isRequired correctly', () => {
const inputGroup = (datePicker as any).inputGroup;
const inputEl = (datePicker as any).inputDirective.nativeElement;

expect(datePicker).toBeDefined();
expect(inputGroup.isRequired).toBeTruthy();
expect(inputEl.getAttribute('aria-required')).toEqual('true');

(fixture.componentInstance as IgxDatePickerNgModelComponent).isRequired = false;
fixture.detectChanges();

expect(inputGroup.isRequired).toBeFalsy();
expect(inputEl.getAttribute('aria-required')).toEqual(null);
});

it('should set validity to initial when the form is reset', fakeAsync(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
private _touchedChanges$: Subscription;
private _fileNames: string;
private _disabled = false;
private _externalValidate: () => IgxInputState = null;

constructor(
public inputGroup: IgxInputGroupBase,
Expand Down Expand Up @@ -197,6 +198,20 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
this.nativeElement.required = this.inputGroup.isRequired = value;
}

/**
* @hidden @internal
* Sets a function to validate the input externally.
* This function should return an `IgxInputState` value.
*/
@Input()
public set externalValidate(fn: () => IgxInputState) {
this._externalValidate = fn;
}

public get externalValidate(): () => IgxInputState {
return this._externalValidate;
}

/**
* Gets whether the igxInput is required.
*
Expand Down Expand Up @@ -384,7 +399,9 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
* @internal
*/
protected updateValidityState() {
if (this.ngControl) {
if (this._externalValidate) {
this._valid = this._externalValidate();
} else if (this.ngControl) {
if (!this.disabled && this.isTouchedOrDirty) {
if (this.hasValidators) {
// Run the validation with empty object to check if required is enabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
</ng-container>

<input #comboInput igxInput [value]="displayValue" role="combobox"
[required]="required" [attr.aria-required]="required" [externalValidate]="externalValidate"
aria-haspopup="listbox" aria-autocomplete="list" aria-readonly="false"
[attr.aria-expanded]="!this.dropdown.collapsed" [attr.aria-controls]="this.dropdown.listId"
[attr.aria-labelledby]="this.ariaLabelledBy || this.label?.id || this.placeholder"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2277,6 +2277,7 @@ describe('IgxSimpleCombo', () => {

expect(combo.valid).toEqual(IgxInputState.INITIAL);
expect(combo.comboInput.valid).toEqual(IgxInputState.INITIAL);
expect(combo.comboInput.nativeElement.attributes['aria-required']).toBeDefined();

// empty string
combo.open();
Expand Down
Loading