← Angular: components, reactivity and application decisions
02 / 10 · 65 MIN

Signals, inputs and output contracts

Separate writable state, derivations and parent-controlled inputs. Interpret reactive dependencies, transforms and events to retain explicit data flow.

Writable state and controlled reading

signal creates a reactive container with an initial value. Calling quantity() reads the value; set supplies a new value and update computes it from the current one. asReadonly exposes reads without write methods, but does not deeply freeze stored arrays and objects.

A consumer directly changing an array can therefore bypass ownership policy even without finding set. Keep write operations with the owner and produce values consistent with equality policy. Default equality is Object.is: mutating an object and supplying the same reference again does not guarantee notification.

Derivations and dynamic dependencies

computed represents a read-only derived value. Calculation is lazy and memoized: the first read calculates, later reads can use cache and a changed dependency invalidates the value for the next read. Do not call set on the result.

Dependencies are the reads actually performed. If a branch reads count only when expanded is true, evaluation with expanded false creates no count dependency. A later evaluation can add that dependency and another can remove it.

This explains why names appearing in the function body do not necessarily represent permanent subscriptions.

Static inputs and required presence

input declares entries statically recorded by Angular in component or directive property initializers. New inputs are not created inside a handler in response to data. input(1) has default 1; input<number>() without a default permits undefined. input.required<number>() requires template usage to supply the input and does not implicitly add undefined to its type.

That build check does not prove a number satisfies a business rule or that a remote response was validated. InputSignal is read-only: the child reads value(), but communicates a proposal to the parent rather than changing the input directly.

Transforms, aliases and type boundaries

An input transform should be pure and statically analyzable. Its parameter type defines what the binding accepts; its result defines the signal’s read value. A function receiving number and returning a string with px permits numeric input and textual reading. booleanAttribute and numberAttribute have their own semantics: the former treats literal string false as false and the latter can produce NaN when conversion fails.

An alias changes the template name without changing the TypeScript property. value with alias sliderValue is still read as value() in the class.

Outputs carry explicit intent

output returns a typed emitter. In the example, next.emit carries the proposed number and $event delivers that number to the parent listener. It is not necessarily a MouseEvent: that would depend on the payload chosen by the component.

Angular outputs do not automatically bubble through the DOM and names are case-sensitive. A wrapper participating in the flow needs an explicit connection through appropriate subscription or forwarding. The example accepts the proposal immediately into local state, but another application can apply validation before supplying new inputs.

When the contract is two-way

model is a special writable input that can propagate changes to a two-way binding. If a control declares value = model(0), Angular also creates valueChange. When volume is a writable parent signal, documented usage is [(value)]="volume", passing the instance rather than volume().

This differs from this lesson’s example, which uses a read-only input and separate proposal output. Choose according to intended responsibility: a value control can expose model; a button requesting a decision can communicate intent without assuming approval.

Model inputs do not support input transforms in the consulted documentation.

Diagnosis and flow summary

To explain a missing update, follow the path: was there an API write? Was the value considered different? Did the consumer read that signal in a tracked context?

Is the output bound at the correct level? Do not immediately replace every input with model or every value with duplicated state. In the example, the sequence is parent 3, child proposal 5, parent receipt 5 and new input 5.

Each step has identifiable responsibility. Assessment uses excerpts and selections to evaluate that reading without requiring compilation, installation or code execution.

// Reading example: parent owns quantity; child emits a proposed value.
import {Component, computed, input, output, signal} from '@angular/core';

@Component({
  selector: 'number-stepper',
  standalone: true,
  template: `<p>{{ label() }}</p>
    <button type="button" (click)="requestNext()">Request next</button>`,
})
export class NumberStepper {
  readonly value = input.required<number>();
  readonly step = input(1);
  readonly next = output<number>();
  readonly label = computed(() => `Quantity: ${this.value()}`);
  requestNext() { this.next.emit(this.value() + this.step()); }
}

@Component({
  selector: 'quantity-review',
  standalone: true,
  imports: [NumberStepper],
  template: `<number-stepper [value]="quantity()" [step]="2"
    (next)="quantity.set($event)" />`,
})
export class QuantityReview {
  readonly quantity = signal(3);
}
IN PRACTICE

The parent starts at 3. The child receives that value and step 2, calculates proposed value 5 on click and emits next. The parent binding accepts that proposal into local signal state.

The child does not call set on its input. The computed label derives from current input. The excerpt includes no service validation, persistence or remote authorization.

Common pitfalls

Calling set on computed or ordinary input; expecting deep freezing from asReadonly; changing arrays while retaining identity and assuming notification; confusing template aliases with TypeScript names; expecting DOM bubbling from outputs.

Related topics: Components, templates and view identity · Lifecycle, effects and reactive cleanup · Forms, validation and user intent

Take this idea with you

Declare who writes and who reads. Computed derivations follow actual reads; ordinary inputs are read-only and outputs communicate data explicitly. Model serves another contract, writing and two-way binding. Syntax does not replace data validation or business rules.

Create account

Reference: Writable signals and updates · Angular 22; standalone components