Tutorials Logic, IN info@tutorialslogic.com

Angular Services Create Inject

Service Foundations

An Angular service packages reusable application behavior or shared state behind an injectable boundary. Design its API around one capability, choose provider scope deliberately, and keep HTTP caching, errors, and tests observable to callers.

A service is an injectable class that owns one application capability, such as product retrieval, session state, pricing rules, or telemetry. Components call that public API and remain responsible for presentation and user interaction.

A service is not automatically global. Its provider location determines who shares the instance and when state is discarded. Start with providedIn: "root" for application-wide stateless behavior, then narrow the provider when state belongs to one route or component subtree.

  • Expose intent-revealing methods such as loadOrder and applyCoupon instead of raw mutable fields.
  • Keep API URLs, transport DTOs, caching policy, and retries out of components.
  • Split a service when it owns unrelated reasons to change or requires unrelated dependencies.

Service Learning Path

Mark a service with Injectable when Angular must create it and resolve its dependencies. A providedIn value registers the provider without a separate providers array and allows unused services to be removed from production bundles.

Service Boundary

Treat the public methods, readonly signals, and Observables as the service contract. Keep writable signals, Subjects, HTTP response shapes, and implementation-specific collaborators private so callers cannot bypass invariants.

Service Architecture

Services may compose lower-level collaborators such as HttpClient, configuration tokens, loggers, and domain rules. Keep dependency direction clear: feature services may depend on infrastructure services, while infrastructure code should not import feature components.

Capture dependencies once with inject or constructor injection when the service is created. Circular dependencies usually mean two responsibilities need a smaller shared abstraction or a better ownership boundary.

  • Prefer private readonly dependencies unless consumers need the collaborator itself.
  • Map transport DTOs into application models before returning them.
  • Represent loading, empty, failure, and success separately when the UI must distinguish them.

Application Use

Good service candidates are behavior or state used by several components, data-access policy, and business rules that should be testable without rendering. A one-line formatting helper or state owned by only one component can remain local.

Service Design

Choose signals for synchronous state read by templates and Observables for asynchronous event or request pipelines. Keep writable state private, expose readonly views, and mutate through methods that enforce the service rules.

Service Operations

Operational behavior belongs in the service only when the service owns it. Data services can map responses and define cache policy; state services can expose commands and derived state; orchestration services can coordinate several capabilities without leaking those details to components.

Avoid subscriptions whose ownership is unclear. Return request pipelines to the caller, or bind a long-lived subscription to the service lifetime with DestroyRef and takeUntilDestroyed.

  • A cold HttpClient Observable sends one request per subscription.
  • Use shareReplay only when a documented cache lifetime and invalidation policy exist.
  • Do not turn every request failure into an empty array; empty data and failed data are different states.

Service Diagnosis

A giant service, a public writable Subject, duplicated API URLs, or multiple unexpected instances are design symptoms. Trace who owns the state, where the provider is registered, who subscribes, and whether the public API allows callers to create invalid state.

Provider Lifetime

providedIn: root creates one instance in the application environment injector. A route provider is shared inside that route environment and is released with it. A component provider creates one instance for each component subtree.

  • Use root for shared stateless or global services.
  • Use feature scope when state belongs to one route area.
  • Document unusual provider scopes.

Caching and Failures

A request method should state whether every call is fresh or whether consumers share cached data. Recover only errors the service can meaningfully handle; otherwise preserve the failure so the caller can show retry, offline, or permission UI.

  • Keep components unaware of raw API details.
  • Return typed observables or signals.
  • Handle recoverable errors near the data boundary.

Testing Services

A service test should verify behavior without rendering a component. Mock HTTP, call the method, and assert the request URL, request body, transformation, or emitted state.

Construct pure services directly. Use TestBed when Angular injection or provider overrides matter, and use the HTTP testing provider for HttpClient. For signal state, invoke public commands and read public signals instead of asserting private implementation fields.

  • Test the public contract and observable result.
  • Use focused fakes for dependencies and never call a real backend.
  • Verify cache sharing, refresh, and reset behavior when those rules are part of the contract.

Typed HTTP Service

Typed HTTP Service
import { HttpClient } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';

export interface Product {
  id: number;
  name: string;
  price: number;
}

@Injectable({ providedIn: 'root' })
export class ProductService {
  private readonly http = inject(HttpClient);

  listProducts(): Observable<Product[]> {
    return this.http.get<Product[]>('/api/products');
  }
}

Component Using Async Pipe

Component Using Async Pipe
products$ = this.productService.listProducts();

constructor(private productService: ProductService) {}

// template
// <article *ngFor="let product of products$ | async">
//   {{ product.name }} - {{ product.price }}
// </article>

Service with Error Handling

Service with Error Handling
getProducts(): Observable<Product[]> {
  return this.http.get<Product[]>('/api/products').pipe(
    catchError(error => {
      return throwError(() =>
        new Error('Products could not be loaded', { cause: error })
      );
    })
  );
}

Read-only Signal State Service

Read-only Signal State Service
private readonly selectedIdState = signal<number | null>(null);
readonly selectedId = this.selectedIdState.asReadonly();

selectProduct(id: number): void {
  this.selectedIdState.set(id);
}

clearSelection(): void {
  this.selectedIdState.set(null);
}
Confirm the page outcome

Service Review

5 checks
  • I can define a focused service contract and keep presentation, transport details, and domain policy in their proper owners.
  • I expose readonly signals or Observables and preserve one controlled write path for shared state.
  • I can choose root, route, or component scope from the required sharing and reset lifetime.
  • I can identify duplicate cold requests, define cache invalidation, and preserve truthful failure states.
  • I test public behavior directly or with TestBed and HTTP testing providers without rendering unrelated UI.

Service Review Questions

HttpClient returns a cold Observable, so each subscription starts a new request. The component may subscribe manually and also use the async pipe, or several consumers may call the same service method independently.

Not always. It converts “the request failed” into “there are no records.” If those states lead to different UI or recovery actions, returning [] hides useful information.

Route scope fits state that belongs to one feature visit: a checkout draft, wizard progress, selected admin filters, or an editor session that should reset when the user leaves the area. A root service survives for the application lifetime and is better for authentication, global settings, or deliberately shared caches.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.