Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 33 additions & 31 deletions src/app/date-picker/date-picker.sample.html
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
<div class="preview" [igSize]="properties.size">
<h6>Angular Date Picker</h6>
<igx-date-picker
#dpAng
[mode]="modeAngular"
[type]="properties.type"
[value]="properties.value"
[displayFormat]="properties.displayFormat"
[inputFormat]="properties.inputFormat"
[required]="properties.required"
[readOnly]="properties.readonly"
[disabled]="properties.disabled"
[placeholder]="properties.placeholder"
showWeekNumbers="true"
headerOrientation="vertical"
>
<label igxLabel>Select a Date</label>
<igx-suffix (click)="dpAng.open()">
<igx-icon>alarm</igx-icon>
</igx-suffix>
<span igxHint>Helper text</span>
<ng-template igxPickerActions let-calendar>
<button igxButton="flat" (click)="calendar.viewDate = today">
Today
</button>
<button igxButton="flat" (click)="calendar.viewDate = nextYear">
Next Year
</button>
<button igxButton="flat" (click)="dpAng.selectToday()">
Select Today
</button>
</ng-template>
</igx-date-picker>
<form [formGroup]="reactiveForm">
<igx-date-picker
#dpAng
formControlName="datePicker"
[mode]="modeAngular"
[type]="properties.type"
[displayFormat]="properties.displayFormat"
[inputFormat]="properties.inputFormat"
[readOnly]="properties.readonly"
[disabled]="properties.disabled"
[placeholder]="properties.placeholder"
showWeekNumbers="true"
headerOrientation="vertical"
>
<label igxLabel>Select a Date</label>
<igx-suffix (click)="dpAng.open()">
<igx-icon>alarm</igx-icon>
</igx-suffix>
<span igxHint>Helper text</span>
<ng-template igxPickerActions let-calendar>
<button igxButton="flat" (click)="calendar.viewDate = today">
Today
</button>
<button igxButton="flat" (click)="calendar.viewDate = nextYear">
Next Year
</button>
<button igxButton="flat" (click)="dpAng.selectToday()">
Select Today
</button>
</ng-template>
</igx-date-picker>
</form>

<h6>WC Date Picker</h6>
<igc-date-picker
Expand All @@ -41,6 +42,7 @@ <h6>WC Date Picker</h6>
[displayFormat]="properties.displayFormat"
[inputFormat]="properties.inputFormat"
[required]="properties.required"
[readOnly]="properties.readonly"
[disabled]="properties.disabled"
[placeholder]="properties.placeholder"
show-week-numbers="true"
Expand Down
76 changes: 68 additions & 8 deletions src/app/date-picker/date-picker.sample.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, CUSTOM_ELEMENTS_SCHEMA, DestroyRef } from '@angular/core';
import { Component, CUSTOM_ELEMENTS_SCHEMA, DestroyRef, inject } from '@angular/core';
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import {
IGX_DATE_PICKER_DIRECTIVES,
IgxButtonDirective,
Expand Down Expand Up @@ -39,9 +40,14 @@ registerIconFromText('alarm', alarm);
IgxSuffixDirective,
IgxIconComponent,
IgSizeDirective,
ReactiveFormsModule,
],
})
export class DatePickerSampleComponent {
private fb = inject(UntypedFormBuilder);
private propertyChangeService = inject(PropertyChangeService);
private destroyRef = inject(DestroyRef);

public date1 = new Date();
public date2 = new Date(
new Date(
Expand Down Expand Up @@ -155,34 +161,88 @@ export class DatePickerSampleComponent {
type: 'text'
}
}
}
};

public properties: Properties;
public properties: Properties = Object.fromEntries(
Object.keys(this.panelConfig).map((key) => {
const control = this.panelConfig[key]?.control;
return [key, control?.defaultValue];
})
) as Properties;

constructor(
private propertyChangeService: PropertyChangeService,
private destroyRef: DestroyRef
) {
// FormControl owns the date picker value
public reactiveForm = this.fb.group({
datePicker: [null],
});

constructor() {
this.propertyChangeService.setPanelConfig(this.panelConfig);

const propertyChange =
this.propertyChangeService.propertyChanges.subscribe(
(properties) => {
this.properties = properties;
this.syncFormControlFromProperties();
}
);

this.destroyRef.onDestroy(() => propertyChange.unsubscribe());
}

/**
* Syncs the reactive form control with the properties panel:
* - programmatic value updates
* - required validator
* - disabled state
*
* All done in a way that does NOT mark the control dirty/touched.
*/
private syncFormControlFromProperties(): void {
const control = this.reactiveForm.get('datePicker');
if (!control) {
return;
}

// 1) Programmatic value update (from properties.value)
// This does NOT mark the control dirty/touched.
if ('value' in this.properties) {
const newValue = this.properties.value ?? null;
const currentValue = control.value;

// Shallow equality check to avoid unnecessary writes
const sameValue =
(newValue === currentValue) ||
(newValue instanceof Date &&
currentValue instanceof Date &&
newValue.getTime() === currentValue.getTime());

if (!sameValue) {
control.setValue(newValue, { emitEvent: false });
}
}

// 2) Required validator
control.setValidators(this.properties?.required ? Validators.required : null);
// This will trigger statusChanges, but control is still pristine/untouched,
// so IgxDatePicker will keep the visual state INITIAL until user interaction.
control.updateValueAndValidity();

// 3) Disabled state
if (this.properties?.disabled) {
control.disable({ emitEvent: false });
} else {
control.enable({ emitEvent: false });
}
}

protected get modeAngular() {
const modeValue = this.propertyChangeService.getProperty('mode');
return modeValue === 'dropdown'
? PickerInteractionMode.DropDown
: PickerInteractionMode.Dialog;
}

protected selectToday(picker) {
protected selectToday(picker: { value: Date; hide: () => void }) {
picker.value = new Date();
picker.hide();
}
Expand Down
22 changes: 22 additions & 0 deletions src/app/time-picker/time-picker.sample.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
<div class="columns-container">
<article class="timepicker-column">
<h4 class="sample-title">Angular Time Picker with Reactive Form</h4>
<div class="preview" [igSize]="properties.size">
<form [formGroup]="reactiveForm">
<igx-time-picker
#tpReactive
formControlName="timePicker"
[mode]="properties.mode"
[type]="properties.type"
[inputFormat]="properties.inputFormat"
[displayFormat]="properties.displayFormat"
[required]="properties.required"
[readOnly]="properties.readonly"
[disabled]="properties.disabled"
[placeholder]="properties.placeholder"
>
<label igxLabel>Select a Time</label>
<span igxHint>Helper text</span>
</igx-time-picker>
</form>
</div>
</article>
<article class="timepicker-column">
<h4 class="sample-title">Time Picker with Date value binding</h4>
<p class="sample-description">{{showDate(date)}}</p>
Expand Down
91 changes: 70 additions & 21 deletions src/app/time-picker/time-picker.sample.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { Component, DestroyRef, inject, TemplateRef, ViewChild, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import {
IgxTimePickerComponent,
IgxInputDirective,
AutoPositionStrategy,
OverlaySettings,
DatePart,
IgxHintDirective,
IgxButtonDirective,
Expand Down Expand Up @@ -32,6 +29,7 @@ import {
imports: [
IgxTimePickerComponent,
FormsModule,
ReactiveFormsModule,
IgxHintDirective,
IgxButtonDirective,
IgxPickerActionsDirective,
Expand All @@ -49,9 +47,6 @@ export class TimePickerSampleComponent implements OnInit {
@ViewChild('tp', { read: IgxTimePickerComponent, static: true })
public tp: IgxTimePickerComponent;

@ViewChild('target')
public target: IgxInputDirective;

@ViewChild('customControls', { static: true })
public customControlsTemplate!: TemplateRef<any>;

Expand All @@ -61,7 +56,6 @@ export class TimePickerSampleComponent implements OnInit {
public hasHint = false;

public itemsDelta = { hours: 1, minutes: 15, seconds: 20 };
public format = 'hh:mm:ss:SS a';
public spinLoop = true;
public datePart = DatePart.Hours;

Expand All @@ -72,14 +66,6 @@ export class TimePickerSampleComponent implements OnInit {
public val = '08:30:00';
public today = new Date(Date.now());

public isRequired = true;

public myOverlaySettings: OverlaySettings = {
modal: false,
closeOnOutsideClick: true,
positionStrategy: new AutoPositionStrategy()
};

public panelConfig: PropertyPanelConfig = {
size: {
control: {
Expand All @@ -105,6 +91,12 @@ export class TimePickerSampleComponent implements OnInit {
defaultValue: 'box'
}
},
required: {
control: {
type: 'boolean',
defaultValue: false
}
},
readonly: {
control: {
type: 'boolean',
Expand Down Expand Up @@ -142,17 +134,30 @@ export class TimePickerSampleComponent implements OnInit {
}
}

public properties: Properties;
private fb = inject(UntypedFormBuilder);
private propertyChangeService = inject(PropertyChangeService);
private destroyRef = inject(DestroyRef);

public properties: Properties = Object.fromEntries(
Object.keys(this.panelConfig).map((key) => {
const control = this.panelConfig[key]?.control;
return [key, control?.defaultValue];
})
) as Properties;

// FormControl owns the time picker value
public reactiveForm = this.fb.group({
timePicker: [null],
});

constructor() {
this.propertyChangeService.setPanelConfig(this.panelConfig);

const propertyChange =
this.propertyChangeService.propertyChanges.subscribe(
(properties) => {
this.properties = properties;
this.syncFormControlFromProperties();
}
);

Expand All @@ -165,11 +170,55 @@ export class TimePickerSampleComponent implements OnInit {
);
}

public change() {
this.isRequired = !this.isRequired;
}
/**
* Syncs the reactive form control with the properties panel:
* - programmatic value updates
* - required validator
* - disabled state
*
* All done in a way that does NOT mark the control dirty/touched.
*/
private syncFormControlFromProperties(): void {
const control = this.reactiveForm.get('timePicker');
if (!control) {
return;
}

// 1) Programmatic value update (from properties.value)
// This does NOT mark the control dirty/touched.
if ('value' in this.properties) {
const newValue = this.properties.value ?? null;
const currentValue = control.value;

// Shallow equality check to avoid unnecessary writes
const sameValue =
(newValue === currentValue) ||
(newValue instanceof Date &&
currentValue instanceof Date &&
newValue.getTime() === currentValue.getTime());

if (!sameValue) {
control.setValue(newValue, { emitEvent: false });
}
}

public valueChanged(event) {
// 2) Required validator - set without triggering validation
const currentValidators = control.validator;
const newValidators = this.properties?.required ? Validators.required : null;

// Only update validators if they actually changed
if (currentValidators !== newValidators) {
control.setValidators(newValidators);
// Don't call updateValueAndValidity - let natural form lifecycle handle validation
}

// 3) Disabled state
if (this.properties?.disabled) {
control.disable({ emitEvent: false });
} else {
control.enable({ emitEvent: false });
}
} public valueChanged(event) {
console.log(event);
}

Expand Down
Loading