-
Notifications
You must be signed in to change notification settings - Fork 27k
Description
Which @angular/* package(s) are relevant/related to the feature request?
core
Description
Angular dirty-checking mechanism works well for synchronising (propagating) model changes to the DOM. However, there are legitimate situations where one would like to compute / derive a new model value (from the existing model values) before rendering. Angular has no such concept - even if requested by users (ex.: #20472).
Proposed solution
We don't have any concrete solution proposal at the moment but we are exploring the space and will open a RFC when the solution space becomes clearer. But even without the concrete proposal on a table, there are certain properties that we would like to see in the final solution:
- semantics of "recompute only if one of the dependencies changed";
- composability: it should be possible to construct of chain of computed values (computed depending on other computed);
- great integration in the framework, ex.:
@Input- it should be possible to compute new model value based on input values changing over time;- convenient syntax for accessing computed values in a component / teamplate.
Alternatives considered
While Angular doesn't have the dedicated concept of the computed properties, there are several existing mechanisms that can serve the purpose (although with different caveats). Here is an overview of the possible approaches that work today.
Use an external reactivity system
Many Angular users integrate RxJS, or similar push-based reactivity systems, into their template rendering pipeline. Most (all?) of the push-based reactivity systems have a concept of deriving values (ex. combinelatest in RxJS).
Use memoization
We could introduce a simple memo utility function. A memo function could just compare previous / new arguments and execute re-computation when needed, similar to what the pure pipe does today. Ex. (taken from #46135):
export class AppComponent {
fruit = 'banana';
idFruit = 0;
getNameFruitUrl = memo((name: string) => `/fruits/${name}.jpg`);
getIdFruitUrl = memo(() => `/fruits/${this.idFruit}.jpg`, () => [this.idFruit]);
getLargeFruitUrl = memo((large?: boolean) => `/${large ? 'large' : 'small'}/${this.fruit}.jpg`,
() => [this.fruit]
);
}Here is a working stackblitz: https://stackblitz.com/edit/angular-ivy-txzpmf?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fmemo.ts
Existing issues / PRs
The idea of computed properties was discussed in the past in the issue tracker - most notably in #20472 (which generated number of comments and ~70 upvotes).