@@ -24,7 +24,7 @@ This data is consumed in different screens:
24
24
interface SelectionScreen1 {
25
25
title: string ;
26
26
sortDirection: ' asc' | ' desc' | ' none' ;
27
- list : Array <{ id: number }>
27
+ sortedList : Array <{ id: number }>
28
28
}
29
29
```
30
30
@@ -33,7 +33,7 @@ interface SelectionScreen1 {
33
33
interface SelectionScreen2 {
34
34
title: string ;
35
35
startingDate: Date ;
36
- list : { id: number }
36
+ filteredList : { id: number }
37
37
}
38
38
```
39
39
@@ -90,18 +90,22 @@ A setup of the compoents class based on `RxState` could look like this:
90
90
@Component ({
91
91
selector: ' app-problem' ,
92
92
template: `
93
- <ng-container>
94
- <h1>{{}}</h1>
93
+ <ng-container *rxLet="viewModel$; let vm">
94
+ <h1>{{vm.title}} - {{vm.sortDirection}}</h1>
95
+ <ul>
96
+ <li *ngFor="let item of vm.sortedList">{{item}}</li>
97
+ </ul>
95
98
</ng-container>
96
99
` ,
97
100
providers: [RxState ],
98
101
})
99
102
export class ProblemComponent {
103
+
104
+ viewModel$: Observable <ViewModel >; // ???
100
105
101
106
constructor (private globalState : GlobalState , private state : RxState <Model >) {
102
- this .state .set (title : ' My list' )
107
+ this .state .set ({ title: ' My list' } )
103
108
this .state .connect (' products' , this .globalState .products$ );
104
-
105
109
}
106
110
107
111
toggleSort() {
@@ -117,13 +121,71 @@ In a components template we want to render the the UI for the above explained vi
117
121
interface SelectionScreen1 {
118
122
title: string ;
119
123
sortDirection: ' asc' | ' desc' | ' none' ;
120
- list : Array <{ id: number }>
124
+ sortedList : Array <{ id: number }>
121
125
}
122
126
```
123
127
128
+ A common implementations looks like this:
129
+
130
+
131
+ ``` typescript
132
+ // template removed for brevity
133
+ export class ProblemComponent {
134
+
135
+ private sortedList$ = this .state .select (
136
+ selectSlice ([' sortDirection' , ' list' ]),
137
+ map (() => {
138
+ // sort `list` by `sortDirection` to `sortedList` here
139
+ return sortedList ;
140
+ })
141
+ );
142
+
143
+ viewModel$ = this .state .select (
144
+ selectSlice ([' title' , ' sortedList' , ' sortDirection' ])
145
+ )
146
+
147
+ // BAD: modle viewmodel mix up 👇
148
+ constructor (private globalState : GlobalState , private state : RxState <Model & Pick <ViewModel , ' sortedList' >>) {
149
+ this .state .set ({title: ' My list' })
150
+ this .state .connect (' products' , this .globalState .products$ );
151
+ // BAD: store derived state 👇
152
+ this .state .connect (' sortedList' , this .sortedList$ );
153
+ }
154
+
155
+ // ...
156
+ }
157
+
158
+ ```
124
159
125
160
![ Selections (6)] ( https://user-images.githubusercontent.com/10064416/152422999-db8260f0-69e1-4d99-b6ac-b2b1d043b4b7.png )
126
161
162
+ By removing the sorted list form the state and moving it into the selection
163
+ we can clean up the state's typing and have a nice separation of which data is owned by the component (model) and which data is owned by the template (view model)
164
+
165
+ ``` typescript
166
+ // template removed for brevity
167
+ export class ProblemComponent {
168
+
169
+ private sortedSlice$ = this .state .select (
170
+ selectSlice ([' sortDirection' , ' list' ]),
171
+ map (({list , sortDirection }) => {
172
+ // sort `list` by `sortDirection` to `sortedList` here
173
+ return { sortDirection , sortedList };
174
+ })
175
+ );
176
+
177
+ // GOOD: Derive view model from model 👇
178
+ viewModel$ = smosh ({ title: this .state .select (' title' )}, this .sortedSlice$ );
179
+
180
+ constructor (private globalState : GlobalState , private state : RxState <Model >) {
181
+ this .state .set ({title: ' My list' });
182
+ this .state .connect (' products' , this .globalState .products$ );
183
+ }
184
+
185
+ // ...
186
+ }
187
+
188
+ ```
127
189
128
190
![ Selections (7)] ( https://user-images.githubusercontent.com/10064416/152423026-d23326c2-97d5-4bd0-9015-f498c3fc0e55.png )
129
191
0 commit comments