@@ -3,14 +3,18 @@ import {
33 inject ,
44 InjectionToken ,
55 isSignal ,
6+ linkedSignal ,
67 signal ,
78} from '@angular/core' ;
89import { TestBed } from '@angular/core/testing' ;
910import {
11+ getState ,
1012 patchState ,
1113 signalStore ,
14+ signalStoreFeature ,
1215 withComputed ,
1316 withHooks ,
17+ withLinkedState ,
1418 withMethods ,
1519 withProps ,
1620 withState ,
@@ -500,7 +504,7 @@ describe('signalStore', () => {
500504 } )
501505 ) ;
502506 TestBed . inject ( Store ) ;
503- TestBed . resetTestEnvironment ( ) ;
507+ TestBed . resetTestingModule ( ) ;
504508
505509 expect ( messages ) . toEqual ( [ 'ending...' ] ) ;
506510 } ) ;
@@ -565,4 +569,229 @@ describe('signalStore', () => {
565569 expect ( secretStore [ SECRET ] ) . toBe ( 'not your business' ) ;
566570 } ) ;
567571 } ) ;
572+
573+ describe ( 'withLinkedState' , ( ) => {
574+ describe ( 'updates automatically if the source changes' , ( ) =>
575+ [
576+ {
577+ name : 'automatic' ,
578+ linkedStateFeature : signalStoreFeature (
579+ withState ( { userId : 1 } ) ,
580+
581+ withLinkedState ( ( { userId } ) => ( {
582+ books : ( ) => {
583+ userId ( ) ;
584+
585+ return [ ] as string [ ] ;
586+ } ,
587+ } ) )
588+ ) ,
589+ } ,
590+ {
591+ name : 'manual' ,
592+ linkedStateFeature : signalStoreFeature (
593+ withState ( { userId : 1 } ) ,
594+ withLinkedState ( ( { userId } ) => ( {
595+ books : linkedSignal ( {
596+ source : userId ,
597+ computation : ( ) => [ ] as string [ ] ,
598+ } ) ,
599+ } ) )
600+ ) ,
601+ } ,
602+ ] . forEach ( ( { name, linkedStateFeature } ) => {
603+ it ( name , ( ) => {
604+ const BookStore = signalStore (
605+ { providedIn : 'root' , protectedState : false } ,
606+ linkedStateFeature ,
607+ withState ( { version : 1 } ) ,
608+ withMethods ( ( store ) => ( {
609+ updateUser ( ) {
610+ patchState ( store , ( value ) => value ) ;
611+ patchState ( store , ( { userId } ) => ( { userId : userId + 1 } ) ) ;
612+ } ,
613+ addBook ( title : string ) {
614+ patchState ( store , ( { books } ) => ( {
615+ books : [ ...books , title ] ,
616+ } ) ) ;
617+ } ,
618+ increaseVersion ( ) {
619+ patchState ( store , ( { version } ) => ( { version : version + 1 } ) ) ;
620+ } ,
621+ } ) )
622+ ) ;
623+
624+ const bookStore = TestBed . inject ( BookStore ) ;
625+ bookStore . addBook ( 'The Neverending Story' ) ;
626+ bookStore . increaseVersion ( ) ;
627+ expect ( bookStore . books ( ) ) . toEqual ( [ 'The Neverending Story' ] ) ;
628+ expect ( bookStore . version ( ) ) . toEqual ( 2 ) ;
629+
630+ patchState ( bookStore , { userId : 2 } ) ;
631+ expect ( bookStore . books ( ) ) . toEqual ( [ ] ) ;
632+ expect ( bookStore . version ( ) ) . toEqual ( 2 ) ;
633+ } ) ;
634+ } ) ) ;
635+
636+ describe ( 'updates also a spread linkedSignal' , ( ) => {
637+ [
638+ {
639+ name : 'automatic' ,
640+ linkedStateFeature : signalStoreFeature (
641+ withState ( { userId : 1 } ) ,
642+ withLinkedState ( ( { userId } ) => ( {
643+ user : ( ) => {
644+ userId ( ) ;
645+ return { name : 'John Doe' } ;
646+ } ,
647+ location : ( ) => {
648+ userId ( ) ;
649+ return { city : 'Berlin' , country : 'Germany' } ;
650+ } ,
651+ } ) )
652+ ) ,
653+ } ,
654+ {
655+ name : 'manual' ,
656+ linkedStateFeature : signalStoreFeature (
657+ withState ( { userId : 1 } ) ,
658+ withLinkedState ( ( { userId } ) => ( {
659+ user : linkedSignal ( {
660+ source : userId ,
661+ computation : ( ) => ( { name : 'John Doe' } ) ,
662+ } ) ,
663+ location : linkedSignal ( {
664+ source : userId ,
665+ computation : ( ) => ( { city : 'Berlin' , country : 'Germany' } ) ,
666+ } ) ,
667+ } ) )
668+ ) ,
669+ } ,
670+ ] . forEach ( ( { name, linkedStateFeature } ) => {
671+ it ( name , ( ) => {
672+ const UserStore = signalStore (
673+ { providedIn : 'root' , protectedState : false } ,
674+ linkedStateFeature ,
675+ withMethods ( ( store ) => ( {
676+ updateUser ( name : string ) {
677+ patchState ( store , ( ) => ( {
678+ user : { name } ,
679+ } ) ) ;
680+ } ,
681+ updateLocation ( city : string , country : string ) {
682+ patchState ( store , ( ) => ( {
683+ location : { city, country } ,
684+ } ) ) ;
685+ } ,
686+ } ) )
687+ ) ;
688+
689+ const userStore = TestBed . inject ( UserStore ) ;
690+
691+ userStore . updateUser ( 'Jane Doe' ) ;
692+ userStore . updateLocation ( 'London' , 'UK' ) ;
693+
694+ expect ( getState ( userStore ) ) . toEqual ( {
695+ userId : 1 ,
696+ user : { name : 'Jane Doe' } ,
697+ location : { city : 'London' , country : 'UK' } ,
698+ } ) ;
699+
700+ patchState ( userStore , { userId : 2 } ) ;
701+ expect ( getState ( userStore ) ) . toEqual ( {
702+ userId : 2 ,
703+ user : { name : 'John Doe' } ,
704+ location : { city : 'Berlin' , country : 'Germany' } ,
705+ } ) ;
706+ } ) ;
707+ } ) ;
708+ } ) ;
709+
710+ describe ( 'can depend on a Signal from another SignalStore' , ( ) => {
711+ const UserStore = signalStore (
712+ { providedIn : 'root' , protectedState : false } ,
713+ withState ( { userId : 1 } )
714+ ) ;
715+
716+ [
717+ {
718+ name : 'automatic' ,
719+ linkedStateFeature : signalStoreFeature (
720+ withLinkedState ( ( ) => ( {
721+ books : ( ) => {
722+ TestBed . inject ( UserStore ) . userId ( ) ;
723+
724+ return [ ] as string [ ] ;
725+ } ,
726+ } ) )
727+ ) ,
728+ } ,
729+ {
730+ name : 'manual' ,
731+ linkedStateFeature : signalStoreFeature (
732+ withLinkedState ( ( ) => {
733+ const userStore = TestBed . inject ( UserStore ) ;
734+
735+ return {
736+ books : linkedSignal ( {
737+ source : userStore . userId ,
738+ computation : ( ) => [ ] as string [ ] ,
739+ } ) ,
740+ } ;
741+ } )
742+ ) ,
743+ } ,
744+ ] . forEach ( ( { name, linkedStateFeature } ) => {
745+ it ( name , ( ) => {
746+ const BookStore = signalStore (
747+ { providedIn : 'root' } ,
748+ linkedStateFeature ,
749+ withMethods ( ( store ) => ( {
750+ addBook ( title : string ) {
751+ patchState ( store , ( { books } ) => ( {
752+ books : [ ...books , title ] ,
753+ } ) ) ;
754+ } ,
755+ } ) )
756+ ) ;
757+
758+ const userStore = TestBed . inject ( UserStore ) ;
759+ const bookStore = TestBed . inject ( BookStore ) ;
760+
761+ bookStore . addBook ( 'The Neverending Story' ) ;
762+ expect ( bookStore . books ( ) ) . toEqual ( [ 'The Neverending Story' ] ) ;
763+
764+ patchState ( userStore , { userId : 2 } ) ;
765+
766+ expect ( bookStore . books ( ) ) . toEqual ( [ ] ) ;
767+ } ) ;
768+ } ) ;
769+ } ) ;
770+
771+ describe ( 'InnerSignalStore access' , ( ) => {
772+ it ( 'can access the state signals' , ( ) => {
773+ const UserStore = signalStore (
774+ { providedIn : 'root' } ,
775+ withState ( { userId : 1 } ) ,
776+ withLinkedState ( ( { userId } ) => ( { value : userId } ) )
777+ ) ;
778+
779+ const userStore = TestBed . inject ( UserStore ) ;
780+
781+ expect ( userStore . value ( ) ) . toBe ( 1 ) ;
782+ } ) ;
783+
784+ it ( 'can access the props' , ( ) => {
785+ const UserStore = signalStore (
786+ { providedIn : 'root' } ,
787+ withProps ( ( ) => ( { userId : 1 } ) ) ,
788+ withLinkedState ( ( { userId } ) => ( { value : ( ) => userId } ) )
789+ ) ;
790+
791+ const userStore = TestBed . inject ( UserStore ) ;
792+
793+ expect ( userStore . value ( ) ) . toBe ( 1 ) ;
794+ } ) ;
795+ } ) ;
796+ } ) ;
568797} ) ;
0 commit comments