(initialValue: T, opts?: ModelOptions)
| 62 | * @param options Additional options for the model. |
| 63 | */ |
| 64 | export function createModelSignal<T>(initialValue: T, opts?: ModelOptions): ModelSignal<T> { |
| 65 | const node: InputSignalNode<T, T> = Object.create(INPUT_SIGNAL_NODE); |
| 66 | const emitterRef = new OutputEmitterRef<T>(); |
| 67 | |
| 68 | node.value = initialValue; |
| 69 | |
| 70 | function getter(): T { |
| 71 | producerAccessed(node); |
| 72 | assertModelSet(node.value); |
| 73 | return node.value; |
| 74 | } |
| 75 | |
| 76 | getter[SIGNAL] = node; |
| 77 | getter.asReadonly = signalAsReadonlyFn.bind(getter as any) as () => Signal<T>; |
| 78 | |
| 79 | // TODO: Should we throw an error when updating a destroyed model? |
| 80 | getter.set = (newValue: T) => { |
| 81 | if (!node.equal(node.value, newValue)) { |
| 82 | signalSetFn(node, newValue); |
| 83 | emitterRef.emit(newValue); |
| 84 | } |
| 85 | }; |
| 86 | |
| 87 | getter.update = (updateFn: (value: T) => T) => { |
| 88 | assertModelSet(node.value); |
| 89 | getter.set(updateFn(node.value)); |
| 90 | }; |
| 91 | |
| 92 | getter.subscribe = emitterRef.subscribe.bind(emitterRef); |
| 93 | getter.destroyRef = emitterRef.destroyRef; |
| 94 | |
| 95 | if (ngDevMode) { |
| 96 | getter.toString = () => `[Model Signal: ${getter()}]`; |
| 97 | node.debugName = opts?.debugName; |
| 98 | } |
| 99 | |
| 100 | return getter as typeof getter & |
| 101 | Pick< |
| 102 | ModelSignal<T>, |
| 103 | | typeof ɵINPUT_SIGNAL_BRAND_READ_TYPE |
| 104 | | typeof ɵINPUT_SIGNAL_BRAND_WRITE_TYPE |
| 105 | | typeof ɵWRITABLE_SIGNAL |
| 106 | >; |
| 107 | } |
| 108 | |
| 109 | /** Asserts that a model's value is set. */ |
| 110 | function assertModelSet(value: unknown): void { |
no test coverage detected
searching dependent graphs…