I have to mange a relation for X*2=Y
, there Y and Y are input fields.
In the moment, I trigger by a change of the input field the related variable as X changed => Y=X*2 Y changed => X=Y/2
import { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
imports: [FormsModule],
template: `
<h1>{{ title }}</h1>
<input type="number"
[(ngModel)]="X"
(change)="FnX()"
/>
*2 =
<input type="number"
[(ngModel)]="Y"
(change)="FnY()"
>
`,
})
export class App {
protected title = 'Relationshiphandling: 2*X=Y'
X=signal(0);
Y=signal(0);
FnX() {
this.Y.set(2 * this.X());
};
FnY() {
this.X.set(this.Y() / 2);
};
}
Now question is, is it possible to solve such cycles with linkedSignal ? As this AI generated example will not run:
X: WritableSignal<number> = linkedSignal({
source: () => this.Y(),
computation: (_source: number, _prev?: { source: number; value: number }) => {
const newValue = _source / 2;
**if (this.X() == newValue) { return 0};** // Do nothing if the same value
console.log( "this.X.set(newValue)")
this.X.set(newValue);
return newValue;
}
});
Y: WritableSignal<number> = linkedSignal({
source: () => this.X(),
computation: (_source: number, _prev?: { source: number; value: number }) => {
const newValue = _source * 2;
**if (this.Y() == newValue) { return 0};** // Do nothing if the same value
this.Y.set(newValue)
console.log( "this.Y.set(newValue)")
return newValue;
}
});
I think it is not possible with linkedSignal
due to circular dependency issue.
Instead go for effect
along with a combination of untracked
(Which can be used to ignore signal changes inside the callback).
First we place the signal we want to track at the top of the effect and the signal update inside the untracked
callback, this update will not retrigger the other effect and will evaluate only once.
constructor() {
effect(() => {
const x = this.X();
untracked(() => {
this.Y.set(2 * x);
});
});
effect(() => {
const y = this.Y();
untracked(() => {
this.X.set(y / 2);
});
});
}
import { Component, signal, effect, untracked } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
imports: [FormsModule],
template: `
<h1>{{ title }}</h1>
<input type="number"
[(ngModel)]="X"
/>
*2 =
<input type="number"
[(ngModel)]="Y"
>
`,
})
export class App {
protected title = 'Relationshiphandling: 2*X=Y';
X = signal(0);
Y = signal(0);
constructor() {
effect(() => {
const x = this.X();
untracked(() => {
console.log('y set');
this.Y.set(2 * x);
});
});
effect(() => {
const y = this.Y();
untracked(() => {
console.log('x set');
this.X.set(y / 2);
});
});
}
}
bootstrapApplication(App);
If you are not a fan of effect
, then go for (ngModelChange)
and update the other signal manually.
import { Component, signal, effect, untracked } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
imports: [FormsModule],
template: `
<h1>{{ title }}</h1>
<input type="number"
[(ngModel)]="X"
(ngModelChange)="FnX()"
/>
*2 =
<input type="number"
[(ngModel)]="Y"
(ngModelChange)="FnY()"
>
`,
})
export class App {
protected title = 'Relationshiphandling: 2*X=Y';
X = signal(0);
Y = signal(0);
FnX() {
this.Y.set(2 * this.X());
}
FnY() {
this.X.set(this.Y() / 2);
}
}
bootstrapApplication(App);