- Published on
Angular Learning Summary 1 - Signals
- Authors

- Name
- Jinjiu Liu
Content
I started learning fastAPI and Angular for my Portfolio Tracking project. It's been a long time since my previous blog, was busy handling lots of things. I hope this year will be a fruitful one with lot of good changes! Fingers crossed!
Today it's a simple blog about the signals and forms in Angular. They allow us to build reactive variables in the html and bind with reactive values. However, there are a few different concepts and use cases.
Signals in Angular
1. signal() (The Standard Writable Signal)
The foundational building block. It holds a value and notifies the template exactly when that value changes.
- Concept: A box containing data that you can read and write to locally.
- How to define it (TS):
import { signal } from '@angular/core';
// Type is inferred as number
count = signal(0);
- How to use it (HTML): Call it as a function to read the value.
<p>Current count: {{ count() }}</p>
- How the value is updated:
.set(value): Completely overwrites the current value..update(fn): Uses the current value to calculate the new one.
this.count.set(5);
this.count.update(current => current + 1);
- Best Practice & Use Case: Use for local component state (e.g., toggles, counters, loading spinners). Keep them
privateorprotectedso outside components cannot randomly manipulate your state.
2. computed() (The Derived Signal)
A read-only signal that automatically calculates its value based on one or more other signals.
- Concept: The "Math Equation." It automatically recalculates when its dependencies change, and it perfectly caches the result so it doesn't waste CPU cycles.
- How to define it (TS):
import { signal, computed } from '@angular/core';
price = signal(10);
quantity = signal(5);
// Automatically becomes 50
total = computed(() => this.price() * this.quantity());
- How to use it (HTML):
<p>Total Value: ${{ total() }}</p>
- How the value is updated: It updates automatically. You cannot use
.set()or.update()on acomputed. It reacts instantly wheneverpriceorquantitychange. - Best Practice & Use Case: Use for filtering, math, and derived data. Crucial Rule: Never execute side effects inside a computed function (like saving to a database or updating another signal). It must be pure logic.
3. linkedSignal() (The Resettable State Signal)
A writable signal that grabs its initial value from a different signal (like an input()), but automatically resets if the parent pushes new data.
- Concept: "Draft Mode." Let the user edit a local copy, but throw away the edits if the server pushes fresh data.
- How to define it (TS):
import { input, linkedSignal } from '@angular/core';
serverQuantity = input.required<number>();
// Starts as the server value, but can be overwritten locally
draftQuantity = linkedSignal(() => this.serverQuantity());
- How to use it (HTML): Usually bound to an input field.
<input type="number" [(ngModel)]="draftQuantity">
- How the value is updated: The user updates it locally via typing, or you use
.set(). However, if the parent component sends a newserverQuantity, thedraftQuantityinstantly forgets the user's edits and resets to match the new server data. - Best Practice & Use Case: Use for Edit Forms. This completely eliminates the need for lifecycle hooks like
ngOnInitorngOnChangeswhen you need to populate a form with database data.
4. input() (The Signal Input)
The modern replacement for the @Input() decorator. It allows a parent to pass data down, acting as a read-only signal inside the child.
- Concept: A one-way data pipe from parent to child.
- How to define it (TS):
import { input } from '@angular/core';
// Optional input with a default value
theme = input<'dark' | 'light'>('light');
// Required input (Compiler throws an error if parent forgets it)
holding = input.required<HoldingInfo>();
- How to use it (HTML):
<h2>Holding Name: {{ holding().name }}</h2>
- How the value is updated: It is updated by the parent component's HTML. The child component cannot modify it locally (it is strictly read-only).
<app-child [holding]="myHoldingData"></app-child>
- Best Practice & Use Case: Use for passing data down the component tree. Always prefer
input.required()if your component will break without that specific data.
5. model() (The Two-Way Binding Signal)
The modern replacement for the @Input() and @Output() pair. It allows data to flow down from a parent, be edited by the child, and automatically sync back up to the parent.
- Concept: A two-way communication channel between parent and child.
- How to define it (TS):
import { model } from '@angular/core';
// Child component declares the model
isVisible = model<boolean>(false);
- How to use it (HTML): The parent uses "banana-in-a-box" syntax.
<app-modal [(isVisible)]="parentVisibilitySignal"></app-modal>
- How the value is updated: Inside the child component, you treat it like a normal writable signal using
.set()or.update(). The moment you update it, the parent's signal is instantly updated too. - Best Practice & Use Case: Use for building custom UI controls (e.g., custom checkboxes, sliders, dropdowns, or modals). Avoid overusing this for standard data; stick to
input()unless the child must mutate the parent's state.