1- import { lastValueFrom , of } from 'rxjs' ;
1+ import {
2+ computed ,
3+ inject ,
4+ Injectable ,
5+ ResourceStatus ,
6+ Signal ,
7+ } from '@angular/core' ;
28import { TestBed } from '@angular/core/testing' ;
3- import { computed , Signal } from '@angular/core' ;
9+ import { tapResponse } from '@ngrx/operators' ;
10+ import { lastValueFrom , Observable , of , pipe , switchMap , tap } from 'rxjs' ;
11+ import { describe , expect , it } from 'vitest' ;
12+ import { EntityState , setAllEntities , withEntities } from '../entities' ;
13+ import { rxMethod } from '../rxjs-interop' ;
414import {
515 getState ,
616 patchState ,
717 signalStore ,
818 signalStoreFeature ,
19+ type ,
920 withComputed ,
1021 withFeature ,
22+ withHooks ,
1123 withMethods ,
24+ withProps ,
1225 withState ,
1326} from '../src' ;
1427
@@ -17,35 +30,23 @@ type User = {
1730 name : string ;
1831} ;
1932
20- function withMyEntity < Entity > ( loadMethod : ( id : number ) => Promise < Entity > ) {
21- return signalStoreFeature (
22- withState ( {
23- currentId : 1 as number | undefined ,
24- entity : undefined as undefined | Entity ,
25- } ) ,
26- withMethods ( ( store ) => ( {
27- async load ( id : number ) {
28- const entity = await loadMethod ( 1 ) ;
29- patchState ( store , { entity, currentId : id } ) ;
30- } ,
31- } ) )
32- ) ;
33- }
34-
35- describe ( 'withFeatureFactory' , ( ) => {
36- it ( 'should allow a sum feature' , ( ) => {
37- function withSum ( a : Signal < number > , b : Signal < number > ) {
33+ describe ( 'withFeature' , ( ) => {
34+ it ( 'provides methods' , async ( ) => {
35+ function withMyEntity < Entity > ( loadMethod : ( id : number ) => Promise < Entity > ) {
3836 return signalStoreFeature (
39- withComputed ( ( ) => ( { sum : computed ( ( ) => a ( ) + b ( ) ) } ) )
37+ withState ( {
38+ currentId : 1 as number | undefined ,
39+ entity : undefined as undefined | Entity ,
40+ } ) ,
41+ withMethods ( ( store ) => ( {
42+ async load ( id : number ) {
43+ const entity = await loadMethod ( 1 ) ;
44+ patchState ( store , { entity, currentId : id } ) ;
45+ } ,
46+ } ) )
4047 ) ;
4148 }
42- signalStore (
43- withState ( { a : 1 , b : 2 } ) ,
44- withFeature ( ( store ) => withSum ( store . a , store . b ) )
45- ) ;
46- } ) ;
4749
48- it ( 'should allow to pass elements from a SignalStore to a feature' , async ( ) => {
4950 const UserStore = signalStore (
5051 { providedIn : 'root' } ,
5152 withMethods ( ( ) => ( {
@@ -66,4 +67,119 @@ describe('withFeatureFactory', () => {
6667 entity : { id : 1 , name : 'Konrad' } ,
6768 } ) ;
6869 } ) ;
70+
71+ it ( 'provides state signals' , async ( ) => {
72+ const withDouble = ( n : Signal < number > ) =>
73+ signalStoreFeature (
74+ withComputed ( ( state ) => ( { double : computed ( ( ) => n ( ) * 2 ) } ) )
75+ ) ;
76+
77+ const Store = signalStore (
78+ { providedIn : 'root' } ,
79+ withState ( { counter : 1 } ) ,
80+ withMethods ( ( store ) => ( {
81+ increaseCounter ( ) {
82+ patchState ( store , ( { counter } ) => ( { counter : counter + 1 } ) ) ;
83+ } ,
84+ } ) ) ,
85+ withFeature ( ( { counter } ) => withDouble ( counter ) )
86+ ) ;
87+
88+ const store = TestBed . inject ( Store ) ;
89+
90+ expect ( store . double ( ) ) . toBe ( 2 ) ;
91+ store . increaseCounter ( ) ;
92+ expect ( store . double ( ) ) . toBe ( 4 ) ;
93+ } ) ;
94+
95+ it ( 'provides properties' , ( ) => {
96+ @Injectable ( { providedIn : 'root' } )
97+ class Config {
98+ baseUrl = 'https://www.ngrx.io' ;
99+ }
100+ const withUrlizer = ( baseUrl : string ) =>
101+ signalStoreFeature (
102+ withMethods ( ( ) => ( {
103+ createUrl : ( path : string ) =>
104+ `${ baseUrl } ${ path . startsWith ( '/' ) ? '' : '/' } ${ path } ` ,
105+ } ) )
106+ ) ;
107+
108+ const Store = signalStore (
109+ { providedIn : 'root' } ,
110+ withProps ( ( ) => ( {
111+ _config : inject ( Config ) ,
112+ } ) ) ,
113+ withFeature ( ( store ) => withUrlizer ( store . _config . baseUrl ) )
114+ ) ;
115+
116+ const store = TestBed . inject ( Store ) ;
117+ expect ( store . createUrl ( 'docs' ) ) . toBe ( 'https://www.ngrx.io/docs' ) ;
118+ } ) ;
119+
120+ it ( 'can be cominbed with inputs' , ( ) => {
121+ function withLoadEntities < Entity extends { id : number } , Filter > ( config : {
122+ filter : Signal < Filter > ;
123+ loader : ( filter : Filter ) => Observable < Entity [ ] > ;
124+ } ) {
125+ return signalStoreFeature (
126+ type < { state : EntityState < Entity > & { status : ResourceStatus } } > ( ) ,
127+ withMethods ( ( store ) => ( {
128+ _loadEntities : rxMethod < Filter > (
129+ pipe (
130+ tap ( ( ) => patchState ( store , { status : ResourceStatus . Loading } ) ) ,
131+ switchMap ( ( filter ) =>
132+ config . loader ( filter ) . pipe (
133+ tapResponse ( {
134+ next : ( entities ) =>
135+ patchState (
136+ store ,
137+ { status : ResourceStatus . Resolved } ,
138+ setAllEntities ( entities )
139+ ) ,
140+ error : ( ) =>
141+ patchState ( store , { status : ResourceStatus . Error } ) ,
142+ } )
143+ )
144+ )
145+ )
146+ ) ,
147+ } ) ) ,
148+ withHooks ( {
149+ onInit : ( { _loadEntities } ) => _loadEntities ( config . filter ) ,
150+ } )
151+ ) ;
152+ }
153+
154+ const Store = signalStore (
155+ { providedIn : 'root' } ,
156+ withEntities < User > ( ) ,
157+ withState ( { filter : { name : '' } , status : ResourceStatus . Idle } ) ,
158+ withMethods ( ( store ) => ( {
159+ setFilter ( name : string ) {
160+ patchState ( store , { filter : { name } } ) ;
161+ } ,
162+ _load ( filters : { name : string } ) {
163+ return of (
164+ [ { id : 1 , name : 'Konrad' } ] . filter ( ( person ) =>
165+ person . name . startsWith ( filters . name )
166+ )
167+ ) ;
168+ } ,
169+ } ) ) ,
170+ withFeature ( ( store ) =>
171+ withLoadEntities ( { filter : store . filter , loader : store . _load } )
172+ )
173+ ) ;
174+
175+ const store = TestBed . inject ( Store ) ;
176+
177+ expect ( store . entities ( ) ) . toEqual ( [ ] ) ;
178+ store . setFilter ( 'K' ) ;
179+ TestBed . flushEffects ( ) ;
180+ expect ( store . entities ( ) ) . toEqual ( [ { id : 1 , name : 'Konrad' } ] ) ;
181+ store . setFilter ( 'Sabine' ) ;
182+ TestBed . flushEffects ( ) ;
183+ expect ( store . entities ( ) ) . toEqual ( [ ] ) ;
184+ } ) ;
69185} ) ;
0 commit comments