DEV Community

Manthan Ankolekar
Manthan Ankolekar

Posted on

Exploring Angular's linkedSignal for Reactive State Management

Introduction

With the latest advancements in Angular 19, Signals have become a powerful alternative to traditional state management approaches. Among these, linkedSignal provides a seamless way to derive state from other signals dynamically.

In this blog, we will explore linkedSignal and computed properties using an example from my AngularExamples project. We’ll create a simple dynamic item list, where users can add, select, reset, and remove items, all while leveraging Angular Signals for reactivity.


πŸ”Ή What is linkedSignal?

linkedSignal is a reactive derived signal that updates automatically based on changes to its source signal. It’s useful when you need to derive state from an existing signal without manually updating it.

βœ… Automatically tracks changes in the source signal

βœ… Ensures reactive updates when the source signal changes

βœ… Simplifies state management with fewer manual updates


πŸ”Ή Implementing linkedSignal in an Angular App

Let's build a simple Item List Manager using linkedSignal and computed properties.

1️⃣ Define the Component with Signals

import { JsonPipe } from '@angular/common';
import { Component, computed, linkedSignal, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  imports: [JsonPipe],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})
export class AppComponent {
  title = 'angular-examples';

  // Signal to store the list of items
  items = signal<string[]>(['Item 1', 'Item 2', 'Item 3']);

  // linkedSignal: Automatically selects the first item from the list
  selectedItem = linkedSignal({
    source: this.items,
    computation: (items) => items[0] || null,
  });

  // Computed property to count the number of items
  itemCount = computed(() => this.items().length);

  // Add a new item to the list
  addItem() {
    this.items.update((currentItems) => [
      ...currentItems,
      `Item ${currentItems.length + 1}`,
    ]);
  }

  // Select a specific item
  selectItem(item: string) {
    if (this.items().includes(item)) {
      this.selectedItem.set(item);
    }
  }

  // Reset items to a default list
  resetItems() {
    this.items.set(['Apple', 'Banana', 'Cherry']);
  }

  // Remove the selected item
  removeItem() {
    const selected = this.selectedItem();
    if (selected) {
      this.items.update((currentItems) =>
        currentItems.filter((item) => item !== selected)
      );
      this.selectedItem.set(this.items()[0] || null); // Reset selection
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Creating the UI for the Item List

We will now build a simple UI to display the item list and allow user interactions.

<div class="container">
  <h2>Item List Example</h2>
  <p>Items: {{ items() | json }}</p>
  <p>Selected Item: {{ selectedItem() }}</p>
  <p>Item Count: {{ itemCount() }}</p>

  <div class="button-group">
    <button (click)="addItem()">Add Item</button>
    <button (click)="selectItem('Item 2')">Select Item 2</button>
    <button (click)="resetItems()">Reset Items</button>
    <button (click)="removeItem()">Remove Selected Item</button>
  </div>

  <div class="item-list">
    <h3>Current Items:</h3>
    <ul>
      @for (item of items(); track $index) {
      <li>
        {{ item }}
        <button class="select-button" (click)="selectItem(item)">Select</button>
      </li>
      }
    </ul>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Styling the UI

To make the UI visually appealing, we add some simple CSS styles.

.container {
  font-family: sans-serif;
  padding: 20px;
  max-width: 600px;
  margin: 0 auto;
}

.button-group {
  margin-bottom: 20px;
}

button {
  padding: 8px 16px;
  margin-right: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  cursor: pointer;
  background-color: #f0f0f0;
}

button:hover {
  background-color: #e0e0e0;
}

.item-list {
  border: 1px solid #ddd;
  padding: 10px;
  border-radius: 4px;
  margin-top: 20px;
}

.item-list ul {
  list-style: none;
  padding: 0;
}

.item-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid #eee;
}

.item-list li:last-child {
  border-bottom: none;
}

.select-button {
  padding: 4px 8px;
  font-size: 0.8em;
  background-color: #4caf50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.select-button:hover {
  background-color: #45a049;
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Why Use linkedSignal and computed?

βœ… Automatic Updates: When items change, selectedItem updates automatically.

βœ… No Manual State Management: No need for extra event listeners or manual subscriptions.

βœ… Reactive Performance Boost: Computed properties optimize updates efficiently.


πŸ”Ή Running the Project

To test this in your AngularExamples project:

1️⃣ Clone the Repository:

git clone https://github.com/yourusername/angular-examples.git
cd angular-examples
Enter fullscreen mode Exit fullscreen mode

2️⃣ Checkout to rxresource Branch:

git checkout rxresource
Enter fullscreen mode Exit fullscreen mode

3️⃣ Install Dependencies:

npm install
Enter fullscreen mode Exit fullscreen mode

4️⃣ Run the Development Server:

ng serve
Enter fullscreen mode Exit fullscreen mode

Now, open your browser and go to http://localhost:4200/. πŸŽ‰


πŸ”Ή Conclusion

The linkedSignal API provides a clean and efficient way to manage derived state in Angular applications. Combined with computed properties, it offers a powerful reactive state management solution.

Exploring the Code

Visit the GitHub repository to explore the code in detail.

Live Demo

Check out the working example on StackBlitz


Additional Resources

Feel free to leave comments or questions below! πŸ‘‹

Top comments (0)