ngxsmk-datepicker 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/ssr.md CHANGED
@@ -1,305 +1,305 @@
1
- # Server-Side Rendering (SSR) Guide
2
-
3
- **Last updated:** June 3, 2026 - **Current stable:** v2.3.1
4
-
5
- ngxsmk-datepicker is fully compatible with Angular Universal and server-side rendering. This guide covers SSR setup, best practices, and troubleshooting.
6
-
7
- ## Overview
8
-
9
- The datepicker is designed to work seamlessly in both server and browser environments:
10
-
11
- - ✅ All browser-only APIs are guarded with platform checks
12
- - ✅ No direct `window` or `document` access during initialization
13
- - ✅ Event listeners only attach in browser environment
14
- - ✅ Compatible with Angular Universal
15
- - ✅ Supports partial hydration
16
-
17
- ## Basic SSR Setup
18
-
19
- ### Angular Universal Installation
20
-
21
- If you haven't set up Angular Universal yet:
22
-
23
- ```bash
24
- ng add @nguniversal/express-engine
25
- ```
26
-
27
- ### No Additional Configuration Required
28
-
29
- The datepicker works out of the box with SSR. No special configuration is needed:
30
-
31
- ```typescript
32
- import { Component } from '@angular/core';
33
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
34
-
35
- @Component({
36
- selector: 'app-root',
37
- standalone: true,
38
- imports: [NgxsmkDatepickerComponent],
39
- template: `
40
- <ngxsmk-datepicker
41
- mode="single"
42
- [value]="selectedDate">
43
- </ngxsmk-datepicker>
44
- `
45
- })
46
- export class AppComponent {
47
- selectedDate: Date | null = null;
48
- }
49
- ```
50
-
51
- ## Platform Detection
52
-
53
- The datepicker automatically detects the platform and only uses browser APIs when available:
54
-
55
- ```typescript
56
- // Internal implementation (you don't need to do this)
57
- private readonly isBrowser = isPlatformBrowser(this.platformId);
58
-
59
- // Browser-only code is automatically guarded
60
- if (this.isBrowser) {
61
- // Safe to use window, document, etc.
62
- }
63
- ```
64
-
65
- ## SSR Best Practices
66
-
67
- ### 1. Initialize Values Safely
68
-
69
- Always initialize date values safely:
70
-
71
- ```typescript
72
- import { Component, PLATFORM_ID, inject } from '@angular/core';
73
- import { isPlatformBrowser } from '@angular/common';
74
-
75
- @Component({
76
- // ...
77
- })
78
- export class SafeComponent {
79
- private platformId = inject(PLATFORM_ID);
80
-
81
- selectedDate: Date | null = null;
82
-
83
- ngOnInit() {
84
- // Only access browser APIs in browser
85
- if (isPlatformBrowser(this.platformId)) {
86
- this.selectedDate = new Date();
87
- }
88
- }
89
- }
90
- ```
91
-
92
- ### 2. Use Signals for Reactive Data
93
-
94
- Signals work great with SSR:
95
-
96
- ```typescript
97
- import { Component, signal } from '@angular/core';
98
-
99
- @Component({
100
- // ...
101
- })
102
- export class SignalComponent {
103
- // Signals are SSR-safe
104
- selectedDate = signal<Date | null>(null);
105
- }
106
- ```
107
-
108
- ### 3. Server-Side Data
109
-
110
- When fetching data on the server:
111
-
112
- ```typescript
113
- import { Component, inject } from '@angular/core';
114
- import { HttpClient } from '@angular/common/http';
115
- import { httpResource } from '@angular/common/http';
116
- import { signal, linkedSignal, computed } from '@angular/core';
117
-
118
- @Component({
119
- // ...
120
- })
121
- export class ServerDataComponent {
122
- private http = inject(HttpClient);
123
-
124
- resource = httpResource({
125
- request: () => this.http.get<{ date: string }>('/api/data'),
126
- loader: signal(false)
127
- });
128
-
129
- localObject = linkedSignal(() => this.resource.response.value());
130
-
131
- // Computed signals are SSR-safe
132
- selectedDate = computed(() => {
133
- const obj = this.localObject();
134
- return obj?.date ? new Date(obj.date) : null;
135
- });
136
- }
137
- ```
138
-
139
- ## Testing SSR
140
-
141
- ### Build for SSR
142
-
143
- ```bash
144
- npm run build:ssr
145
- npm run serve:ssr
146
- ```
147
-
148
- ### Verify SSR Works
149
-
150
- 1. Build your app with SSR
151
- 2. Check the server-rendered HTML contains the datepicker markup
152
- 3. Verify no console errors during server rendering
153
- 4. Test that the datepicker works after hydration
154
-
155
- ## Common SSR Issues
156
-
157
- ### Issue: Datepicker not rendering on server
158
-
159
- **Solution**: Ensure you're using the component correctly. The datepicker renders its initial state on the server, but interactive features (like opening the calendar) only work in the browser.
160
-
161
- ### Issue: `window is not defined` error
162
-
163
- **Solution**: This shouldn't happen with the current version. If you see this error, ensure you're using the latest version of the datepicker. All browser APIs are properly guarded.
164
-
165
- ### Issue: Dates not displaying correctly
166
-
167
- **Solution**: Ensure date values are properly serialized/deserialized. Use `Date` objects, not strings, when possible:
168
-
169
- ```typescript
170
- // ❌ Bad - may cause issues
171
- date: "2024-01-15"
172
-
173
- // ✅ Good
174
- date: new Date("2024-01-15")
175
- ```
176
-
177
- ### Issue: Locale not working on server
178
-
179
- **Solution**: The datepicker uses the browser's locale by default. On the server, you may need to explicitly set the locale:
180
-
181
- ```html
182
- <ngxsmk-datepicker
183
- [locale]="'en-US'"
184
- mode="single">
185
- </ngxsmk-datepicker>
186
- ```
187
-
188
- Or inject `LOCALE_ID`:
189
-
190
- ```typescript
191
- import { LOCALE_ID, inject } from '@angular/core';
192
-
193
- @Component({
194
- // ...
195
- providers: [
196
- { provide: LOCALE_ID, useValue: 'en-US' }
197
- ]
198
- })
199
- export class LocaleComponent {
200
- locale = inject(LOCALE_ID);
201
- }
202
- ```
203
-
204
- ## Hydration
205
-
206
- The datepicker supports Angular's partial hydration:
207
-
208
- 1. The component renders on the server
209
- 2. On client hydration, interactive features become available
210
- 3. No additional configuration needed
211
-
212
- ## Performance Considerations
213
-
214
- ### OnPush Change Detection
215
-
216
- The datepicker uses `OnPush` change detection, which is optimal for SSR:
217
-
218
- - Fewer change detection cycles
219
- - Better performance in both server and browser
220
- - Compatible with zoneless applications
221
-
222
- ### Lazy Initialization
223
-
224
- Browser-only features are lazily initialized:
225
-
226
- - Event listeners attach only in browser
227
- - Media queries only checked in browser
228
- - Navigator API only accessed in browser
229
-
230
- ## Zone.js and Zoneless
231
-
232
- The datepicker works with or without Zone.js:
233
-
234
- - ✅ Works with Zone.js (default)
235
- - ✅ Works without Zone.js (zoneless)
236
- - Uses `ChangeDetectorRef.markForCheck()` for manual change detection
237
- - Compatible with Angular's zoneless mode
238
-
239
- ## Example: Full SSR Setup
240
-
241
- ```typescript
242
- import { Component, signal, inject } from '@angular/core';
243
- import { HttpClient } from '@angular/common/http';
244
- import { httpResource } from '@angular/common/http';
245
- import { linkedSignal, computed } from '@angular/core';
246
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
247
-
248
- @Component({
249
- selector: 'app-ssr-example',
250
- standalone: true,
251
- imports: [NgxsmkDatepickerComponent],
252
- template: `
253
- <ngxsmk-datepicker
254
- [value]="selectedDate()"
255
- (valueChange)="onDateChange($event)"
256
- mode="single"
257
- placeholder="Select a date">
258
- </ngxsmk-datepicker>
259
- `
260
- })
261
- export class SSRExampleComponent {
262
- private http = inject(HttpClient);
263
-
264
- // Fetch data (works on both server and client)
265
- resource = httpResource({
266
- request: () => this.http.get<{ date: string }>('/api/data'),
267
- loader: signal(false)
268
- });
269
-
270
- // Link to response
271
- localObject = linkedSignal(() => this.resource.response.value());
272
-
273
- // Compute date (SSR-safe)
274
- selectedDate = computed(() => {
275
- const obj = this.localObject();
276
- return obj?.date ? new Date(obj.date) : null;
277
- });
278
-
279
- onDateChange(date: Date | null) {
280
- // Update logic here
281
- console.log('Date changed:', date);
282
- }
283
- }
284
- ```
285
-
286
- ## Troubleshooting Checklist
287
-
288
- - [ ] Build completes without errors: `npm run build:ssr`
289
- - [ ] Server starts without errors: `npm run serve:ssr`
290
- - [ ] No `window is not defined` errors in server logs
291
- - [ ] Datepicker renders in server HTML
292
- - [ ] Datepicker works after client hydration
293
- - [ ] No console errors in browser
294
- - [ ] Dates display correctly
295
- - [ ] Locale works as expected
296
-
297
- ## Additional Resources
298
-
299
- - **[SSR Example](./SSR-EXAMPLE.md)** - Complete working example with code samples
300
- - [Angular Universal Guide](https://angular.dev/guide/ssr)
301
- - [Angular SSR Documentation](https://angular.dev/guide/ssr)
302
- - [Platform Detection](https://angular.dev/api/common/isPlatformBrowser)
303
-
304
-
305
-
1
+ # Server-Side Rendering (SSR) Guide
2
+
3
+ **Last updated:** July 2, 2026 - **Current stable:** v2.4.0
4
+
5
+ ngxsmk-datepicker is fully compatible with Angular Universal and server-side rendering. This guide covers SSR setup, best practices, and troubleshooting.
6
+
7
+ ## Overview
8
+
9
+ The datepicker is designed to work seamlessly in both server and browser environments:
10
+
11
+ - ✅ All browser-only APIs are guarded with platform checks
12
+ - ✅ No direct `window` or `document` access during initialization
13
+ - ✅ Event listeners only attach in browser environment
14
+ - ✅ Compatible with Angular Universal
15
+ - ✅ Supports partial hydration
16
+
17
+ ## Basic SSR Setup
18
+
19
+ ### Angular Universal Installation
20
+
21
+ If you haven't set up Angular Universal yet:
22
+
23
+ ```bash
24
+ ng add @nguniversal/express-engine
25
+ ```
26
+
27
+ ### No Additional Configuration Required
28
+
29
+ The datepicker works out of the box with SSR. No special configuration is needed:
30
+
31
+ ```typescript
32
+ import { Component } from '@angular/core';
33
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
34
+
35
+ @Component({
36
+ selector: 'app-root',
37
+ standalone: true,
38
+ imports: [NgxsmkDatepickerComponent],
39
+ template: `
40
+ <ngxsmk-datepicker
41
+ mode="single"
42
+ [value]="selectedDate">
43
+ </ngxsmk-datepicker>
44
+ `
45
+ })
46
+ export class AppComponent {
47
+ selectedDate: Date | null = null;
48
+ }
49
+ ```
50
+
51
+ ## Platform Detection
52
+
53
+ The datepicker automatically detects the platform and only uses browser APIs when available:
54
+
55
+ ```typescript
56
+ // Internal implementation (you don't need to do this)
57
+ private readonly isBrowser = isPlatformBrowser(this.platformId);
58
+
59
+ // Browser-only code is automatically guarded
60
+ if (this.isBrowser) {
61
+ // Safe to use window, document, etc.
62
+ }
63
+ ```
64
+
65
+ ## SSR Best Practices
66
+
67
+ ### 1. Initialize Values Safely
68
+
69
+ Always initialize date values safely:
70
+
71
+ ```typescript
72
+ import { Component, PLATFORM_ID, inject } from '@angular/core';
73
+ import { isPlatformBrowser } from '@angular/common';
74
+
75
+ @Component({
76
+ // ...
77
+ })
78
+ export class SafeComponent {
79
+ private platformId = inject(PLATFORM_ID);
80
+
81
+ selectedDate: Date | null = null;
82
+
83
+ ngOnInit() {
84
+ // Only access browser APIs in browser
85
+ if (isPlatformBrowser(this.platformId)) {
86
+ this.selectedDate = new Date();
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### 2. Use Signals for Reactive Data
93
+
94
+ Signals work great with SSR:
95
+
96
+ ```typescript
97
+ import { Component, signal } from '@angular/core';
98
+
99
+ @Component({
100
+ // ...
101
+ })
102
+ export class SignalComponent {
103
+ // Signals are SSR-safe
104
+ selectedDate = signal<Date | null>(null);
105
+ }
106
+ ```
107
+
108
+ ### 3. Server-Side Data
109
+
110
+ When fetching data on the server:
111
+
112
+ ```typescript
113
+ import { Component, inject } from '@angular/core';
114
+ import { HttpClient } from '@angular/common/http';
115
+ import { httpResource } from '@angular/common/http';
116
+ import { signal, linkedSignal, computed } from '@angular/core';
117
+
118
+ @Component({
119
+ // ...
120
+ })
121
+ export class ServerDataComponent {
122
+ private http = inject(HttpClient);
123
+
124
+ resource = httpResource({
125
+ request: () => this.http.get<{ date: string }>('/api/data'),
126
+ loader: signal(false)
127
+ });
128
+
129
+ localObject = linkedSignal(() => this.resource.response.value());
130
+
131
+ // Computed signals are SSR-safe
132
+ selectedDate = computed(() => {
133
+ const obj = this.localObject();
134
+ return obj?.date ? new Date(obj.date) : null;
135
+ });
136
+ }
137
+ ```
138
+
139
+ ## Testing SSR
140
+
141
+ ### Build for SSR
142
+
143
+ ```bash
144
+ npm run build:ssr
145
+ npm run serve:ssr
146
+ ```
147
+
148
+ ### Verify SSR Works
149
+
150
+ 1. Build your app with SSR
151
+ 2. Check the server-rendered HTML contains the datepicker markup
152
+ 3. Verify no console errors during server rendering
153
+ 4. Test that the datepicker works after hydration
154
+
155
+ ## Common SSR Issues
156
+
157
+ ### Issue: Datepicker not rendering on server
158
+
159
+ **Solution**: Ensure you're using the component correctly. The datepicker renders its initial state on the server, but interactive features (like opening the calendar) only work in the browser.
160
+
161
+ ### Issue: `window is not defined` error
162
+
163
+ **Solution**: This shouldn't happen with the current version. If you see this error, ensure you're using the latest version of the datepicker. All browser APIs are properly guarded.
164
+
165
+ ### Issue: Dates not displaying correctly
166
+
167
+ **Solution**: Ensure date values are properly serialized/deserialized. Use `Date` objects, not strings, when possible:
168
+
169
+ ```typescript
170
+ // ❌ Bad - may cause issues
171
+ date: "2024-01-15"
172
+
173
+ // ✅ Good
174
+ date: new Date("2024-01-15")
175
+ ```
176
+
177
+ ### Issue: Locale not working on server
178
+
179
+ **Solution**: The datepicker uses the browser's locale by default. On the server, you may need to explicitly set the locale:
180
+
181
+ ```html
182
+ <ngxsmk-datepicker
183
+ [locale]="'en-US'"
184
+ mode="single">
185
+ </ngxsmk-datepicker>
186
+ ```
187
+
188
+ Or inject `LOCALE_ID`:
189
+
190
+ ```typescript
191
+ import { LOCALE_ID, inject } from '@angular/core';
192
+
193
+ @Component({
194
+ // ...
195
+ providers: [
196
+ { provide: LOCALE_ID, useValue: 'en-US' }
197
+ ]
198
+ })
199
+ export class LocaleComponent {
200
+ locale = inject(LOCALE_ID);
201
+ }
202
+ ```
203
+
204
+ ## Hydration
205
+
206
+ The datepicker supports Angular's partial hydration:
207
+
208
+ 1. The component renders on the server
209
+ 2. On client hydration, interactive features become available
210
+ 3. No additional configuration needed
211
+
212
+ ## Performance Considerations
213
+
214
+ ### OnPush Change Detection
215
+
216
+ The datepicker uses `OnPush` change detection, which is optimal for SSR:
217
+
218
+ - Fewer change detection cycles
219
+ - Better performance in both server and browser
220
+ - Compatible with zoneless applications
221
+
222
+ ### Lazy Initialization
223
+
224
+ Browser-only features are lazily initialized:
225
+
226
+ - Event listeners attach only in browser
227
+ - Media queries only checked in browser
228
+ - Navigator API only accessed in browser
229
+
230
+ ## Zone.js and Zoneless
231
+
232
+ The datepicker works with or without Zone.js:
233
+
234
+ - ✅ Works with Zone.js (default)
235
+ - ✅ Works without Zone.js (zoneless)
236
+ - Uses `ChangeDetectorRef.markForCheck()` for manual change detection
237
+ - Compatible with Angular's zoneless mode
238
+
239
+ ## Example: Full SSR Setup
240
+
241
+ ```typescript
242
+ import { Component, signal, inject } from '@angular/core';
243
+ import { HttpClient } from '@angular/common/http';
244
+ import { httpResource } from '@angular/common/http';
245
+ import { linkedSignal, computed } from '@angular/core';
246
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
247
+
248
+ @Component({
249
+ selector: 'app-ssr-example',
250
+ standalone: true,
251
+ imports: [NgxsmkDatepickerComponent],
252
+ template: `
253
+ <ngxsmk-datepicker
254
+ [value]="selectedDate()"
255
+ (valueChange)="onDateChange($event)"
256
+ mode="single"
257
+ placeholder="Select a date">
258
+ </ngxsmk-datepicker>
259
+ `
260
+ })
261
+ export class SSRExampleComponent {
262
+ private http = inject(HttpClient);
263
+
264
+ // Fetch data (works on both server and client)
265
+ resource = httpResource({
266
+ request: () => this.http.get<{ date: string }>('/api/data'),
267
+ loader: signal(false)
268
+ });
269
+
270
+ // Link to response
271
+ localObject = linkedSignal(() => this.resource.response.value());
272
+
273
+ // Compute date (SSR-safe)
274
+ selectedDate = computed(() => {
275
+ const obj = this.localObject();
276
+ return obj?.date ? new Date(obj.date) : null;
277
+ });
278
+
279
+ onDateChange(date: Date | null) {
280
+ // Update logic here
281
+ console.log('Date changed:', date);
282
+ }
283
+ }
284
+ ```
285
+
286
+ ## Troubleshooting Checklist
287
+
288
+ - [ ] Build completes without errors: `npm run build:ssr`
289
+ - [ ] Server starts without errors: `npm run serve:ssr`
290
+ - [ ] No `window is not defined` errors in server logs
291
+ - [ ] Datepicker renders in server HTML
292
+ - [ ] Datepicker works after client hydration
293
+ - [ ] No console errors in browser
294
+ - [ ] Dates display correctly
295
+ - [ ] Locale works as expected
296
+
297
+ ## Additional Resources
298
+
299
+ - **[SSR Example](./SSR-EXAMPLE.md)** - Complete working example with code samples
300
+ - [Angular Universal Guide](https://angular.dev/guide/ssr)
301
+ - [Angular SSR Documentation](https://angular.dev/guide/ssr)
302
+ - [Platform Detection](https://angular.dev/api/common/isPlatformBrowser)
303
+
304
+
305
+