Goal: I want to reset the activeIndex
when the message
input changes.
It doesn’t seem appropriate to use a computed
signal because the value isn’t solely calculated from the input.
Possible solutions:
- Set
allowSignalWrites: true
in the effect and write the value. - In the effect, write the signal value in an
untracked
block.
Both of these seem like they are sub-optimal.
Stackblitz
Code:
interface Message {
readonly id: number;
readonly contentA: string;
readonly contentB: string;
}
@Component({
selector: 'app-child',
standalone: true,
template: `
<button (click)="setTab(0)" [style.font-weight]="activeIndex() === 0 ? 'bold' : 'normal'">Tab A</button>
<button (click)="setTab(1)" [style.font-weight]="activeIndex() === 1 ? 'bold' : 'normal'">Tab B</button>
<div>{{ activeIndex() === 0 ? message().contentA : message().contentB }}</div>
`,
})
export class ChildComponent {
readonly message = input.required<Message>();
// Can't pass equality check for input?
readonly computedMessage = computed(() => this.message(), {
equal: (a, b) => a.id === b.id,
});
// Assume activeIndex is hooked up to some tab control
readonly activeIndex = model(0);
constructor() {
effect(() => {
const message = this.computedMessage();
// Want to reset activeIndex when a new message is passed
// this.activeIndex.set(0);
});
}
setTab(index: number) {
this.activeIndex.set(index);
}
}
let count = 0;
const getNextMessage = (): Message => {
++count;
return {
id: count,
contentA: `MessageA-${count}`,
contentB: `MessageB-${count}`,
};
};
@Component({
selector: 'app-root',
standalone: true,
template: `
<button (click)="loadNext()">Load next</button>
<hr />
<app-child [message]="message()" />
`,
imports: [ChildComponent],
})
export class PlaygroundComponent {
readonly message = signal<Message>(getNextMessage());
loadNext() {
this.message.set(getNextMessage());
}
}