-
-
Notifications
You must be signed in to change notification settings - Fork 204
adding ability to define computed properties easily #1175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hi! Thanks for the issue! Did you check the overloads? E.g. we have: this.state.connect('nameOfComputedProp', value$, (state, value) => state.prop1 + value); Is it helpful? |
Thanks! The distinction in my case is that the external observable I want to compute a property from other properties on the same component state. |
What about a operator like this? vm$ = selectComputed(
this.state,
'nameOfComputedProp', ['prop1', 'prop2'], (state, {prop1, prop2}) => prop1 + prop2
); |
Problem interface Model {
selectedProductId: number;
sort: boolean;
products: any[];
sortedProducts: any[];
}
this.state = new RxState<ViewModel>();
this.state.connect(this.state.select(selectSlice(['sort', 'products'])),
(state, { sort, products }) => {
return {
sortedProducts: products.sort(sort)
};
});
interface ViewModel {
selectedProductId: number;
sort: boolean;
sortedProducts: any[];
}
vm$ = this.state.select(selectSlice(['selectedProductId', 'sort', 'sortedProducts'])); <ng-conainer *rxLet="vm$; let vm">
<b>Selected Id: {{vm.selectedProductId}}</b>
<button (click)="sort()">
{{vm.sort ? 'ASC' : 'DSC'}}
<button>
<ul>
<li *ngFor="let product of vm.sortedProducts">{{product}}</li>
</ul>
</ng-container> The conceptual problem I see here is we store derived state directly instead of computing it.
If we look at it from the 2 perspectives DX and performance we can say: DX We already thought about this operator here in a different way here: So the above suggested Performance From a performance perspective, we can consider 3 major things here:
As we store the sorted list also in the state with connect we hold them 2 times in our memory even if nobody is consuming the sorted list. We could avoid sorting too often by implementing some distinction logic to avoid:
but the impact would not be that much compared to what we can do in the template only the sharing part is important in the component class. The rendering performance is most impactful. In the
In addition With this in mind, I would avoid having additional logic in the core of Let me know if my example above is correct or if you would like to have products also unsorted in the template. This would need an adoption then. |
Wow, first of all great job @BioPhoton of summarizing our conversation from yesterday Few things I thought about -
|
I found the old PR we discussed in the call: Opened it here for you to check: #1192 Also added some docs on that area here: WDYS? |
So I had more time to progress with docs on the new operator. With the old PR the implementation only for works for
// ✔ Target API suggestion 👇
viewModel$ = smosh({
prop1: 'prop1', // string
prop2: prop2$ // Observable<'prop2'>
},
slice1$, // Observable<{prop3: 3}>
slice2$ // Observable<{prop4: 'four'}>,
// durationSelector$ (optional) => test DX
);
// Result:
// Observable<{
// prop1: 'prop1',
// prop2: 'prop2',
// prop3: 3,
// prop4: 'four'
// }> Let me know how you want to proceed. Here is the new explanation of the problem: Advanced derivation architectureGoal ArchitectureThe problem We have the following state sources to manage:
A setup of the compoents class based on @Component({
selector: 'app-problem',
template: `
<ng-container *rxLet="viewModel$; let vm">
<h1>{{vm.title}} - {{vm.sortDirection}}</h1>
<ul>
<li *ngFor="let item of vm.sortedList">{{item}}</li>
</ul>
</ng-container>
`,
providers: [RxState],
})
export class ProblemComponent {
viewModel$: Observable<ViewModel>; // ???
constructor(private globalState: GlobalState, private state: RxState<Model>) {
this.state.connect('title', this.globalState.title$);
this.state.connect('products', this.globalState.products$);
}
toggleSort() {
this.state.set('sort', ({sort}) => !sort))
}
} In a components template we want to render the the UI for the above explained view model interface SelectionScreen1 {
title: string;
sortDirection: 'asc' | 'desc' | 'none';
sortedList: Array<{ id: number }>
} A common implementations looks like this: // template removed for brevity
export class ProblemComponent {
private sortedList$ = this.state.select(
selectSlice(['sortDirection', 'list']),
map(() => {
// sort `list` by `sortDirection` to `sortedList` here
return sortedList;
})
);
viewModel$ = this.state.select(
selectSlice(['title', 'sortedList', 'sortDirection'])
)
// ❌ BAD: modle viewmodel mix up 👇
constructor(private globalState: GlobalState, private state: RxState<Model & Pick<ViewModel, 'sortedList'>>) {
// ...
// ❌ BAD: store derived state 👇
this.state.connect('sortedList', this.sortedList$);
}
// ...
} By removing the sorted list form the state and moving it into the selection // template removed for brevity
export class ProblemComponent {
private sortedSlice$ = this.state.select(
selectSlice(['sortDirection', 'list']),
map(({list, sortDirection}) => {
// sort `list` by `sortDirection` to `sortedList` here
return { sortDirection, sortedList };
})
);
// ✔ GOOD: Derive view model from model 👇
viewModel$ = smosh({ title: this.state.select('title')}, this.sortedSlice$);
// ✔ GOOD: Derive view model from model 👇
viewModel$ = smosh({
title: this.state.select('title'),
...this.sortedSlice$
});
constructor(private globalState: GlobalState, private state: RxState<Model>) {
// ...
}
// ...
} |
Problem Solved By The Feature
I want to be able to define computed properties in an easier way
For example, currently I have to do this:
(maybe I'm doing it wrong and there's a better way...)
Solution
I'd like to have something like this:
alternatives Considered
Maybe an operator?
But I think it's a quite common use case to justify a method
Additional Context
I don't mind creating a PR for that if you approve the design
The text was updated successfully, but these errors were encountered: