Lesson 0013 · Frontend · ±14 menit

Component Angular, dilihat dari React

NgModule (dan sekilas standalone component), data binding, structural directive v16, dan Lifecycle Hook

Target lesson ini: Anda bisa menjawab pertanyaan interview "Anda dari React. Jelaskan bagaimana component Angular 16 disusun dan hidup: NgModule, binding, dan Lifecycle Hook." dalam 60 detik, dengan padanan React untuk setiap bagian.

1. Component = class + template + selector

Di React, component adalah function yang mengembalikan JSX. Di Angular, component terdiri dari "An HTML template that declares what renders on the page", "A TypeScript class that defines behavior", dan "A CSS selector that defines how the component is used in a template" [1]. Template dan logika dipisah, lalu disambungkan oleh decorator @Component.

// product-card.component.ts
@Component({
  selector: 'app-product-card',            // dipakai sebagai <app-product-card>
  templateUrl: './product-card.component.html',
  styleUrls: ['./product-card.component.css'],
})
export class ProductCardComponent {
  @Input() product!: Product;              // parent → child (seperti props)
  @Output() deleted = new EventEmitter<number>();  // child → parent (seperti callback prop)
}

2. NgModule: wadah yang tidak ada di React

"An NgModule is a class marked by the @NgModule decorator", dan NgModule "configure the injector and the compiler and help organize related things together" [2]. Di React, Anda cukup import component lalu memakainya. Di Angular berbasis NgModule, template hanya mengenal component, directive, dan pipe yang terdaftar di module-nya.

ArrayIsinyaPadanan Node/React
declarationsComponent, directive, pipe milik module iniDaftar file component di folder fitur
importsModule lain yang dibutuhkan template (mis. FormsModule)import paket npm
providersService untuk DI (dibahas di 0014)Context Provider; providers di NestJS
bootstrapRoot component, biasanya AppComponentcreateRoot(...).render(<App />)
@NgModule({
  declarations: [AppComponent, ProductCardComponent],
  imports: [BrowserModule, FormsModule, HttpClientModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Peran keempat array ini dirangkum di dokumentasi v16: declarations "Declares which components, directives, and pipes belong to the module", imports mengambil "other modules with the components, directives, and pipes that components in the current module need", dan "@NgModule can also add service providers to the application dependency injectors" [2].

Sekilas standalone component. Di v16, component juga bisa ditandai standalone: true: "Standalone components specify their dependencies directly instead of getting them through NgModules", dan aplikasi bisa di-bootstrap tanpa NgModule lewat bootstrapApplication [3]. Ini lebih mirip function component React. Tapi ng new di v16 baru menghasilkan aplikasi standalone kalau diberi flag --standalone [4], jadi codebase v16 di perusahaan kemungkinan besar masih memakai AppModule. Kuasai NgModule dulu.

@Component({
  selector: 'app-product-card',
  standalone: true,
  imports: [CommonModule],                 // dependensi disebut di component itu sendiri
  templateUrl: './product-card.component.html',
})
export class ProductCardComponent {}

3. Data binding: empat bentuk, dua arah

Setiap binding punya "a target which will receive the bound value" dan "a template expression which produces a value from the model" [5]. Kuncinya ada di bentuk kurungnya:

SintaksNamaArah dataPadanan JSX
{{ product.name }}Interpolationclass → tampilan{product.name}
[disabled]="isSaving"Property bindingclass → elemendisabled={isSaving}
(click)="save()"Event bindingelemen → classonClick={save}
[(ngModel)]="name"Two-way bindingdua arahvalue={name} onChange={e => setName(...)}

Property binding "moves a value in one direction, from a component's property into a target element property" [6]. Event binding "lets you listen for and respond to user actions such as keystrokes, mouse movements, clicks, and touches" [7]. Two-way binding "is a combination of square brackets and parentheses, [()]" (sering disebut banana in a box), dan untuk elemen form ia "requires NgModel" [8], yang datang dari FormsModule [9].

Antar component: "@Input() lets a parent component update data in the child component. Conversely, @Output() lets the child send data to a parent component" [10]. Ini sama dengan props dan callback prop di React. Bedanya, child Angular memanggil this.deleted.emit(id), lalu parent mendengarnya dengan (deleted)="onDelete($event)".

4. Structural directive (sintaks v16)

"Structural directives are directives which change the DOM layout by adding and removing DOM elements" [11]. Tanda bintang adalah singkatan: "Angular transforms the asterisk in front of a structural directive into an <ng-template>".

<p *ngIf="products.length === 0; else list">Belum ada Product.</p>   <!-- {cond && ...} / ternary -->
<ng-template #list>
  <ul>
    <li *ngFor="let p of products; trackBy: trackById">{{ p.name }}</li>  <!-- products.map(p => <li key={p.id}>) -->
  </ul>
</ng-template>

<ng-container *ngIf="isAdmin">          <!-- seperti <>...</> Fragment: tanpa elemen tambahan -->
  <app-product-card *ngFor="let p of products" [product]="p" (deleted)="onDelete($event)"></app-product-card>
</ng-container>

<div [ngSwitch]="product.status">
  <span *ngSwitchCase="'active'">Aktif</span>
  <span *ngSwitchDefault>Nonaktif</span>
</div>

5. Lifecycle Hook vs useEffect

AngularKapan dipanggilPadanan React
constructorSaat class dibuat. Hanya untuk inject dependency.Badan function sebelum hook
ngOnChanges(changes)"Called before ngOnInit() (if the component has bound inputs) and whenever one or more data-bound input properties change"useEffect(..., [productId])
ngOnInit()"Called once, after the first ngOnChanges()"useEffect(..., []) (mount)
ngOnDestroy()"Called immediately before Angular destroys the directive or component"fungsi cleanup yang di-return useEffect

Kutipan di tabel berasal dari dokumentasi Lifecycle Hook v16 [12]. Dua aturan yang sering ditanyakan berasal dari halaman yang sama: "You should not, for example, fetch data in a component constructor" dan "An ngOnInit() is a good place for a component to fetch its initial data". Di ngOnDestroy, "Unsubscribe Observables and detach event handlers to avoid memory leaks" [12] (detail unsubscribe dibahas di 0015).

export class ProductDetailComponent implements OnChanges, OnInit, OnDestroy {
  @Input() productId!: number;
  private sub?: Subscription;

  constructor(private products: ProductService) {}       // inject saja, jangan fetch

  ngOnChanges(changes: SimpleChanges) {                   // input berubah
    if (changes['productId'] && !changes['productId'].firstChange) this.load();
  }
  ngOnInit() { this.load(); }                             // data awal
  ngOnDestroy() { this.sub?.unsubscribe(); }              // cleanup

  private load() {
    this.sub?.unsubscribe();
    this.sub = this.products.getById(this.productId).subscribe(p => (this.product = p));
  }
  product?: Product;
}

Latihan 1 — Kuis campuran

Topiknya sengaja diselang-seling. Jawab tanpa scroll ke atas; kalau ragu, tebak dulu, lalu baca penjelasannya.

Kuis di lesson ini adalah feedback cepat, bukan bukti penguasaan; yang dicatat hanya hasil di mock interview 0018.

Latihan 2 — Ucapkan dengan suara keras

Pasang timer 60 detik. Jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai.

"Anda dari React. Jelaskan bagaimana component Angular 16 disusun dan hidup: NgModule, binding, dan Lifecycle Hook."
Bandingkan dengan jawaban contoh dan checklist-nya

"Di Angular 16 component adalah class TypeScript dengan decorator @Component yang berisi selector, template, dan style. Component itu dideklarasikan di sebuah NgModule: declarations untuk component milik module, imports untuk module lain seperti FormsModule, providers untuk service. Di v16 juga ada standalone component yang mengisi imports sendiri, lebih mirip function component React. Template terhubung ke class lewat binding: {{ }} interpolation, [prop] property binding, (event) event binding, dan [(ngModel)] two-way. Parent ke child lewat @Input() seperti props, child ke parent lewat @Output() dan EventEmitter seperti callback prop. Untuk kondisi dan list saya pakai *ngIf dan *ngFor dengan trackBy. Lifecycle-nya: constructor hanya untuk inject dependency, ngOnInit untuk ambil data awal, ngOnChanges saat input berubah, dan ngOnDestroy untuk unsubscribe, mirip mount, dependency, dan cleanup di useEffect."

Checklist: apakah Anda menyebut…

  • isi @Component (selector, template, style) dan perannya sebagai class?
  • declarations / imports / providers di NgModule?
  • standalone component sebagai alternatif, dan padanannya di React?
  • empat bentuk binding dan arah datanya?
  • @Input/@Output serta *ngIf/*ngFor (sintaks v16)?
  • constructor vs ngOnInit / ngOnChanges / ngOnDestroy dengan padanan useEffect?

Kalau kurang dari 4 poin, ulangi sebelum lesson 0014. Soal ini juga akan muncul lagi di mock interview 0018.

Bacaan utama (±15 menit) Angular v16: Component lifecycle, dokumentasi resmi versi 16. Baca bagian "Lifecycle event sequence", "Initializing a component or directive", dan "Cleaning up on instance destruction". Kalau masih ada waktu, lanjut ke NgModules.

Ringkasan untuk dicetak: Reference: Angular 16 dasar untuk developer React · Istilah: GLOSSARY.md

Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "kapan saya butuh ngOnChanges kalau sudah ada ngOnInit?" atau "apa bedanya attribute dan DOM property di property binding?"