Angular
Async Programming
Array Manipulation
Frontend Development
JavaScript

Async update of array in Angular

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Asynchronously updating an array in Angular is usually not about the array itself. The real issue is how new data flows from an observable into component state and whether change detection sees the update predictably. A good solution combines observable streams, immutable updates, and clear ownership of list state.

Replace the Array After an Async Call

For a one-time HTTP request, the simplest pattern is to subscribe and replace the array reference when data arrives.

typescript
1import { Component, OnInit } from '@angular/core';
2import { HttpClient } from '@angular/common/http';
3
4interface Item {
5  id: number;
6  name: string;
7}
8
9@Component({
10  selector: 'app-items',
11  template: `<li *ngFor="let item of items">{{ item.name }}</li>`
12})
13export class ItemsComponent implements OnInit {
14  items: Item[] = [];
15
16  constructor(private http: HttpClient) {}
17
18  ngOnInit(): void {
19    this.http.get<Item[]>('/api/items').subscribe(data => {
20      this.items = data;
21    });
22  }
23}

Replacing the reference is important because it works well with Angular's default change detection and with OnPush components.

Append or Merge Without Mutating In Place

When updates arrive incrementally from polling, websockets, or repeated user actions, avoid mutating the existing array directly if you rely on OnPush or want predictable rendering.

typescript
this.http.get<Item[]>('/api/items').subscribe(newItems => {
  this.items = [...this.items, ...newItems];
});

If items should merge by key, build that logic explicitly.

typescript
1const merged = new Map(this.items.map(item => [item.id, item]));
2for (const item of newItems) {
3  merged.set(item.id, item);
4}
5this.items = Array.from(merged.values());

That makes the update rule obvious instead of scattering list logic across the component.

Use a Store Pattern for Shared Arrays

If several components depend on the same list, keep the array in a service-backed stream instead of updating separate component-local copies.

typescript
1import { Injectable } from '@angular/core';
2import { BehaviorSubject } from 'rxjs';
3
4@Injectable({ providedIn: 'root' })
5export class ItemStore {
6  private readonly itemsSubject = new BehaviorSubject<Item[]>([]);
7  readonly items$ = this.itemsSubject.asObservable();
8
9  setItems(items: Item[]): void {
10    this.itemsSubject.next(items);
11  }
12
13  addItem(item: Item): void {
14    this.itemsSubject.next([...this.itemsSubject.value, item]);
15  }
16}

A shared stream avoids the common bug where one component appends while another replaces and the UI drifts out of sync.

Prefer the async Pipe When Possible

Manual subscriptions work, but Angular's async pipe often produces cleaner templates and safer lifecycle behavior.

typescript
1@Component({
2  selector: 'app-items',
3  template: `<li *ngFor="let item of items$ | async">{{ item.name }}</li>`
4})
5export class ItemsComponent {
6  items$ = this.store.items$;
7
8  constructor(private store: ItemStore) {}
9}

This removes subscription cleanup from the component and makes the data flow easier to follow.

Handle Errors and Loading State Explicitly

An empty array can mean "nothing returned" or "request failed". Those are different states and should not be merged accidentally.

Track loading and error state separately so UI behavior stays honest:

  • loading indicator while awaiting response
  • error message for failed requests
  • array content only for successful data

That distinction matters more than the array update itself.

Common Pitfalls

  • Pushing into the existing array and expecting OnPush views to refresh.
  • Mixing replace, append, and merge semantics without deciding which one the feature actually needs.
  • Subscribing manually in many components to the same stream and then duplicating update logic.
  • Ignoring error and loading state so failures look like empty data.
  • Forgetting to unsubscribe from long-lived manual subscriptions.

Summary

  • Async array updates in Angular are really state-flow problems.
  • Replace array references instead of mutating in place for predictable rendering.
  • Use explicit append or merge rules when data arrives incrementally.
  • Keep shared arrays in a service-backed observable or store pattern.
  • Prefer async pipe and explicit loading and error handling to reduce UI bugs.

Course illustration
Course illustration

All Rights Reserved.