Skip to content

Instantly share code, notes, and snippets.

@Airblader
Last active May 10, 2021 07:20
Show Gist options
  • Save Airblader/28a84533a9d1c46d4abb9a956aa93eef to your computer and use it in GitHub Desktop.
Save Airblader/28a84533a9d1c46d4abb9a956aa93eef to your computer and use it in GitHub Desktop.
Typescript / Angular Decorator: DetectChangesOnChange
// Copyright 2018 Ingo Bürk
// Released under the MIT License (https://opensource.org/licenses/MIT)
// TODO: Is there a better way to get access to a ChangeDetectorRef from
// within the decorator?
/**
* Decorator for class methods which caches the returned value and,
* if the value has changed, triggers change detection.
*
* Requires the method to take a ChangeDetectorRef as the first argument,
* e.g.:
*
* ```
* @Component({ selector: "app-person", … })
* export class PersonComponent {
* constructor(private cdRef: ChangeDetectorRef) {}
*
* public get something() {
* return this._calculateSomething(this.cdRef);
* }
*
* @DetectChangesOnChange()
* private _calculateSomething(cdRef: ChangeDetectorRef) {
* // …
* }
* }
* ```
*
* NOTE: This requires the cached function to take exactly one argument,
* which is a ChangeDetectorRef.
*
* NOTE: Use with caution. Generally speaking, you should not call functions
* from the template. If possible, watch for changes on the tracked
* object and update internal component state instead.
*/
function DetectChangesOnChange() {
return function(
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<(cdRef: ChangeDetectorRef) => any>
) {
const memoizedName = `${propertyKey}__DetectChangesOnChange`;
const _original = descriptor.value;
descriptor.value = function (cdRef: ChangeDetectorRef) {
const result = _original.apply(this, [cdRef]);
const detectChanges = result !== this[memoizedName];
this[memoizedName] = result;
if (detectChanges) {
cdRef.detectChanges();
}
return result;
}
return descriptor;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment