← Back to Chapters

Getter/Setter for Data Management

⚙️ Getter/Setter for Data Management

Angular Services   Encapsulation   State Management

? Quick Overview

In Angular applications, getters and setters are powerful tools for data management. They help encapsulate private data, enforce validation rules, and control how components and services access or modify shared state. This leads to cleaner, safer, and more maintainable code across your app.

? Key Concepts

  • Encapsulation: keep fields private and expose them using getters/setters.
  • Safe reads: getters can return copies of arrays/objects to prevent accidental mutation.
  • Validated writes: setters can reject or adjust invalid data before it changes state.
  • Computed data: getters can behave like computed properties (e.g., total revenue).
  • Centralized state: services store data in one place, while components read or update it via getters/setters.

? Syntax & Theory (TypeScript Getters/Setters)

In TypeScript (and therefore Angular), getters and setters are defined using the get and set keywords inside a class. They look like methods but are used like properties.

? View Code Example
// user.model.ts - basic getter/setter example
export class User {
  private _firstName = '';
  private _lastName = '';

  get fullName(): string {
    // Combine first and last name into a single readable value
    return `${this._firstName} ${this._lastName}`.trim();
  }

  set fullName(value: string) {
    // Simple parsing: split the string into first + last name
    const [first, last] = value.split(' ');
    this._firstName = first ?? '';
    this._lastName = last ?? '';
  }
}

Notice how fullName behaves like a normal property from the outside, but internally it runs logic to read or update the underlying fields.

? Example 1 — Centralized Data Service

A common pattern in Angular is to use a service as a single source of truth for shared data. Getters and setters protect that data.

? View Code Example
// data.service.ts - central data store with guarded setter
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class DataService {
  private _items: string[] = [];

  get items(): string[] {
    return [...this._items]; // Return a copy to prevent direct mutation from outside
  }

  set items(newItems: string[]) {
    // Allow bulk updates only when the list is reasonably small
    if (newItems.length <= 100) {
      this._items = [...newItems];
    } else {
      console.warn('Too many items, maximum limit is 100.');
    }
  }

  addItem(item: string) {
    // Helper method for safely adding a single item
    this._items.push(item);
  }
}

? Example 2 — Using the Data Service in a Component

Components consume the service by reading the getter and calling methods. They never touch the private data directly.

? View Code Example
// inventory.component.ts - consuming the DataService in a view
import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-inventory',
  template: `
    <h2>Inventory List</h2>
    <ul>
      <li *ngFor="let item of dataService.items">{{ item }}</li>
    </ul>
    <button (click)="addNewItem()">Add Item</button>
  `
})
export class InventoryComponent {
  constructor(public dataService: DataService) {}

  addNewItem() {
    // Use the service API instead of mutating the array directly
    this.dataService.addItem('New Product ' + (this.dataService.items.length + 1));
  }
}

? Example 3 — Computed Getter (Total Revenue)

Getters can also compute values on the fly from underlying data, such as aggregating the total revenue of all orders.

? View Code Example
// order.service.ts - exposing orders and computed totalRevenue
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class OrderService {
  private _orders: { id: number; total: number }[] = [];

  get orders() {
    // Expose the raw list (could be cloned if needed)
    return this._orders;
  }

  get totalRevenue(): number {
    // Calculate the sum of all order totals whenever accessed
    return this._orders.reduce((sum, order) => sum + order.total, 0);
  }

  addOrder(order: { id: number; total: number }) {
    // Central place to validate and push new orders
    this._orders.push(order);
  }
}

? Live Output / Explanation

  • DataService keeps _items private. The getter returns a copy so components cannot accidentally mutate the internal array.
  • The setter on items validates bulk updates (max 100 items) before replacing the list.
  • InventoryComponent uses dataService.items in the template and calls addItem() to add new products, keeping all logic inside the service.
  • OrderService uses a computed getter totalRevenue to calculate the sum of all orders whenever you read it, so you never have to manually recalculate it in components.

? Typical Use Cases

  • Managing shared lists like products, orders, or notifications from a central service.
  • Exposing derived values like cartTotal, isLoggedIn, or activeUserCount.
  • Applying validation whenever data changes, such as maximum length, non-negative quantities, or required fields.

✅ Tips & Best Practices

  • Keep data fields private and expose them with getters to avoid unwanted external changes.
  • Use setters to implement business rules (e.g., maximum items, non-empty names) before updating state.
  • Return copies of arrays/objects from getters if you want to guarantee immutability.
  • Combine getters with RxJS BehaviorSubject if you need reactive streams of data in multiple components.
  • Keep heavy computations outside of getters (e.g., inside explicit methods) if they become too expensive.
  • Let services own business logic; keep components focused on presentation and user interaction.

? Try It Yourself / Practice Tasks

  • Create a UserService with firstName and lastName fields and a getter fullName that combines them.
  • Add validation in a CartService setter to prevent negative product quantities or a cart size above a chosen limit.
  • Build an AnalyticsService with a computed getter activeUserCount that returns the total number of active users based on an internal array.
  • Refactor an existing component that directly mutates an array into using a service with getters/setters instead.