Skip to content

Add WritableComputed to allow computed with write operations #55673

@Harpush

Description

@Harpush

Which @angular/* package(s) are relevant/related to the feature request?

core

Description

Revival with use case of #50498

Currently signal produces a writable signal.
Currently computed produces a non writable signal.
What I am missing is a writableComputed.

Use case example:
I have a required input called value but inside the component I can edit this value without affecting the outside. At the end an apply button is clicked and I emit the new value via an output.
Something like this:

class Test {
  value = input.required<string>();
  tempValue = signal(/*??*/);
  commit = output<string>()

  onEdit(newValue: string) {
    this.tempValue.set(newValue);
  }

  onApply() {
    this.commit.emit(this.tempValue());
  }
}

The first problem is tempValue isn't based on value.
The second problem is when value is changed from outside. At this point I wish to discard tempValue and start fresh with the new value.
If I had writableComputed I could do:

tempValue = writableComputed(() => this.value());

Which means anytime value changes it becomes the new tempValue value. If tempValue is edited the new edit is the tempValue new value until something gets changed in value.

Proposed solution

A new writableComputed which tracks the value inside based on the writeable part and will get reset each time the computed function runs to what it creates.
Examples:

const a = signal(7);
const b = writableComputed(() => a() + 1);
// b is 8
const a = signal(7);
const b = writableComputed(() => a() + 1);
b.set(9);
// A design question - 8 or 9
const a = signal(7);
const b = writableComputed(() => a() + 1);
a.set(11);
b.set(9);
// A design question - 12 or 9
const a = signal(7);
const b = writableComputed(() => a() + 1);
// b is 8
// Later
b.set(10)
// b is 10
// Later
a.set(20);
// b is 21

Alternatives considered

Using effect. The downside is with required inputs.

tempValue = signal<string | undefined>(undefined);
effect(() => this.tempValue.set(this.value()), {allowSignalWrites: true});

This will result in tempValue having undefined initially and for example in ngOnInit it will still have undefined although value already have the actual value set. Also a computed will have its value set too.
using writableComputed in ngOnInit will have the correct computed value.

Another option is that scenario hints that something I am doing is wrong from the basis and I would like to hear better suggestions how to handle it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreIssues related to the framework runtimecore: reactivityWork related to fine-grained reactivity in the core frameworkcross-cutting: signals

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions