I was doing a little POC work with Angular 17 and signals and hit a use case that I'm not sure how to address without devolving into ngOnChanges
and @Input
handling.
So, say you have a component with input signals. You then want to copy / set those same values into a service so that components in that hierarchy can access it.
I tried to put writable signal
s in the service, but the trick is trying to set the service values from the component inputs with the right timing. So I could be setting the value into the service with the component initial signal values, but then they don't track changes.
So this seemed like an okay use for toObservable
or effect
, but it sounds that toObservable
uses effects. What seems to happen is that effects run after children components are initialized (ngOnInit
being called). So, my question is how to listen for signal value changes, be able to set them on something else (provider / service) without passing the actual signal which doesn't seem like a good idea.
With just "normal" components, using ngOnChanges
makes it simple to track the changes, but I'm wondering how it could be tracked using signals with the same timing.
Greatly simplified example, but shows the issue
@Injectable()
export class ExampleProvider {
readonly name = signal('');
}
@Component({
...
providers: [ExampleProvider]
})
export class ExampleComponent {
readonly name = input<string>();
private readonly exampleProvider = inject(ExampleProvider);
constructor() {
effect(() => {
// This will run after `ngOnInit` of child components
this.exampleProvider.name.set(this.name());
});
}
}
How can one propagate the changes to the input of the component to the service and have it set before the child components are initialized / re-rendered?
https://stackblitz.com/edit/stackblitz-starters-hjmcgz?file=src%2Fmain.ts
You can run this example.
There are 4 inputs. While the UI will show the correct values, if you open the console, you will notice that during ngOnInit
of the child component, the service has not been updated yet for the values set by effect
nor toObservable
.
You will see that during an input change, two change detections will run. You will see the first one has the old value and then the second one will have updated value.
Also, during the first rendering you will see the values for 2 and 4 have not been set yet:
top-level.component.ts:66 TopLevelComponent ngOnInit
input1: a
input2: b
input3: c
input4: d
child.component.ts:39 ChildComponent ngOnInit
input1: a
input2:
input3: c
input4:
Upgrading to Angular 19 fixes the issue. The timing of effect
s are now correct and the signal values propogate as desired/expected.