• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 16194406744

10 Jul 2025 11:55AM UTC coverage: 4.657% (-87.0%) from 91.64%
16194406744

Pull #16028

github

web-flow
Merge fdb0f0dbc into 7622e5478
Pull Request #16028: fix(radio-group): dynamically added radio buttons do not initialize

178 of 15764 branches covered (1.13%)

18 of 19 new or added lines in 2 files covered. (94.74%)

25721 existing lines in 324 files now uncovered.

1377 of 29570 relevant lines covered (4.66%)

0.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.8
/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts
1
import {
2
    AfterViewChecked,
3
    AfterViewInit,
4
    AfterContentChecked,
5
    ChangeDetectorRef,
6
    Component,
7
    ContentChild,
8
    ContentChildren,
9
    ElementRef,
10
    EventEmitter,
11
    HostBinding,
12
    HostListener,
13
    Inject,
14
    Injector,
15
    Input,
16
    LOCALE_ID,
17
    OnDestroy,
18
    OnInit,
19
    Optional,
20
    Output,
21
    PipeTransform,
22
    QueryList,
23
    Renderer2,
24
    ViewChild,
25
    ViewContainerRef,
26
    booleanAttribute
27
} from '@angular/core';
28
import {
29
    AbstractControl,
30
    ControlValueAccessor,
31
    NgControl,
32
    NG_VALIDATORS,
33
    NG_VALUE_ACCESSOR,
34
    ValidationErrors,
35
    Validator
36
} from '@angular/forms';
37
import {
38
    IgxCalendarComponent, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective,
39
     IFormattingViews, IFormattingOptions
40
} from '../calendar/public_api';
41
import { isDateInRanges } from '../calendar/common/helpers';
42
import {
43
    IgxLabelDirective, IGX_INPUT_GROUP_TYPE, IgxInputGroupType, IgxInputState, IgxInputGroupComponent, IgxPrefixDirective, IgxInputDirective, IgxSuffixDirective
44
} from '../input-group/public_api';
45
import { fromEvent, Subscription, noop, MonoTypeOperatorFunction } from 'rxjs';
46
import { filter, takeUntil } from 'rxjs/operators';
47

48
import { DateRangeDescriptor, DateRangeType } from '../core/dates/dateRange';
49
import { DatePickerResourceStringsEN, IDatePickerResourceStrings } from '../core/i18n/date-picker-resources';
50
import { IBaseCancelableBrowserEventArgs, isDate, PlatformUtil } from '../core/utils';
51
import { IgxCalendarContainerComponent } from '../date-common/calendar-container/calendar-container.component';
52
import { PickerBaseDirective } from '../date-common/picker-base.directive';
53
import { IgxPickerActionsDirective, IgxPickerClearComponent } from '../date-common/public_api';
54
import { PickerHeaderOrientation } from '../date-common/types';
55
import { DateTimeUtil } from '../date-common/util/date-time.util';
56
import { DatePart, DatePartDeltas, IgxDateTimeEditorDirective } from '../directives/date-time-editor/public_api';
57
import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive';
58
import {
59
    AbsoluteScrollStrategy,
60
    AutoPositionStrategy,
61
    IgxOverlayService,
62
    OverlayCancelableEventArgs,
63
    OverlayEventArgs,
64
    OverlaySettings
65
} from '../services/public_api';
66
import { IDatePickerValidationFailedEventArgs } from './date-picker.common';
67
import { IgxIconComponent } from '../icon/icon.component';
68
import { IgxTextSelectionDirective } from '../directives/text-selection/text-selection.directive';
69
import { getCurrentResourceStrings } from '../core/i18n/resources';
70
import { fadeIn, fadeOut } from 'igniteui-angular/animations';
71

72
let NEXT_ID = 0;
3✔
73

74
/**
75
 * Date Picker displays a popup calendar that lets users select a single date.
76
 *
77
 * @igxModule IgxDatePickerModule
78
 * @igxTheme igx-calendar-theme, igx-icon-theme
79
 * @igxGroup Scheduling
80
 * @igxKeywords datepicker, calendar, schedule, date
81
 * @example
82
 * ```html
83
 * <igx-date-picker [(ngModel)]="selectedDate"></igx-date-picker>
84
 * ```
85
 */
86
@Component({
87
    providers: [
88
        { provide: NG_VALUE_ACCESSOR, useExisting: IgxDatePickerComponent, multi: true },
89
        { provide: NG_VALIDATORS, useExisting: IgxDatePickerComponent, multi: true }
90
    ],
91
    selector: 'igx-date-picker',
92
    templateUrl: 'date-picker.component.html',
93
    styles: [':host { display: block; }'],
94
    imports: [
95
        IgxInputGroupComponent,
96
        IgxPrefixDirective,
97
        IgxIconComponent,
98
        IgxInputDirective,
99
        IgxDateTimeEditorDirective,
100
        IgxTextSelectionDirective,
101
        IgxSuffixDirective
102
    ]
103
})
104
export class IgxDatePickerComponent extends PickerBaseDirective implements ControlValueAccessor, Validator,
3✔
105
    OnInit, AfterViewInit, OnDestroy, AfterViewChecked, AfterContentChecked {
106

107
    /**
108
     * Gets/Sets whether the inactive dates will be hidden.
109
     *
110
     * @remarks
111
     * Applies to dates that are out of the current month.
112
     * Default value is `false`.
113
     * @example
114
     * ```html
115
     * <igx-date-picker [hideOutsideDays]="true"></igx-date-picker>
116
     * ```
117
     * @example
118
     * ```typescript
119
     * let hideOutsideDays = this.datePicker.hideOutsideDays;
120
     * ```
121
     */
122
    @Input({ transform: booleanAttribute })
123
    public hideOutsideDays: boolean;
124

125
    /**
126
     * Gets/Sets the number of month views displayed.
127
     *
128
     * @remarks
129
     * Default value is `1`.
130
     *
131
     * @example
132
     * ```html
133
     * <igx-date-picker [displayMonthsCount]="2"></igx-date-picker>
134
     * ```
135
     * @example
136
     * ```typescript
137
     * let monthViewsDisplayed = this.datePicker.displayMonthsCount;
138
     * ```
139
     */
140
    @Input()
UNCOV
141
    public displayMonthsCount = 1;
×
142

143
    /**
144
     * Show/hide week numbers
145
     *
146
     * @example
147
     * ```html
148
     * <igx-date-picker [showWeekNumbers]="true"></igx-date-picker>
149
     * ``
150
     */
151
    @Input({ transform: booleanAttribute })
152
    public showWeekNumbers: boolean;
153

154
    /**
155
     * Gets/Sets a custom formatter function on the selected or passed date.
156
     *
157
     * @example
158
     * ```html
159
     * <igx-date-picker [value]="date" [formatter]="formatter"></igx-date-picker>
160
     * ```
161
     */
162
    @Input()
163
    public formatter: (val: Date) => string;
164

165
    /**
166
     * Gets/Sets the orientation of the `IgxDatePickerComponent` header.
167
     *
168
     *  @example
169
     * ```html
170
     * <igx-date-picker headerOrientation="vertical"></igx-date-picker>
171
     * ```
172
     */
173
    @Input()
UNCOV
174
    public headerOrientation: PickerHeaderOrientation = PickerHeaderOrientation.Horizontal;
×
175

176
    /**
177
     * Gets/Sets the today button's label.
178
     *
179
     *  @example
180
     * ```html
181
     * <igx-date-picker todayButtonLabel="Today"></igx-date-picker>
182
     * ```
183
     */
184
    @Input()
185
    public todayButtonLabel: string;
186

187
    /**
188
     * Gets/Sets the cancel button's label.
189
     *
190
     * @example
191
     * ```html
192
     * <igx-date-picker cancelButtonLabel="Cancel"></igx-date-picker>
193
     * ```
194
     */
195
    @Input()
196
    public cancelButtonLabel: string;
197

198
    /**
199
     * Specify if the currently spun date segment should loop over.
200
     *
201
     *  @example
202
     * ```html
203
     * <igx-date-picker [spinLoop]="false"></igx-date-picker>
204
     * ```
205
     */
206
    @Input({ transform: booleanAttribute })
UNCOV
207
    public spinLoop = true;
×
208

209
    /**
210
     * Delta values used to increment or decrement each editor date part on spin actions.
211
     * All values default to `1`.
212
     *
213
     * @example
214
     * ```html
215
     * <igx-date-picker [spinDelta]="{ date: 5, month: 2 }"></igx-date-picker>
216
     * ```
217
     */
218
    @Input()
219
    public spinDelta: Pick<DatePartDeltas, 'date' | 'month' | 'year'>;
220

221
    /**
222
     * Gets/Sets the container used for the popup element.
223
     *
224
     * @remarks
225
     *  `outlet` is an instance of `IgxOverlayOutletDirective` or an `ElementRef`.
226
     * @example
227
     * ```html
228
     * <div igxOverlayOutlet #outlet="overlay-outlet"></div>
229
     * //..
230
     * <igx-date-picker [outlet]="outlet"></igx-date-picker>
231
     * //..
232
     * ```
233
     */
234
    @Input()
235
    public override outlet: IgxOverlayOutletDirective | ElementRef;
236

237
    /**
238
     * Gets/Sets the value of `id` attribute.
239
     *
240
     * @remarks If not provided it will be automatically generated.
241
     * @example
242
     * ```html
243
     * <igx-date-picker [id]="'igx-date-picker-3'" cancelButtonLabel="cancel" todayButtonLabel="today"></igx-date-picker>
244
     * ```
245
     */
246
    @Input()
247
    @HostBinding('attr.id')
UNCOV
248
    public id = `igx-date-picker-${NEXT_ID++}`;
×
249

250
    //#region calendar members
251

252
    /**
253
     * Gets/Sets the format views of the `IgxDatePickerComponent`.
254
     *
255
     * @example
256
     * ```typescript
257
     * let formatViews = this.datePicker.formatViews;
258
     *  this.datePicker.formatViews = {day:false, month: false, year:false};
259
     * ```
260
     */
261
    @Input()
262
    public formatViews: IFormattingViews;
263

264
    /**
265
     * Gets/Sets the disabled dates descriptors.
266
     *
267
     * @example
268
     * ```typescript
269
     * let disabledDates = this.datepicker.disabledDates;
270
     * this.datePicker.disabledDates = [ {type: DateRangeType.Weekends}, ...];
271
     * ```
272
     */
273
    @Input()
274
    public get disabledDates(): DateRangeDescriptor[] {
UNCOV
275
        return this._disabledDates;
×
276
    }
277
    public set disabledDates(value: DateRangeDescriptor[]) {
UNCOV
278
        this._disabledDates = value;
×
UNCOV
279
        this._onValidatorChange();
×
280
    }
281

282
    /**
283
     * Gets/Sets the special dates descriptors.
284
     *
285
     * @example
286
     * ```typescript
287
     * let specialDates = this.datepicker.specialDates;
288
     * this.datePicker.specialDates = [ {type: DateRangeType.Weekends}, ... ];
289
     * ```
290
     */
291
    @Input()
292
    public get specialDates(): DateRangeDescriptor[] {
UNCOV
293
        return this._specialDates;
×
294
    }
295
    public set specialDates(value: DateRangeDescriptor[]) {
UNCOV
296
        this._specialDates = value;
×
297
    }
298

299

300
    /**
301
     * Gets/Sets the format options of the `IgxDatePickerComponent`.
302
     *
303
     * @example
304
     * ```typescript
305
     * this.datePicker.calendarFormat = {day: "numeric",  month: "long", weekday: "long", year: "numeric"};
306
     * ```
307
     */
308
    @Input()
309
    public calendarFormat: IFormattingOptions;
310

311
    //#endregion
312

313
    /**
314
     * Gets/Sets the selected date.
315
     *
316
     *  @example
317
     * ```html
318
     * <igx-date-picker [value]="date"></igx-date-picker>
319
     * ```
320
     */
321
    @Input()
322
    public get value(): Date | string {
UNCOV
323
        return this._value;
×
324
    }
325
    public set value(date: Date | string) {
UNCOV
326
        this._value = date;
×
UNCOV
327
        this.setDateValue(date);
×
UNCOV
328
        if (this.dateTimeEditor.value !== date) {
×
UNCOV
329
            this.dateTimeEditor.value = this._dateValue;
×
330
        }
UNCOV
331
        this.valueChange.emit(this.dateValue);
×
UNCOV
332
        this._onChangeCallback(this.dateValue);
×
333
    }
334

335
    /**
336
     * The minimum value the picker will accept.
337
     *
338
     * @example
339
     * <igx-date-picker [minValue]="minDate"></igx-date-picker>
340
     */
341
    @Input()
342
    public set minValue(value: Date | string) {
UNCOV
343
        this._minValue = value;
×
UNCOV
344
        this._onValidatorChange();
×
345
    }
346

347
    public get minValue(): Date | string {
UNCOV
348
        return this._minValue;
×
349
    }
350

351
    /**
352
     * The maximum value the picker will accept.
353
     *
354
     * @example
355
     * <igx-date-picker [maxValue]="maxDate"></igx-date-picker>
356
     */
357
    @Input()
358
    public set maxValue(value: Date | string) {
UNCOV
359
        this._maxValue = value;
×
UNCOV
360
        this._onValidatorChange();
×
361
    }
362

363
    public get maxValue(): Date | string {
UNCOV
364
        return this._maxValue;
×
365
    }
366

367
    /**
368
     * Gets/Sets the resource strings for the picker's default toggle icon.
369
     * By default it uses EN resources.
370
     */
371
    @Input()
372
    public resourceStrings: IDatePickerResourceStrings;
373

374
    /** @hidden @internal */
375
    @Input({ transform: booleanAttribute })
UNCOV
376
    public readOnly = false;
×
377

378
    /**
379
     * Emitted when the picker's value changes.
380
     *
381
     * @remarks
382
     * Used for `two-way` bindings.
383
     *
384
     * @example
385
     * ```html
386
     * <igx-date-picker [(value)]="date"></igx-date-picker>
387
     * ```
388
     */
389
    @Output()
UNCOV
390
    public valueChange = new EventEmitter<Date>();
×
391

392
    /**
393
     * Emitted when the user types/spins invalid date in the date-picker editor.
394
     *
395
     *  @example
396
     * ```html
397
     * <igx-date-picker (validationFailed)="onValidationFailed($event)"></igx-date-picker>
398
     * ```
399
     */
400
    @Output()
UNCOV
401
    public validationFailed = new EventEmitter<IDatePickerValidationFailedEventArgs>();
×
402

403
    /** @hidden @internal */
404
    @ContentChildren(IgxPickerClearComponent)
405
    public clearComponents: QueryList<IgxPickerClearComponent>;
406

407
    /** @hidden @internal */
408
    @ContentChild(IgxLabelDirective)
409
    public label: IgxLabelDirective;
410

411
    @ContentChild(IgxCalendarHeaderTitleTemplateDirective)
412
    private headerTitleTemplate: IgxCalendarHeaderTitleTemplateDirective;
413

414
    @ContentChild(IgxCalendarHeaderTemplateDirective)
415
    private headerTemplate: IgxCalendarHeaderTemplateDirective;
416

417
    @ViewChild(IgxDateTimeEditorDirective, { static: true })
418
    private dateTimeEditor: IgxDateTimeEditorDirective;
419

420
    @ViewChild(IgxInputGroupComponent, { read: ViewContainerRef })
421
    private viewContainerRef: ViewContainerRef;
422

423
    @ViewChild(IgxLabelDirective)
424
    private labelDirective: IgxLabelDirective;
425

426
    @ViewChild(IgxInputDirective)
427
    private inputDirective: IgxInputDirective;
428

429
    @ContentChild(IgxCalendarSubheaderTemplateDirective)
430
    private subheaderTemplate: IgxCalendarSubheaderTemplateDirective;
431

432
    @ContentChild(IgxPickerActionsDirective)
433
    private pickerActions: IgxPickerActionsDirective;
434

435
    private get dialogOverlaySettings(): OverlaySettings {
UNCOV
436
        return Object.assign({}, this._dialogOverlaySettings, this.overlaySettings);
×
437
    }
438

439
    private get dropDownOverlaySettings(): OverlaySettings {
UNCOV
440
        return Object.assign({}, this._dropDownOverlaySettings, this.overlaySettings);
×
441
    }
442

443
    private get inputGroupElement(): HTMLElement {
UNCOV
444
        return this.inputGroup?.element.nativeElement;
×
445
    }
446

447
    private get dateValue(): Date {
UNCOV
448
        return this._dateValue;
×
449
    }
450

451
    private get pickerFormatViews(): IFormattingViews {
UNCOV
452
        return Object.assign({}, this._defFormatViews, this.formatViews);
×
453
    }
454

455
    private get pickerCalendarFormat(): IFormattingOptions {
UNCOV
456
        return Object.assign({}, this._calendarFormat, this.calendarFormat);
×
457
    }
458

459
    /** @hidden @internal */
UNCOV
460
    public displayValue: PipeTransform = { transform: (date: Date) => this.formatter(date) };
×
461

UNCOV
462
    private _resourceStrings = getCurrentResourceStrings(DatePickerResourceStringsEN);
×
463
    private _dateValue: Date;
464
    private _overlayId: string;
465
    private _value: Date | string;
466
    private _targetViewDate: Date;
UNCOV
467
    private _ngControl: NgControl = null;
×
468
    private _statusChanges$: Subscription;
469
    private _calendar: IgxCalendarComponent;
470
    private _calendarContainer?: HTMLElement;
UNCOV
471
    private _specialDates: DateRangeDescriptor[] = null;
×
UNCOV
472
    private _disabledDates: DateRangeDescriptor[] = null;
×
UNCOV
473
    private _overlaySubFilter:
×
474
        [MonoTypeOperatorFunction<OverlayEventArgs>,
475
            MonoTypeOperatorFunction<OverlayEventArgs | OverlayCancelableEventArgs>] = [
UNCOV
476
            filter(x => x.id === this._overlayId),
×
477
            takeUntil(this._destroy$)
478
        ];
UNCOV
479
    private _dropDownOverlaySettings: OverlaySettings = {
×
480
        target: this.inputGroupElement,
481
        closeOnOutsideClick: true,
482
        modal: false,
483
        closeOnEscape: true,
484
        scrollStrategy: new AbsoluteScrollStrategy(),
485
        positionStrategy: new AutoPositionStrategy({
486
            openAnimation: fadeIn,
487
            closeAnimation: fadeOut
488
        })
489
    };
UNCOV
490
    private _dialogOverlaySettings: OverlaySettings = {
×
491
        closeOnOutsideClick: true,
492
        modal: true,
493
        closeOnEscape: true
494
    };
UNCOV
495
    private _calendarFormat: IFormattingOptions = {
×
496
        day: 'numeric',
497
        month: 'short',
498
        weekday: 'short',
499
        year: 'numeric'
500
    };
UNCOV
501
    private _defFormatViews: IFormattingViews = {
×
502
        day: false,
503
        month: true,
504
        year: false
505
    };
UNCOV
506
    private _onChangeCallback: (_: Date) => void = noop;
×
UNCOV
507
    private _onTouchedCallback: () => void = noop;
×
UNCOV
508
    private _onValidatorChange: () => void = noop;
×
509

510
    constructor(element: ElementRef<HTMLElement>,
511
        @Inject(LOCALE_ID) _localeId: string,
UNCOV
512
        @Inject(IgxOverlayService) private _overlayService: IgxOverlayService,
×
UNCOV
513
        private _injector: Injector,
×
UNCOV
514
        private _renderer: Renderer2,
×
UNCOV
515
        private platform: PlatformUtil,
×
UNCOV
516
        private cdr: ChangeDetectorRef,
×
517
        @Optional() @Inject(IGX_INPUT_GROUP_TYPE) _inputGroupType?: IgxInputGroupType) {
UNCOV
518
        super(element, _localeId, _inputGroupType);
×
UNCOV
519
        this.locale = this.locale || this._localeId;
×
520
    }
521

522
    /** @hidden @internal */
523
    public get required(): boolean {
UNCOV
524
        if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) {
×
525
            // Run the validation with empty object to check if required is enabled.
UNCOV
526
            const error = this._ngControl.control.validator({} as AbstractControl);
×
UNCOV
527
            return error && error.required;
×
528
        }
529

UNCOV
530
        return false;
×
531
    }
532

533
    /** @hidden @internal */
534
    public get pickerResourceStrings(): IDatePickerResourceStrings {
UNCOV
535
        return Object.assign({}, this._resourceStrings, this.resourceStrings);
×
536
    }
537

538
    protected override get toggleContainer(): HTMLElement | undefined {
UNCOV
539
        return this._calendarContainer;
×
540
    }
541

542
    /** @hidden @internal */
543
    @HostListener('keydown', ['$event'])
544
    public onKeyDown(event: KeyboardEvent) {
UNCOV
545
        switch (event.key) {
×
546
            case this.platform.KEYMAP.ARROW_UP:
547
                if (event.altKey) {
×
548
                    this.close();
×
549
                }
550
                break;
×
551
            case this.platform.KEYMAP.ARROW_DOWN:
UNCOV
552
                if (event.altKey) {
×
UNCOV
553
                    this.open();
×
554
                }
UNCOV
555
                break;
×
556
            case this.platform.KEYMAP.SPACE:
UNCOV
557
                event.preventDefault();
×
UNCOV
558
                this.open();
×
UNCOV
559
                break;
×
560
        }
561
    }
562

563
    /**
564
     * Opens the picker's dropdown or dialog.
565
     *
566
     * @example
567
     * ```html
568
     * <igx-date-picker #picker></igx-date-picker>
569
     *
570
     * <button type="button" igxButton (click)="picker.open()">Open Dialog</button>
571
     * ```
572
     */
573
    public open(settings?: OverlaySettings): void {
UNCOV
574
        if (!this.collapsed || this.disabled) {
×
UNCOV
575
            return;
×
576
        }
577

UNCOV
578
        const overlaySettings = Object.assign({}, this.isDropdown
×
579
            ? this.dropDownOverlaySettings
580
            : this.dialogOverlaySettings
581
            , settings);
582

UNCOV
583
        if (this.isDropdown && this.inputGroupElement) {
×
UNCOV
584
            overlaySettings.target = this.inputGroupElement;
×
585
        }
UNCOV
586
        if (this.outlet) {
×
UNCOV
587
            overlaySettings.outlet = this.outlet;
×
588
        }
UNCOV
589
        this._overlayId = this._overlayService
×
590
            .attach(IgxCalendarContainerComponent, this.viewContainerRef, overlaySettings);
UNCOV
591
        this._overlayService.show(this._overlayId);
×
592
    }
593

594
    /**
595
     * Toggles the picker's dropdown or dialog
596
     *
597
     * @example
598
     * ```html
599
     * <igx-date-picker #picker></igx-date-picker>
600
     *
601
     * <button type="button" igxButton (click)="picker.toggle()">Toggle Dialog</button>
602
     * ```
603
     */
604
    public toggle(settings?: OverlaySettings): void {
UNCOV
605
        if (this.collapsed) {
×
UNCOV
606
            this.open(settings);
×
607
        } else {
UNCOV
608
            this.close();
×
609
        }
610
    }
611

612
    /**
613
     * Closes the picker's dropdown or dialog.
614
     *
615
     * @example
616
     * ```html
617
     * <igx-date-picker #picker></igx-date-picker>
618
     *
619
     * <button type="button" igxButton (click)="picker.close()">Close Dialog</button>
620
     * ```
621
     */
622
    public close(): void {
UNCOV
623
        if (!this.collapsed) {
×
UNCOV
624
            this._overlayService.hide(this._overlayId);
×
625
        }
626
    }
627

628
    /**
629
     * Selects a date.
630
     *
631
     * @remarks Updates the value in the input field.
632
     *
633
     * @example
634
     * ```typescript
635
     * this.datePicker.select(date);
636
     * ```
637
     * @param date passed date that has to be set to the calendar.
638
     */
639
    public select(value: Date): void {
UNCOV
640
        this.value = value;
×
641
    }
642

643
    /**
644
     * Selects today's date and closes the picker.
645
     *
646
     * @example
647
     * ```html
648
     * <igx-date-picker #picker></igx-date-picker>
649
     *
650
     * <button type="button" igxButton (click)="picker.selectToday()">Select Today</button>
651
     * ```
652
     * */
653
    public selectToday(): void {
UNCOV
654
        const today = new Date();
×
UNCOV
655
        today.setHours(0);
×
UNCOV
656
        today.setMinutes(0);
×
UNCOV
657
        today.setSeconds(0);
×
UNCOV
658
        today.setMilliseconds(0);
×
UNCOV
659
        this.select(today);
×
UNCOV
660
        this.close();
×
661
    }
662

663
    /**
664
     * Clears the input field and the picker's value.
665
     *
666
     * @example
667
     * ```typescript
668
     * this.datePicker.clear();
669
     * ```
670
     */
671
    public clear(): void {
UNCOV
672
        if (!this.disabled) {
×
UNCOV
673
            this._calendar?.deselectDate();
×
UNCOV
674
            this.dateTimeEditor.clear();
×
675
        }
676
    }
677

678
    /**
679
     * Increment a specified `DatePart`.
680
     *
681
     * @param datePart The optional DatePart to increment. Defaults to Date.
682
     * @param delta The optional delta to increment by. Overrides `spinDelta`.
683
     * @example
684
     * ```typescript
685
     * this.datePicker.increment(DatePart.Date);
686
     * ```
687
     */
688
    public increment(datePart?: DatePart, delta?: number): void {
UNCOV
689
        this.dateTimeEditor.increment(datePart, delta);
×
690
    }
691

692
    /**
693
     * Decrement a specified `DatePart`
694
     *
695
     * @param datePart The optional DatePart to decrement. Defaults to Date.
696
     * @param delta The optional delta to decrement by. Overrides `spinDelta`.
697
     * @example
698
     * ```typescript
699
     * this.datePicker.decrement(DatePart.Date);
700
     * ```
701
     */
702
    public decrement(datePart?: DatePart, delta?: number): void {
UNCOV
703
        this.dateTimeEditor.decrement(datePart, delta);
×
704
    }
705

706
    //#region Control Value Accessor
707
    /** @hidden @internal */
708
    public writeValue(value: Date | string) {
UNCOV
709
        this._value = value;
×
UNCOV
710
        this.setDateValue(value);
×
UNCOV
711
        if (this.dateTimeEditor.value !== value) {
×
UNCOV
712
            this.dateTimeEditor.value = this._dateValue;
×
713
        }
714
    }
715

716
    /** @hidden @internal */
717
    public registerOnChange(fn: any) {
UNCOV
718
        this._onChangeCallback = fn;
×
719
    }
720

721
    /** @hidden @internal */
722
    public registerOnTouched(fn: any) {
UNCOV
723
        this._onTouchedCallback = fn;
×
724
    }
725

726
    /** @hidden @internal */
727
    public setDisabledState?(isDisabled: boolean): void {
UNCOV
728
        this.disabled = isDisabled;
×
729
    }
730
    //#endregion
731

732
    //#region Validator
733
    /** @hidden @internal */
734
    public registerOnValidatorChange(fn: any) {
UNCOV
735
        this._onValidatorChange = fn;
×
736
    }
737

738
    /** @hidden @internal */
739
    public validate(control: AbstractControl): ValidationErrors | null {
UNCOV
740
        if (!control.value) {
×
UNCOV
741
            return null;
×
742
        }
743
        // InvalidDate handling
UNCOV
744
        if (isDate(control.value) && !DateTimeUtil.isValidDate(control.value)) {
×
745
            return { value: true };
×
746
        }
747

UNCOV
748
        const errors = {};
×
UNCOV
749
        const value = DateTimeUtil.isValidDate(control.value) ? control.value : DateTimeUtil.parseIsoDate(control.value);
×
UNCOV
750
        if (value && this.disabledDates && isDateInRanges(value, this.disabledDates)) {
×
751
            Object.assign(errors, { dateIsDisabled: true });
×
752
        }
UNCOV
753
        Object.assign(errors, DateTimeUtil.validateMinMax(value, this.minValue, this.maxValue, false));
×
754

UNCOV
755
        return Object.keys(errors).length > 0 ? errors : null;
×
756
    }
757
    //#endregion
758

759
    /** @hidden @internal */
760
    public ngOnInit(): void {
UNCOV
761
        this._ngControl = this._injector.get<NgControl>(NgControl, null);
×
762

UNCOV
763
        this.locale = this.locale || this._localeId;
×
764
    }
765

766
    /** @hidden @internal */
767
    public override ngAfterViewInit() {
UNCOV
768
        super.ngAfterViewInit();
×
UNCOV
769
        this.subscribeToClick();
×
UNCOV
770
        this.subscribeToOverlayEvents();
×
UNCOV
771
        this.subscribeToDateEditorEvents();
×
772

UNCOV
773
        this.subToIconsClicked(this.clearComponents, () => this.clear());
×
UNCOV
774
        this.clearComponents.changes.pipe(takeUntil(this._destroy$))
×
UNCOV
775
            .subscribe(() => this.subToIconsClicked(this.clearComponents, () => this.clear()));
×
776

UNCOV
777
        this._dropDownOverlaySettings.excludeFromOutsideClick = [this.inputGroup.element.nativeElement];
×
778

UNCOV
779
        fromEvent(this.inputDirective.nativeElement, 'blur')
×
780
            .pipe(takeUntil(this._destroy$))
781
            .subscribe(() => {
UNCOV
782
                if (this.collapsed) {
×
UNCOV
783
                    this._onTouchedCallback();
×
UNCOV
784
                    this.updateValidity();
×
785
                }
786
            });
787

UNCOV
788
        if (this._ngControl) {
×
UNCOV
789
            this._statusChanges$ =
×
790
                this._ngControl.statusChanges.subscribe(this.onStatusChanged.bind(this));
UNCOV
791
            if (this._ngControl.control.validator) {
×
UNCOV
792
                this.inputGroup.isRequired = this.required;
×
UNCOV
793
                this.cdr.detectChanges();
×
794
            }
795
        }
796
    }
797

798
    /** @hidden @internal */
799
    public ngAfterViewChecked() {
UNCOV
800
        if (this.labelDirective) {
×
UNCOV
801
            this._renderer.setAttribute(this.inputDirective.nativeElement, 'aria-labelledby', this.labelDirective.id);
×
802
        }
803
    }
804

805
    /** @hidden @internal */
806
    public override ngOnDestroy(): void {
UNCOV
807
        super.ngOnDestroy();
×
UNCOV
808
        if (this._statusChanges$) {
×
UNCOV
809
            this._statusChanges$.unsubscribe();
×
810
        }
UNCOV
811
        if (this._overlayId) {
×
UNCOV
812
            this._overlayService.detach(this._overlayId);
×
UNCOV
813
            delete this._overlayId;
×
814
        }
815
    }
816

817
    /** @hidden @internal */
818
    public getEditElement(): HTMLInputElement {
UNCOV
819
        return this.inputDirective.nativeElement;
×
820
    }
821

822
    private subscribeToClick() {
UNCOV
823
        fromEvent(this.getEditElement(), 'click')
×
824
            .pipe(takeUntil(this._destroy$))
825
            .subscribe(() => {
UNCOV
826
                if (!this.isDropdown) {
×
UNCOV
827
                    this.toggle();
×
828
                }
829
            });
830
    }
831

832
    private setDateValue(value: Date | string) {
UNCOV
833
        if (isDate(value) && isNaN(value.getTime())) {
×
UNCOV
834
            this._dateValue = value;
×
UNCOV
835
            return;
×
836
        }
UNCOV
837
        this._dateValue = DateTimeUtil.isValidDate(value) ? value : DateTimeUtil.parseIsoDate(value);
×
838
    }
839

840
    private updateValidity() {
841
        // B.P. 18 May 2021: IgxDatePicker does not reset its state upon resetForm #9526
UNCOV
842
        if (this._ngControl && !this.disabled && this.isTouchedOrDirty) {
×
UNCOV
843
            if (this.hasValidators && this.inputGroup.isFocused) {
×
UNCOV
844
                this.inputDirective.valid = this._ngControl.valid ? IgxInputState.VALID : IgxInputState.INVALID;
×
845
            } else {
UNCOV
846
                this.inputDirective.valid = this._ngControl.valid ? IgxInputState.INITIAL : IgxInputState.INVALID;
×
847
            }
848
        } else {
UNCOV
849
            this.inputDirective.valid = IgxInputState.INITIAL;
×
850
        }
851
    }
852

853
    private get isTouchedOrDirty(): boolean {
UNCOV
854
        return (this._ngControl.control.touched || this._ngControl.control.dirty);
×
855
    }
856

857
    private get hasValidators(): boolean {
UNCOV
858
        return (!!this._ngControl.control.validator || !!this._ngControl.control.asyncValidator);
×
859
    }
860

UNCOV
861
    private onStatusChanged = () => {
×
UNCOV
862
        this.disabled = this._ngControl.disabled;
×
UNCOV
863
        this.updateValidity();
×
UNCOV
864
        this.inputGroup.isRequired = this.required;
×
865
    };
866

867
    private handleSelection(date: Date): void {
UNCOV
868
        if (this.dateValue && DateTimeUtil.isValidDate(this.dateValue)) {
×
UNCOV
869
            date.setHours(this.dateValue.getHours());
×
UNCOV
870
            date.setMinutes(this.dateValue.getMinutes());
×
UNCOV
871
            date.setSeconds(this.dateValue.getSeconds());
×
UNCOV
872
            date.setMilliseconds(this.dateValue.getMilliseconds());
×
873
        }
UNCOV
874
        this.value = date;
×
UNCOV
875
        this._calendar.viewDate = date;
×
UNCOV
876
        this.close();
×
877
    }
878

879
    private subscribeToDateEditorEvents(): void {
UNCOV
880
        this.dateTimeEditor.valueChange.pipe(
×
881
            takeUntil(this._destroy$)).subscribe(val => {
UNCOV
882
                this.value = val;
×
883
            });
UNCOV
884
        this.dateTimeEditor.validationFailed.pipe(
×
885
            takeUntil(this._destroy$)).subscribe((event) => {
UNCOV
886
                this.validationFailed.emit({
×
887
                    owner: this,
888
                    prevValue: event.oldValue,
889
                    currentValue: this.value
890
                });
891
            });
892
    }
893

894
    private subscribeToOverlayEvents() {
UNCOV
895
        this._overlayService.opening.pipe(...this._overlaySubFilter).subscribe((e: OverlayCancelableEventArgs) => {
×
UNCOV
896
            const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: e.cancel };
×
UNCOV
897
            this.opening.emit(args);
×
UNCOV
898
            e.cancel = args.cancel;
×
UNCOV
899
            if (args.cancel) {
×
UNCOV
900
                this._overlayService.detach(this._overlayId);
×
UNCOV
901
                return;
×
902
            }
903

UNCOV
904
            this._initializeCalendarContainer(e.componentRef.instance);
×
UNCOV
905
            this._calendarContainer = e.componentRef.location.nativeElement;
×
UNCOV
906
            this._collapsed = false;
×
907
        });
908

UNCOV
909
        this._overlayService.opened.pipe(...this._overlaySubFilter).subscribe(() => {
×
UNCOV
910
            this.opened.emit({ owner: this });
×
911

UNCOV
912
            this._calendar.wrapper?.nativeElement?.focus();
×
913

UNCOV
914
            if (this._targetViewDate) {
×
UNCOV
915
                this._targetViewDate.setHours(0, 0, 0, 0);
×
916
                // INFO: We need to set the active date to the target view date so there's something to
917
                // navigate when the calendar is opened.
UNCOV
918
                this._calendar.activeDate = this._targetViewDate;
×
919
            }
920
        });
921

UNCOV
922
        this._overlayService.closing.pipe(...this._overlaySubFilter).subscribe((e: OverlayCancelableEventArgs) => {
×
UNCOV
923
            const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: e.cancel };
×
UNCOV
924
            this.closing.emit(args);
×
UNCOV
925
            e.cancel = args.cancel;
×
UNCOV
926
            if (args.cancel) {
×
UNCOV
927
                return;
×
928
            }
929
            // do not focus the input if clicking outside in dropdown mode
UNCOV
930
            const outsideEvent = args.event && (args.event as KeyboardEvent).key !== this.platform.KEYMAP.ESCAPE;
×
UNCOV
931
            if (this.getEditElement() && !(outsideEvent && this.isDropdown)) {
×
UNCOV
932
                this.inputDirective.focus();
×
933
            } else {
UNCOV
934
                this._onTouchedCallback();
×
UNCOV
935
                this.updateValidity();
×
936
            }
937
        });
938

UNCOV
939
        this._overlayService.closed.pipe(...this._overlaySubFilter).subscribe(() => {
×
UNCOV
940
            this.closed.emit({ owner: this });
×
UNCOV
941
            this._overlayService.detach(this._overlayId);
×
UNCOV
942
            this._collapsed = true;
×
UNCOV
943
            this._overlayId = null;
×
UNCOV
944
            this._calendar = null;
×
UNCOV
945
            this._calendarContainer = undefined;
×
946
        });
947
    }
948

949
    private getMinMaxDates() {
UNCOV
950
        const minValue = DateTimeUtil.isValidDate(this.minValue) ? this.minValue : DateTimeUtil.parseIsoDate(this.minValue);
×
UNCOV
951
        const maxValue = DateTimeUtil.isValidDate(this.maxValue) ? this.maxValue : DateTimeUtil.parseIsoDate(this.maxValue);
×
UNCOV
952
        return { minValue, maxValue };
×
953
    }
954

955
    private setDisabledDates(): void {
UNCOV
956
        this._calendar.disabledDates = this.disabledDates ? [...this.disabledDates] : [];
×
UNCOV
957
        const { minValue, maxValue } = this.getMinMaxDates();
×
UNCOV
958
        if (minValue) {
×
UNCOV
959
            this._calendar.disabledDates.push({ type: DateRangeType.Before, dateRange: [minValue] });
×
960
        }
UNCOV
961
        if (maxValue) {
×
UNCOV
962
            this._calendar.disabledDates.push({ type: DateRangeType.After, dateRange: [maxValue] });
×
963
        }
964
    }
965

966

967
    private _initializeCalendarContainer(componentInstance: IgxCalendarContainerComponent) {
UNCOV
968
        this._calendar = componentInstance.calendar;
×
UNCOV
969
        this._calendar.hasHeader = !this.isDropdown;
×
UNCOV
970
        this._calendar.formatOptions = this.pickerCalendarFormat;
×
UNCOV
971
        this._calendar.formatViews = this.pickerFormatViews;
×
UNCOV
972
        this._calendar.locale = this.locale;
×
UNCOV
973
        this._calendar.weekStart = this.weekStart;
×
UNCOV
974
        this._calendar.specialDates = this.specialDates;
×
UNCOV
975
        this._calendar.headerTitleTemplate = this.headerTitleTemplate;
×
UNCOV
976
        this._calendar.headerTemplate = this.headerTemplate;
×
UNCOV
977
        this._calendar.subheaderTemplate = this.subheaderTemplate;
×
UNCOV
978
        this._calendar.headerOrientation = this.headerOrientation;
×
UNCOV
979
        this._calendar.hideOutsideDays = this.hideOutsideDays;
×
UNCOV
980
        this._calendar.monthsViewNumber = this.displayMonthsCount;
×
UNCOV
981
        this._calendar.showWeekNumbers = this.showWeekNumbers;
×
UNCOV
982
        this._calendar.selected.pipe(takeUntil(this._destroy$)).subscribe((ev: Date) => this.handleSelection(ev));
×
UNCOV
983
        this.setDisabledDates();
×
984

UNCOV
985
        if (DateTimeUtil.isValidDate(this.dateValue)) {
×
986
            // calendar will throw if the picker's value is InvalidDate #9208
UNCOV
987
            this._calendar.value = this.dateValue;
×
988
        }
UNCOV
989
        this.setCalendarViewDate();
×
990

UNCOV
991
        componentInstance.mode = this.mode;
×
992
        // componentInstance.headerOrientation = this.headerOrientation;
UNCOV
993
        componentInstance.closeButtonLabel = this.cancelButtonLabel;
×
UNCOV
994
        componentInstance.todayButtonLabel = this.todayButtonLabel;
×
UNCOV
995
        componentInstance.pickerActions = this.pickerActions;
×
996

UNCOV
997
        componentInstance.calendarClose.pipe(takeUntil(this._destroy$)).subscribe(() => this.close());
×
UNCOV
998
        componentInstance.todaySelection.pipe(takeUntil(this._destroy$)).subscribe(() => this.selectToday());
×
999
    }
1000

1001
    private setCalendarViewDate() {
UNCOV
1002
        const { minValue, maxValue } = this.getMinMaxDates();
×
UNCOV
1003
        const dateValue = DateTimeUtil.isValidDate(this.dateValue) ? this.dateValue : new Date();
×
UNCOV
1004
        if (minValue && DateTimeUtil.lessThanMinValue(dateValue, minValue)) {
×
1005
            this._calendar.viewDate = this._targetViewDate = minValue;
×
1006
            return;
×
1007
        }
UNCOV
1008
        if (maxValue && DateTimeUtil.greaterThanMaxValue(dateValue, maxValue)) {
×
UNCOV
1009
            this._calendar.viewDate = this._targetViewDate = maxValue;
×
UNCOV
1010
            return;
×
1011
        }
UNCOV
1012
        this._calendar.viewDate = this._targetViewDate = dateValue;
×
1013
    }
1014
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc