Skip to content

Computed properties (model => model synchronisation) #47553

@pkozlowski-opensource

Description

@pkozlowski-opensource

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: coreIssues related to the framework runtimecanonicalThis issue represents a canonical design issue in Angular.core: change detectiontype: use-case

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions