> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# Signals

> Fine-grained reactivity with Angular Signals for better performance and simpler state management

## Overview

Signals are a reactive primitive that provide fine-grained reactivity for managing state in Angular applications. They enable automatic dependency tracking and efficient change detection.

## Core Concepts

### Writable Signals

Create signals using the `signal()` function to hold reactive values.

```typescript theme={null}
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <div class="counter">
      <h2>Count: {{ count() }}</h2>
      <button (click)="increment()">+</button>
      <button (click)="decrement()">-</button>
      <button (click)="reset()">Reset</button>
    </div>
  `
})
export class CounterComponent {
  // Create a writable signal with initial value
  count = signal(0);

  increment(): void {
    // Update signal value
    this.count.update(value => value + 1);
  }

  decrement(): void {
    this.count.update(value => value - 1);
  }

  reset(): void {
    // Set absolute value
    this.count.set(0);
  }
}
```

### Reading Signal Values

Access signal values by calling them as functions.

```typescript theme={null}
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-user-profile',
  template: `
    <div>
      <h2>{{ username() }}</h2>
      <p>Score: {{ score() }}</p>
    </div>
  `
})
export class UserProfileComponent {
  username = signal('John Doe');
  score = signal(0);

  constructor() {
    // Read signal values in TypeScript
    console.log('Current username:', this.username());
    console.log('Current score:', this.score());
  }
}
```

## Computed Signals

Create derived values that automatically update when dependencies change.

```typescript theme={null}
import { Component, signal, computed } from '@angular/core';

interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

@Component({
  selector: 'app-shopping-cart',
  template: `
    <div class="cart">
      <h2>Shopping Cart</h2>
      <div *ngFor="let item of items()">
        <span>{{ item.name }} x {{ item.quantity }}</span>
        <span>\${{ item.price * item.quantity }}</span>
      </div>
      <hr>
      <p>Total Items: {{ totalItems() }}</p>
      <p>Subtotal: \${{ subtotal() }}</p>
      <p>Tax (8%): \${{ tax() }}</p>
      <p><strong>Total: \${{ total() }}</strong></p>
    </div>
  `
})
export class ShoppingCartComponent {
  items = signal<CartItem[]>([
    { id: 1, name: 'Laptop', price: 999.99, quantity: 1 },
    { id: 2, name: 'Mouse', price: 29.99, quantity: 2 }
  ]);

  // Computed signals automatically track dependencies
  totalItems = computed(() => {
    return this.items().reduce((sum, item) => sum + item.quantity, 0);
  });

  subtotal = computed(() => {
    return this.items().reduce(
      (sum, item) => sum + (item.price * item.quantity), 
      0
    );
  });

  tax = computed(() => this.subtotal() * 0.08);

  total = computed(() => this.subtotal() + this.tax());

  addItem(item: CartItem): void {
    this.items.update(current => [...current, item]);
  }

  updateQuantity(id: number, quantity: number): void {
    this.items.update(current =>
      current.map(item => 
        item.id === id ? { ...item, quantity } : item
      )
    );
  }
}
```

<Note>
  Computed signals are read-only and automatically recompute only when their dependencies change, making them highly efficient.
</Note>

## Effects

Execute side effects when signals change using the `effect()` function.

```typescript theme={null}
import { Component, signal, effect } from '@angular/core';
import { AnalyticsService } from './analytics.service';

@Component({
  selector: 'app-search',
  template: `
    <input 
      [value]="searchQuery()"
      (input)="searchQuery.set($any($event.target).value)"
      placeholder="Search..."
    >
    <p>Searching for: {{ searchQuery() }}</p>
  `
})
export class SearchComponent {
  searchQuery = signal('');

  constructor(private analytics: AnalyticsService) {
    // Effect runs whenever searchQuery changes
    effect(() => {
      const query = this.searchQuery();
      if (query.length > 2) {
        console.log('Searching for:', query);
        this.analytics.trackSearch(query);
      }
    });
  }
}
```

### Effect Cleanup

Register cleanup functions to handle resource disposal.

```typescript theme={null}
import { Component, signal, effect } from '@angular/core';

@Component({
  selector: 'app-auto-save',
  template: `
    <textarea 
      [value]="content()"
      (input)="content.set($any($event.target).value)"
    ></textarea>
    <p>{{ saveStatus() }}</p>
  `
})
export class AutoSaveComponent {
  content = signal('');
  saveStatus = signal('All changes saved');

  constructor() {
    effect((onCleanup) => {
      const currentContent = this.content();
      this.saveStatus.set('Saving...');

      // Set up auto-save timer
      const timerId = setTimeout(() => {
        this.saveToServer(currentContent);
        this.saveStatus.set('All changes saved');
      }, 1000);

      // Cleanup function cancels pending save
      onCleanup(() => {
        clearTimeout(timerId);
      });
    });
  }

  private saveToServer(content: string): void {
    console.log('Saving:', content);
  }
}
```

## Advanced Patterns

### Signal-based State Management

```typescript theme={null}
import { Injectable, signal, computed } from '@angular/core';

export interface Todo {
  id: number;
  title: string;
  completed: boolean;
  createdAt: Date;
}

export type TodoFilter = 'all' | 'active' | 'completed';

@Injectable({ providedIn: 'root' })
export class TodoStore {
  // Private state
  private todos = signal<Todo[]>([]);
  private filter = signal<TodoFilter>('all');

  // Public computed selectors
  readonly allTodos = this.todos.asReadonly();
  
  readonly filteredTodos = computed(() => {
    const todos = this.todos();
    const currentFilter = this.filter();

    switch (currentFilter) {
      case 'active':
        return todos.filter(t => !t.completed);
      case 'completed':
        return todos.filter(t => t.completed);
      default:
        return todos;
    }
  });

  readonly stats = computed(() => {
    const todos = this.todos();
    return {
      total: todos.length,
      active: todos.filter(t => !t.completed).length,
      completed: todos.filter(t => t.completed).length
    };
  });

  readonly currentFilter = this.filter.asReadonly();

  // Actions
  addTodo(title: string): void {
    const newTodo: Todo = {
      id: Date.now(),
      title,
      completed: false,
      createdAt: new Date()
    };
    this.todos.update(todos => [...todos, newTodo]);
  }

  toggleTodo(id: number): void {
    this.todos.update(todos =>
      todos.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  }

  removeTodo(id: number): void {
    this.todos.update(todos => todos.filter(t => t.id !== id));
  }

  setFilter(filter: TodoFilter): void {
    this.filter.set(filter);
  }

  clearCompleted(): void {
    this.todos.update(todos => todos.filter(t => !t.completed));
  }
}
```

### Using the Store

```typescript theme={null}
import { Component } from '@angular/core';
import { TodoStore, TodoFilter } from './todo.store';

@Component({
  selector: 'app-todos',
  template: `
    <div class="todo-app">
      <input 
        #input
        (keyup.enter)="addTodo(input.value); input.value = ''"
        placeholder="What needs to be done?"
      >
      
      <div class="filters">
        <button (click)="setFilter('all')">All ({{ store.stats().total }})</button>
        <button (click)="setFilter('active')">Active ({{ store.stats().active }})</button>
        <button (click)="setFilter('completed')">Completed ({{ store.stats().completed }})</button>
      </div>

      <ul>
        <li *ngFor="let todo of store.filteredTodos()">
          <input 
            type="checkbox" 
            [checked]="todo.completed"
            (change)="store.toggleTodo(todo.id)"
          >
          <span [class.completed]="todo.completed">{{ todo.title }}</span>
          <button (click)="store.removeTodo(todo.id)">Delete</button>
        </li>
      </ul>

      <button 
        *ngIf="store.stats().completed > 0"
        (click)="store.clearCompleted()"
      >
        Clear Completed
      </button>
    </div>
  `
})
export class TodosComponent {
  constructor(public store: TodoStore) {}

  addTodo(title: string): void {
    if (title.trim()) {
      this.store.addTodo(title.trim());
    }
  }

  setFilter(filter: TodoFilter): void {
    this.store.setFilter(filter);
  }
}
```

## RxJS Interop

Convert between signals and observables for integration with existing RxJS code.

```typescript theme={null}
import { Component, signal } from '@angular/core';
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { debounceTime, switchMap } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';

interface SearchResult {
  id: number;
  title: string;
}

@Component({
  selector: 'app-search-rxjs',
  template: `
    <input 
      [value]="searchTerm()"
      (input)="searchTerm.set($any($event.target).value)"
    >
    <ul>
      <li *ngFor="let result of results()">{{ result.title }}</li>
    </ul>
    <p *ngIf="isLoading()">Loading...</p>
  `
})
export class SearchRxJSComponent {
  searchTerm = signal('');
  
  // Convert signal to observable
  private searchTerm$ = toObservable(this.searchTerm);

  // Process with RxJS operators
  private results$ = this.searchTerm$.pipe(
    debounceTime(300),
    switchMap(term => 
      term ? this.http.get<SearchResult[]>(`/api/search?q=${term}`) : []
    )
  );

  // Convert observable back to signal
  results = toSignal(this.results$, { initialValue: [] });
  isLoading = signal(false);

  constructor(private http: HttpClient) {}
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Computed for Derived State" icon="calculator">
    Prefer `computed()` over manually updating multiple signals. It's more efficient and easier to maintain.
  </Card>

  <Card title="Keep Signals Fine-Grained" icon="atom">
    Split state into focused signals rather than one large object signal for better granularity.
  </Card>

  <Card title="Effects for Side Effects Only" icon="wand-magic-sparkles">
    Use `effect()` for side effects, not for deriving state. Use `computed()` for derived values.
  </Card>

  <Card title="Immutable Updates" icon="shield">
    Use immutable patterns with `update()` to ensure predictable state changes.
  </Card>
</CardGroup>

<Warning>
  Don't read signals inside effects without using them. This creates unnecessary dependencies and can cause performance issues.
</Warning>

## Performance Benefits

Signals provide significant performance improvements:

* **Fine-grained updates**: Only components reading changed signals are updated
* **Automatic dependency tracking**: No manual subscription management
* **Efficient computation**: Computed values only recalculate when dependencies change
* **Works with OnPush**: Seamlessly integrates with `OnPush` change detection

## Migration from RxJS

```typescript theme={null}
// Before: Using RxJS BehaviorSubject
import { BehaviorSubject } from 'rxjs';

export class OldComponent {
  private countSubject = new BehaviorSubject(0);
  count$ = this.countSubject.asObservable();

  increment(): void {
    this.countSubject.next(this.countSubject.value + 1);
  }
}

// After: Using Signals
import { signal } from '@angular/core';

export class NewComponent {
  count = signal(0);

  increment(): void {
    this.count.update(n => n + 1);
  }
}
```

## Additional Resources

* [Change Detection](/advanced/change-detection)
* [Effects and Reactive Programming](https://angular.dev/guide/signals)
* [RxJS Integration](https://angular.dev/guide/signals/rxjs-interop)
