@dsivd/prestations-ng 19.2.0-beta.14 → 19.2.0-beta.15

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.
@@ -338,7 +338,7 @@ It's the most telling example to show the team: **the signal drives the user inp
338
338
 
339
339
  ### `rxResource()` — asynchronous calls driven by a signal, with loading/error state included
340
340
 
341
- An alternative to `toSignal()` when you also want state tracking (loading, error, reload) without coding it by hand. `rxResource()` accepts an `Observable` as data source, and is triggered again automatically when the signals the `request` depends on change.
341
+ An alternative to `toSignal()` when you also want state tracking (loading, error, reload) without coding it by hand. `rxResource()` accepts an `Observable` as data source, and is triggered again automatically when the signals read by its `params` function change.
342
342
 
343
343
  ```ts
344
344
  import { rxResource } from '@angular/core/rxjs-interop';
@@ -346,8 +346,8 @@ import { rxResource } from '@angular/core/rxjs-interop';
346
346
  readonly userId = input.required<string>();
347
347
 
348
348
  readonly userResource = rxResource({
349
- request: () => this.userId(),
350
- loader: ({ request: id }) => this.userService.getUser(id), // Observable<User>
349
+ params: () => this.userId(),
350
+ stream: ({ params: id }) => this.userService.getUser(id), // Observable<User>
351
351
  });
352
352
 
353
353
  // in the template: userResource.value(), userResource.isLoading(), userResource.error()
@@ -373,8 +373,8 @@ ngOnInit(): void {
373
373
  ```ts
374
374
  // 🟢 After: rxResource(), loading/error included, automatic re-fetch if userId() changes
375
375
  readonly userResource = rxResource({
376
- request: () => this.userId(),
377
- loader: ({ request: id }) => this.userService.getUser(id),
376
+ params: () => this.userId(),
377
+ stream: ({ params: id }) => this.userService.getUser(id),
378
378
  });
379
379
  // userResource.value(), userResource.isLoading(), userResource.error()
380
380
  ```
@@ -423,6 +423,50 @@ The `async` pipe stays fully legitimate when the `Observable` is consumed **only
423
423
 
424
424
  It also applies between layers: a service can compose entirely in RxJS (`debounceTime`, `retry`, `combineLatest`...) and stay testable independently of any component — it's the consuming component, "at the edge", that converts to a signal when displaying.
425
425
 
426
+ ### And the `BehaviorSubject`s?
427
+
428
+ > 🎤 a `BehaviorSubject` holds a current value like a signal, but it is also a stream: we don't convert them systematically, we look at what it represents and who consumes it.
429
+
430
+ It becomes a `signal()` when it is a **private state**, written with `next()` and read with `getValue()` or `| async`, without any operator behind it:
431
+
432
+ ```ts
433
+ // 🔴 A state kept in a BehaviorSubject
434
+ private readonly selectedTab = new BehaviorSubject<Tab>('summary');
435
+
436
+ // 🟢 The same state in a signal: readable in a computed(), no subscription
437
+ private readonly selectedTab = signal<Tab>('summary');
438
+ ```
439
+
440
+ It stays in RxJS when:
441
+
442
+ - it is the **source of an RxJS chain** (`switchMap` to an HTTP call, `debounceTime`, `combineLatest`...): as a signal, it would have to go back through `toObservable()`, which emits later (through an `effect()`) and only the last value of several writes in a row. A useless round trip, and the timing changes
443
+ - it carries **events**, not a state: a signal ignores a value equal to the current one (`Object.is`) and merges the writes in a row
444
+ - it is part of the **public API** of a library or a shared service: replacing the `Observable` breaks its consumers. If they need a signal, add one next to it (`toSignal()` in the service) rather than replacing it
445
+ - some code relies on the **synchronous notification**: `subscribe()` is called at each `next()`, right away, whereas an `effect()` runs later, once for several writes
446
+
447
+ ```ts
448
+ // 🔴 An event turned into a signal: the second reload triggers nothing
449
+ private readonly reloadTrigger = signal(true);
450
+
451
+ reload(): void {
452
+ this.reloadTrigger.set(true); // same value as before: no notification
453
+ }
454
+
455
+ // 🟢 An event stays a stream: each next() counts
456
+ private readonly reloadTrigger = new Subject<void>();
457
+
458
+ readonly alerts$ = this.reloadTrigger.pipe(
459
+ startWith(undefined),
460
+ switchMap(() => this.alertService.getAlerts()),
461
+ );
462
+
463
+ reload(): void {
464
+ this.reloadTrigger.next();
465
+ }
466
+ ```
467
+
468
+ > ⚠️ In a zoneless application, the `BehaviorSubject` itself is not the problem: a `subscribe()` writing a field read by the template, without notifying Angular, is. Fix the consumer with `toSignal()` or `| async` (see "Displaying an asynchronous state" in [GOOD_PRACTICES.md](GOOD_PRACTICES.md)).
469
+
426
470
  ---
427
471
 
428
472
  ## 7. Pitfall #1: mutation vs reassignment
@@ -501,3 +545,4 @@ export class ContactListComponent {
501
545
  - I never mutate a signal value in place anymore — always `.set()`/`.update()` with a new reference
502
546
  - I know when to use `untracked()` in an `effect()`
503
547
  - I don't try to replace all my RxJS with signals — I keep RxJS for composed async (HTTP, debounce, switchMap...) and I only convert to a signal at the edge, with `toSignal()`/`toObservable()`/`rxResource()`
548
+ - I only turn a `BehaviorSubject` into a signal when it is a private state without operators — the sources of RxJS chains, the events and the public APIs stay in RxJS
package/package.json CHANGED
@@ -39,7 +39,7 @@
39
39
  "ng-update": {
40
40
  "migrations": "./schematics/migration-collection.json"
41
41
  },
42
- "version": "19.2.0-beta.14",
42
+ "version": "19.2.0-beta.15",
43
43
  "module": "fesm2022/dsivd-prestations-ng.mjs",
44
44
  "typings": "types/dsivd-prestations-ng.d.ts",
45
45
  "exports": {