Episode ini membahas services dan dependency injection: cara membuat service dan meng-inject-nya ke komponen, provider scope di level root, module, dan component, pola singleton service dengan hierarchical injectors, serta interceptor dan provider configuration.

Sejauh ini data tinggal di dalam komponen. Ketika aplikasi tumbuh, logika bisnis dan data harus dibagikan antar komponen — di sinilah service dan dependency injection (DI) berperan.
Episode 6 membahas cara membuat service dan meng-inject-nya, provider scope di level root, module, dan component, pola singleton dengan hierarchical injectors, serta interceptor dan provider configuration. Konsep DI inilah yang membuat Angular begitu kuat untuk aplikasi enterprise.
Service adalah class berdekorator @Injectable yang membungkus logika bisnis dan data yang bisa dibagikan.
ng generate service keranjangSetelah di-generate, isi service dengan state dan method:
import { Injectable, signal, computed } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class KeranjangService {
private readonly isi = signal<Produk[]>([]);
readonly totalItem = computed(() => this.isi().length);
readonly totalHarga = computed(() =>
this.isi().reduce((akum, p) => akum + p.harga, 0),
);
tambah(produk: Produk): void {
this.isi.update((list) => [...list, produk]);
}
hapus(id: number): void {
this.isi.update((list) => list.filter((p) => p.id !== id));
}
}Service ini menyimpan daftar produk dalam signal, lalu menghitung totalItem dan totalHarga dengan computed. providedIn: 'root' berarti service tersedia di seluruh aplikasi.
Karena service disediakan di root, komponen bisa meng-inject-nya lewat fungsi inject:
import { Component, inject } from '@angular/core';
import { KeranjangService } from './keranjang.service';
@Component({
selector: 'app-header',
standalone: true,
template: `<p>Keranjang: {{ keranjang.totalItem() }} item</p>`,
})
export class HeaderComponent {
private readonly keranjang = inject(KeranjangService);
}inject(KeranjangService) adalah cara modern menggantikan injection via konstruktor. Angular membuat satu instance service dan memberikan instance yang sama ke semua komponen yang meng-inject-nya.
Provider scope menentukan siapa yang bisa mengakses instance service:
providedIn: 'root': satu instance untuk seluruh aplikasi, bahkan untuk lazy-loaded modules.providedIn: 'feature-module': instance dibagikan dalam satu feature module.@Component({
selector: 'app-form-item',
standalone: true,
providers: [FormStateService],
template: `...`,
})
export class FormItemComponent {
private readonly state = inject(FormStateService);
}providers: [FormStateService] membuat instance baru setiap kali komponen FormItemComponent dibuat. Dua komponen yang sama tidak akan berbagi state — berguna untuk form atau daftar yang saling independen.
Injector Angular membentuk hierarki mengikuti struktur komponen. Saat sebuah service di-inject, Angular mencari dari level komponen ke atas hingga menemukan provider-nya. Ini memungkinkan pola shadowing: komponen anak bisa mengganti implementasi service hanya untuk subtree-nya, misalnya menyediakan konfigurasi berbeda untuk fitur tertentu.
Interceptor adalah layanan khusus yang menyelip di antara aplikasi dan HTTP client — cocok untuk menambah header, menangani error, atau logging. Detail lengkapnya di episode 14, tetapi pola provider-nya layak dipahami sejak sekarang:
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { provideRouter } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([logInterceptor])),
],
};appConfig di app.config.ts adalah pusat konfigurasi provider aplikasi standalone. provideHttpClient(withInterceptors([...])) mendaftarkan interceptor secara fungsional — pola yang akan kita pakai untuk auth dan error handling.
Inti yang harus dibawa pulang:
@Injectable yang membungkus logika dan data bersama.inject() menggantikan injection via konstruktor di komponen modern.providedIn: 'root' menciptakan singleton untuk seluruh aplikasi.app.config.ts lewat fungsi provide*.Di episode 7 selanjutnya kita akan membahas forms dan validation — membandingkan template-driven dan reactive forms, memakai FormControl, FormGroup, dan FormArray, menyusun validasi built-in dan custom, serta membangun dynamic forms dengan feedback pengguna yang baik.