Skip to content

Commit 3ca6cde

Browse files
committed
fix(time-picker): implement control value accessor, #6471
1 parent 9c5a58f commit 3ca6cde

File tree

2 files changed

+89
-37
lines changed

2 files changed

+89
-37
lines changed

projects/igniteui-angular/src/lib/time-picker/time-picker.component.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ng-template #dropdownInputTemplate>
2-
<igx-input-group #group>
2+
<igx-input-group #group (mousedown)="mouseDown($event)">
33
<label igxLabel>Time</label>
44
<igx-prefix (click)="openDialog(group.element.nativeElement)">
55
<igx-icon>access_time</igx-icon>
@@ -14,7 +14,7 @@
1414
</igx-input-group>
1515
</ng-template>
1616
<ng-template #defaultTimePickerTemplate>
17-
<igx-input-group (click)="openDialog()">
17+
<igx-input-group (click)="openDialog()" (mousedown)="mouseDown($event)">
1818
<igx-prefix>
1919
<igx-icon>access_time</igx-icon>
2020
</igx-prefix>

projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts

+87-35
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ import {
1616
ViewChild,
1717
ContentChild,
1818
Injectable,
19-
AfterViewInit
19+
AfterViewInit,
20+
Injector
2021
} from '@angular/core';
21-
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
22+
import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl, AbstractControl } from '@angular/forms';
2223
import { HAMMER_GESTURE_CONFIG, HammerGestureConfig } from '@angular/platform-browser';
2324
import { IgxIconModule } from '../icon/index';
2425
import { IgxInputGroupModule, IgxInputGroupComponent } from '../input-group/input-group.component';
25-
import { IgxInputDirective } from '../directives/input/input.directive';
26+
import { IgxInputDirective, IgxInputState } from '../directives/input/input.directive';
2627
import {
2728
IgxAmPmItemDirective,
2829
IgxHourItemDirective,
@@ -32,7 +33,7 @@ import {
3233
IgxTimePickerTemplateDirective,
3334
IgxTimePickerActionsDirective
3435
} from './time-picker.directives';
35-
import { Subject, fromEvent, interval, animationFrameScheduler } from 'rxjs';
36+
import { Subject, fromEvent, interval, animationFrameScheduler, Subscription } from 'rxjs';
3637
import { EditorProvider } from '../core/edit-provider';
3738
import { IgxTimePickerBase, IGX_TIME_PICKER_COMPONENT } from './time-picker.common';
3839
import { AbsoluteScrollStrategy } from '../services/overlay/scroll';
@@ -70,6 +71,8 @@ export interface IgxTimePickerValidationFailedEventArgs extends IBaseEventArgs {
7071
setThroughUI: boolean;
7172
}
7273

74+
const noop = () => { };
75+
7376
@Component({
7477
providers: [
7578
{
@@ -548,6 +551,9 @@ export class IgxTimePickerComponent implements
548551
@ViewChild('input', { read: ElementRef })
549552
private input: ElementRef;
550553

554+
@ViewChild(IgxInputDirective, { read: IgxInputDirective})
555+
private _inputDirective: IgxInputDirective;
556+
551557
/**
552558
* @hidden
553559
*/
@@ -643,6 +649,7 @@ export class IgxTimePickerComponent implements
643649

644650
private _dateFromModel: Date;
645651
private _destroy$ = new Subject<boolean>();
652+
private _statusChanges$: Subscription;
646653
private _dropDownOverlaySettings: OverlaySettings;
647654
private _dialogOverlaySettings: OverlaySettings;
648655

@@ -658,9 +665,38 @@ export class IgxTimePickerComponent implements
658665
private _minutesPos = new Set();
659666
private _secondsPos = new Set();
660667
private _amPmPos = new Set();
668+
private _ngControl: NgControl = null;
669+
670+
//#region ControlValueAccessor
671+
672+
private _onChangeCallback: (_: Date) => void = noop;
673+
private _onTouchedCallback: () => void = noop;
674+
675+
/** @hidden @internal */
676+
public writeValue(value: Date) {
677+
// use this flag to make sure that min/maxValue are checked (in _convertMinMaxValue() method)
678+
// against the real value when initializing the component and value is bound via ngModel
679+
this._dateFromModel = value;
680+
681+
this._value = value;
682+
683+
if (this.mode === InteractionMode.DropDown) {
684+
this.displayValue = this._formatTime(this.value, this.format);
685+
}
686+
}
687+
688+
/** @hidden @internal */
689+
public registerOnChange(fn: (_: Date) => void) { this._onChangeCallback = fn; }
690+
691+
/** @hidden @internal */
692+
public registerOnTouched(fn: () => void) { this._onTouchedCallback = fn; }
693+
694+
/** @hidden @internal */
695+
public setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; }
696+
697+
//#endregion
698+
661699

662-
private _onTouchedCallback: () => void = () => { };
663-
private _onChangeCallback: (_: Date) => void = () => { };
664700

665701
private trimMask(): void {
666702
this.mask = this.mask.slice(this.mask.indexOf(':') + 1, this.mask.length);
@@ -851,6 +887,7 @@ export class IgxTimePickerComponent implements
851887
positionStrategy: new AutoPositionStrategy()
852888
};
853889
this._dialogOverlaySettings = {};
890+
this._ngControl = this._injector.get<NgControl>(NgControl, null);
854891
}
855892

856893
/**
@@ -908,6 +945,12 @@ export class IgxTimePickerComponent implements
908945
});
909946

910947
this.determineCursorPos();
948+
949+
if (this._ngControl) {
950+
this._statusChanges$ = this._ngControl.statusChanges.subscribe(this.onStatusChanged.bind(this));
951+
}
952+
953+
this.manageRequiredAsterisk();
911954
}
912955
}
913956

@@ -937,6 +980,8 @@ export class IgxTimePickerComponent implements
937980
this.openDialog(this.getInputGroupElement());
938981
}
939982

983+
constructor(private _injector: Injector) { }
984+
940985
private determineCursorPos(): void {
941986
this.clearCursorPos();
942987
for (const char of this.format) {
@@ -1493,6 +1538,25 @@ export class IgxTimePickerComponent implements
14931538
}
14941539
}
14951540

1541+
protected onStatusChanged() {
1542+
if ((this._ngControl.control.touched || this._ngControl.control.dirty) &&
1543+
(this._ngControl.control.validator || this._ngControl.control.asyncValidator)) {
1544+
if (this.group.isFocused) {
1545+
this._inputDirective.valid = this._ngControl.valid ? IgxInputState.VALID : IgxInputState.INVALID;
1546+
} else {
1547+
this._inputDirective.valid = this._ngControl.valid ? IgxInputState.INITIAL : IgxInputState.INVALID;
1548+
}
1549+
}
1550+
this.manageRequiredAsterisk();
1551+
}
1552+
1553+
protected manageRequiredAsterisk(): void {
1554+
if (this._ngControl && this._ngControl.control.validator) {
1555+
// Run the validation with empty object to check if required is enabled.
1556+
const error = this._ngControl.control.validator({} as AbstractControl);
1557+
this.group.isRequired = error && error.required;
1558+
}
1559+
}
14961560
/**
14971561
* @hidden
14981562
*/
@@ -1507,35 +1571,6 @@ export class IgxTimePickerComponent implements
15071571
return this.group ? this.group.element.nativeElement : null;
15081572
}
15091573

1510-
/**
1511-
* @hidden
1512-
*/
1513-
public writeValue(value: Date) {
1514-
// use this flag to make sure that min/maxValue are checked (in _convertMinMaxValue() method)
1515-
// against the real value when initializing the component and value is bound via ngModel
1516-
this._dateFromModel = value;
1517-
1518-
this.value = value;
1519-
1520-
if (this.mode === InteractionMode.DropDown) {
1521-
this.displayValue = this._formatTime(this.value, this.format);
1522-
}
1523-
}
1524-
1525-
/**
1526-
* @hidden
1527-
*/
1528-
public registerOnChange(fn: (_: Date) => void) { this._onChangeCallback = fn; }
1529-
1530-
/**
1531-
*@hidden
1532-
*/
1533-
public setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; }
1534-
1535-
/**
1536-
* @hidden
1537-
*/
1538-
public registerOnTouched(fn: () => void) { this._onTouchedCallback = fn; }
15391574

15401575
/**
15411576
* opens the dialog.
@@ -1982,6 +2017,23 @@ export class IgxTimePickerComponent implements
19822017
this.onValidationFailed.emit(args);
19832018
}
19842019
}
2020+
2021+
this._onTouchedCallback();
2022+
if (this.toggleRef.collapsed) {
2023+
if (this._ngControl && !this._ngControl.valid) {
2024+
this._inputDirective.valid = IgxInputState.INVALID;
2025+
} else {
2026+
this._inputDirective.valid = IgxInputState.INITIAL;
2027+
}
2028+
}
2029+
}
2030+
2031+
public mouseDown(event: MouseEvent): void {
2032+
// if the click is not on the input but in input group
2033+
// e.g. on prefix or suffix, prevent default and this way prevent blur
2034+
if (event.target !== this.getEditElement()) {
2035+
event.preventDefault();
2036+
}
19852037
}
19862038

19872039
/**

0 commit comments

Comments
 (0)