Skip to content
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

Decorator for class functions to act like pure pipes in terms of update cycle, so can be used in component templates. #46135

Closed
Phil-DS opened this issue May 25, 2022 · 3 comments
Assignees
Labels
area: core Issues related to the framework runtime core: change detection
Milestone

Comments

@Phil-DS
Copy link

Phil-DS commented May 25, 2022

Which @angular/* package(s) are relevant/related to the feature request?

core

Description

If you have a function that gets an image url based on the inputs. Currently, you would either use a function or a pipe to map the data to a URL. For example, images representing items in a list view for fruits available at a supermarket. I know that, for the inputs, this function will always return the same URL for the fruit in question. An apple will always show an apple, a banana will be a banana, and same with a cherry. Currently, there doesn't seem to be a way to tell the change detector that it will always be the same based on inputs. Or, say, it's depending on internal values on a class. Let the change detection know it's directly correlated to certain inputs.

As a code example, I have the following class:

export class Fruit {
  fruit: string
  idFruit: number

  getFruitUrl() {
    // get me a pic of my fruit based on idFruit
  }
}

To use this in a component to render the output of transformResultToBeHumanReadable, you would either need to use a pipe that triggers the function, trigger this in a change event and cache the results on the component, or, use the function in the template, at the expense of being rendered multiple times. For multiple little things like this, you end up having to create multiple pipes or variables to cache the data. A declaration either via a function decorator or extended in the component decorator could potentially provide the update cycle with the knowledge of where the function updates, in order to not have to always update.

Proposed solution

Add a set of directives that allow functions to be recognised similar to pipes in terms of update cycle. There could even be bindings to tie update state to individual variables on the class.

export class Fruit {
  fruit: string
  idFruit: number

  @PureFunction({changesOn: [this.idFruit]})
  getFruitUrl() {
    // Only relies on idFruit, so only called when idFruit updates
  }

 @PureFunction()
  getFruitUrl(input: any) {
    // Only relies on input, so only changes if the values being input are different.
  }

  @PureFunction({changesOn: [this.idFruit]})
  getFruitUrl(large?: boolean) {
    // Only relies on both class variables and input variables, so changes when they do.
  }
}

or add declarations for the functions in the component declaration.

@Component({
  pureFunctions: [
    {name: "myFunc", changesOn: ["result"]}
  ]
}) class ObjComponent {
  result;
  myFunc() {

  }
}

Alternatives considered

While pipes were designed for this, excessive small pipes that are only needed in one or two places might not be as useful if we can just apply the pipe's change detection strategies to the function. An extension ChangeDetectionStrategy could be used to improve detecting changes if hints are given about how some functions handle change. Again, with the fruit idea, if it relies on only its inputs, and has an output that directly correlates to the input, and won't change its value outside of change detection, like an impure pipe, then it could be able to be considered like a pure Pipe.

@pkozlowski-opensource pkozlowski-opensource added core: change detection area: core Issues related to the framework runtime labels May 25, 2022
@ngbot ngbot bot added this to the needsTriage milestone May 25, 2022
@pkozlowski-opensource
Copy link
Member

I'm not sure if a separate decorator is the best approach here as the same effect can be achieved in a simpler and - IMO - more elegant way, with a simple memo function. A memo function could just compare previous / new arguments and execute re-computation when needed. This is exactly what the pure pipe does, btw.

Given the mentioned utility we could have the following syntax - which is really close to what you are suggesting:

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

Again, this is exactly what the pure pipe does and we could add a similar memo utility to the framework (although there are plenty of 3rd party libraries that do it already).

I'm going to close this issue as we don't plan to implement the @PureFunction annotation suggested here. We do have another issue #20472 that tracks the general concept of "computed properties" and we are looking into ways of implementing it. Happy to chat more in #20472 about possible approaches.

@pkozlowski-opensource pkozlowski-opensource closed this as not planned Won't fix, can't repro, duplicate, stale Jun 30, 2022
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jul 31, 2022
@pkozlowski-opensource
Copy link
Member

Since we are getting multiple issues / feature requests around computed properties, we've opened a canonical issue #47553 to track this use-case. Currently there is no exact solution / design, but we do acknowledge the use-cases and the need for such concept.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: core Issues related to the framework runtime core: change detection
Projects
None yet
Development

No branches or pull requests

2 participants