Generally, an effect
in Angular will run once initially and then again whenever any of the signals it depends on change.
Let’s imagine you have an effect
like this:
effect(() => {
if(this.someElement){
console.log(this.someSignal())
}
})
The scenario here is that someElement
is an element from the template accessed with ViewChild
, and someSignal
is just a regular signal.
The intent here might be to do something with someSignal
every time it changes, but only if someElement
exists. This seems like it might do the job, but it won’t.
What will actually happen is that the first time the effect
runs, when someElement
does not exist yet, it will ignore everything inside of this if
conditional as it is not relevant. Now even when someSignal
gets a new value later the effect will not run, because it is being ignored and someElement
will never be re-evaluated.
One way to deal with this is to make sure that the conditional you are testing is also a signal, e.g:
effect(() => {
if(this.someElement()){
console.log(this.someSignal())
}
})
If someElement
is a signal, then the effect will be re-evaluated when its value changes, which would then allow the conditional to pass and for someSignal
to no longer be ignored.
Generally, this is going to be the best solution - if you have things in your effects that change, make sure they are signals.
However, at least until future versions of Angular come out, it is somewhat awkward to get the result of @ViewChild
into a signal so you might prefer to convert everything to signals. Another option is to create a local variable for the value of someSignal
:
effect(() => {
const value = this.someSignal();
if(this.someElement){
console.log(value)
}
})
Now that someSignal
is referenced outside of the conditional it will trigger this effect whenever it is changed, and then someElement
will also be re-evaluated as a result of this.