ngxsmk-datepicker
Version:
<!-- SEO Keywords: Angular DatePicker, Angular Date Range Picker, Lightweight Calendar Component, Angular Signals DatePicker, SSR Ready DatePicker, Zoneless Angular, A11y DatePicker, Mobile-Friendly DatePicker, Ionic DatePicker Meta Description: The
266 lines (203 loc) • 6.93 kB
Markdown
# Signals Integration Guide
**Last updated:** March 21, 2026 · **Current stable:** v2.2.8
ngxsmk-datepicker is fully compatible with Angular Signals, providing seamless integration with both traditional reactive forms and modern signal-based patterns.
## Basic Signal Binding
### Using Writable Signals
The simplest way to use signals is with a writable signal and two-way binding:
```typescript
import { Component, signal } from '@angular/core';
import { NgxsmkDatepickerComponent, DatepickerValue } from 'ngxsmk-datepicker';
@Component({
selector: 'app-example',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<ngxsmk-datepicker
mode="single"
[value]="selectedDate()"
(valueChange)="selectedDate.set($event)">
</ngxsmk-datepicker>
<p>Selected: {{ selectedDate() | json }}</p>
`
})
export class ExampleComponent {
selectedDate = signal<DatepickerValue>(null);
}
```
### Using Computed Signals
You can derive values from signals:
```typescript
import { Component, signal, computed } from '@angular/core';
@Component({
// ...
})
export class ExampleComponent {
selectedDate = signal<DatepickerValue>(null);
// Computed value based on selected date
formattedDate = computed(() => {
const date = this.selectedDate();
if (!date) return 'No date selected';
if (date instanceof Date) {
return date.toLocaleDateString();
}
return 'Invalid date';
});
}
```
## Signal Forms (Angular 21+)
### Using the `[field]` Input
For Angular 21+ Signal Forms, use the `[field]` input for direct integration. The datepicker automatically tracks dirty state when using this binding:
```typescript
import { Component, signal, form, objectSchema } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
@Component({
selector: 'app-form',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<form>
<ngxsmk-datepicker
[field]="myForm.dateInQuestion"
mode="single"
placeholder="Select a date">
</ngxsmk-datepicker>
<button [disabled]="!myForm().dirty()">Save</button>
</form>
`
})
export class FormComponent {
localObject = signal({
dateInQuestion: new Date()
});
myForm = form(this.localObject, objectSchema({
dateInQuestion: objectSchema<Date>()
}));
}
```
**Important**: The `[field]` binding automatically tracks dirty state. Avoid mixing it with manual `(valueChange)` handlers that bypass the form API, as this may prevent proper dirty tracking. See the [Signal Forms Integration Guide](signal-forms.md) for detailed documentation.
### Manual Signal Updates
If you need more control, you can manually update signals:
```typescript
import { Component, signal, effect } from '@angular/core';
@Component({
// ...
})
export class ExampleComponent {
selectedDate = signal<DatepickerValue>(null);
constructor() {
// React to date changes
effect(() => {
const date = this.selectedDate();
if (date) {
console.log('Date changed:', date);
// Perform side effects like API calls
}
});
}
onDateChange(newDate: DatepickerValue) {
this.selectedDate.set(newDate);
// Additional logic here
}
}
```
## httpResource Integration
The datepicker works seamlessly with `httpResource` for server-side data:
```typescript
import { Component, inject } from '@angular/core';
import { httpResource } from '@angular/common/http';
import { signal, linkedSignal } from '@angular/core';
@Component({
// ...
})
export class DataComponent {
private http = inject(HttpClient);
// Create a resource that fetches data
resource = httpResource({
request: () => this.http.get<{ dateInQuestion: Date }>('/api/data'),
loader: signal(false)
});
// Link the response to a signal
localObject = linkedSignal(() => this.resource.response.value());
// Compute the date from the response
myDate = computed(() => {
const obj = this.localObject();
return obj?.dateInQuestion ? new Date(obj.dateInQuestion) : null;
});
// Update the datepicker when data arrives
updateDate(newDate: DatepickerValue) {
// Update your resource or local signal
this.localObject.update(obj => ({
...obj,
dateInQuestion: newDate
}));
}
}
```
```html
<ngxsmk-datepicker
[value]="myDate()"
(valueChange)="updateDate($event)"
mode="single">
</ngxsmk-datepicker>
```
## Range Selection with Signals
For date ranges, use signals with range objects:
```typescript
import { Component, signal } from '@angular/core';
@Component({
// ...
})
export class RangeComponent {
dateRange = signal<{ start: Date; end: Date } | null>(null);
onRangeChange(range: { start: Date; end: Date } | null) {
this.dateRange.set(range);
}
}
```
```html
<ngxsmk-datepicker
mode="range"
[value]="dateRange()"
(valueChange)="dateRange.set($event)">
</ngxsmk-datepicker>
```
## Multiple Date Selection
For multiple date selection:
```typescript
import { Component, signal } from '@angular/core';
@Component({
// ...
})
export class MultipleComponent {
selectedDates = signal<Date[]>([]);
}
```
```html
<ngxsmk-datepicker
mode="multiple"
[value]="selectedDates()"
(valueChange)="selectedDates.set($event)">
</ngxsmk-datepicker>
```
## Best Practices
1. **Use `signal()` for local state**: For component-local date selection, use writable signals.
2. **Use `[field]` for forms**: When using Angular Signal Forms, prefer the `[field]` input for automatic synchronization and dirty state tracking.
3. **Avoid mixing `[field]` with manual handlers**: Don't use both `[field]` and `(valueChange)` together, as the manual handler may bypass form dirty tracking.
4. **Use `computed()` for derived values**: Derive formatted dates, validation states, or other computed properties.
5. **Handle null values**: Always check for null/undefined when working with datepicker values.
6. **Type safety**: Use the `DatepickerValue` type for better TypeScript support:
```typescript
import { DatepickerValue } from 'ngxsmk-datepicker';
```
For detailed Signal Forms integration including dirty state tracking, see the [Signal Forms Integration Guide](signal-forms.md).
## Migration from Reactive Forms
If you're migrating from Reactive Forms to Signals:
**Before (Reactive Forms):**
```typescript
dateControl = new FormControl<DatepickerValue>(null);
```
**After (Signals):**
```typescript
dateSignal = signal<DatepickerValue>(null);
```
The datepicker supports both patterns simultaneously, so you can migrate gradually.