I want to run an action just once when a condition changes (to true
), e.g.
areAllDetailsLoaded() => run a function
I was thinking to code it as a Signal like
areAllDetailsLoaded=signal(false);
and using it as
effect( () => {
if (areAllDetailsLoaded()) {
storage().getDetails() ....... run code
The problem I’m facing is that obviously the Signal storage
is also taken into account and anytime the storage()
changes the code runs. Which is not something I want. After areAllDetailsLoaded
is changed to true
the code should run only once.
Honestly I’m running out of ideas. Or perhaps my understanding of Angular Signals is not good enough. Therefore a boiler template pattern would be appreciated – perfered with Signals.
I found some very nice observations.
-
Only the signal reads at the top level are triggering the effect, not sure if its by design.
-
The effect returns a reference, with a destroy property to destroy the effect and make sure the signal runs only once!
FULL CODE:
import { Component, effect, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
@Component({
selector: 'app-root',
standalone: true,
template: `
<button (click)="areAllDetailsLoaded.set(true)">areAllDetailsLoaded set true</button>
`,
})
export class App {
areAllDetailsLoaded = signal(false);
storage = signal(false);
name = 'Angular';
constructor() {
const effectRef = effect(() => {
console.log('effect runs');
// const storage1 = this.storage(); // un comment this to see the effect running for each change of storage
if (this.areAllDetailsLoaded()) {
const storage = this.storage();
console.log('code runs');
effectRef?.destroy();
}
});
}
ngOnInit() {
setInterval(() => {
this.storage.set(!this.storage());
console.log('interval', this.storage());
}, 1000);
}
}
bootstrapApplication(App);
Stackblitz Demo
1
Here are two approaches to achieving what you want:
-
Using a private flag with ngOnChanges lifecycle hook
-
Using a BehaviorSubject from RxJS