Template-Driven Forms — Angular

Template-driven forms menggunakan directive di template untuk mengelola form. Contoh Form @Component({ imports: [FormsModule], template: ` @if…

Template-driven form adalah form yang aturannya ditulis langsung di template, sebagai atribut HTML: required, pattern, min. Kamu gak membuat FormGroup sendiri; Angular membacanya dari template dan membangun kontrol-kontrolnya di belakang layar. Bayangin formulir pesan-antar Martabak Bang Jali yang aturannya sudah tercetak di kertasnya: "wajib diisi" di bawah kolom nama, "diawali 08" di bawah kolom HP. Kasir gak perlu buku aturan; dia membaca aturannya dari kertas itu. Tapi kolom tanpa judul gak dia hitung sama sekali.

🧪 Tebakan cepat: Di dalam <form #f="ngForm">, kolom HP ditulis <input ngModel required />, tanpa atribut name. Nama sudah diisi, jumlah 1, HP masih kosong. Tombol <button [disabled]="f.invalid"> mati atau menyala? Jawabannya ada di bagian "name: tanpa itu kolomnya gak terdaftar".

ngForm dan ngModel

Form pesanan yang sama dengan pelajaran Reactive Forms, ditulis dengan cara template-driven:

import { Component } from '@angular/core'
import { FormsModule, NgForm } from '@angular/forms'

@Component({
  selector: 'app-pesanan',
  standalone: true,
  imports: [FormsModule],
  template: `
    <form #f="ngForm" (ngSubmit)="kirim(f)">
      <input name="nama" ngModel required placeholder="Nama" #nama="ngModel" />
      @if (nama.touched && nama.errors?.['required']) {
        <p class="error">Nama wajib diisi.</p>
      }
      <input name="hp" ngModel required pattern="08[0-9]{8,11}" placeholder="Nomor HP" #hp="ngModel" />
      @if (hp.touched && hp.errors?.['required']) {
        <p class="error">Nomor HP wajib diisi.</p>
      }
      @if (hp.touched && hp.errors?.['pattern']) {
        <p class="error">Nomor HP diawali 08, total 10 sampai 13 angka.</p>
      }
      <input name="jumlah" type="number" [ngModel]="1" required min="1" #jumlah="ngModel" />
      @if (jumlah.touched && jumlah.errors) {
        <p class="error">Jumlah minimal 1.</p>
      }
      <button type="submit" [disabled]="f.invalid">Kirim</button>
    </form>
  `,
})
export class Pesanan {
  kirim(f: NgForm) {
    console.log('terkirim:', JSON.stringify(f.value))
  }
}

name: tanpa itu kolomnya gak terdaftar

NgForm mendaftarkan tiap kolom dengan nama dari atribut name. Kolom HP tanpa name gak bisa didaftarkan. Angular menolaknya, dan console menulis error yang diawali:

NG01352: If ngModel is used within a form tag, either the name attribute must be set or the form
    control must be defined as 'standalone' in ngModelOptions.

Halamannya tetap tampil, tapi kolom HP berdiri sendiri di luar hitungan form:

Dengan name="hp" Tanpa name
f.value (nama Sinta, HP kosong) {"nama":"Sinta","hp":"","jumlah":1} {"nama":"Sinta","jumlah":1}
required di kolom HP dihitung gak dihitung
f.invalid true, Kirim mati false, Kirim menyala

Jadi tombolnya menyala walau HP kosong, dan pesanan terkirim tanpa nomor HP. Itu jawaban tebakannya. Tiap ngModel di dalam <form> butuh name. Kolom yang memang bukan bagian form (misalnya kotak cari) diberi [ngModelOptions]="{ standalone: true }".

ngModel, [ngModel], atau [(ngModel)]

Ditulis Nilai awal Ketikan disimpan di
ngModel kosong f.value saja
[ngModel]="1" 1 f.value saja
[(ngModel)]="pesanan.hp" isi pesanan.hp f.value dan pesanan.hp di class

Two-way binding dari pelajaran sebelumnya tetap jalan di dalam form, asal ada name: <input name="hp" [(ngModel)]="pesanan.hp" /> mengisi objek pesanan di class sambil tetap terdaftar di f.

Reactive atau template-driven?

Reactive Template-driven
Kontrolnya dibuat di class: new FormControl(...) template: ngModel + name
Aturannya Validators.required, Validators.pattern(...) atribut required, pattern="..."
Module ReactiveFormsModule FormsModule
Dites tanpa DOM bisa perlu merender template-nya
Cocok untuk form besar, kolom yang bertambah, aturan yang saling bergantung form kecil: login, kotak cari, satu dua kolom

Dua-duanya menghasilkan errors, touched, dan valid yang sama. Pilih satu cara per form, jangan dicampur.

Latihan memakai bentuk paling kecil: <form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">, satu <input name="email" ngModel required />, dan <button [disabled]="myForm.invalid">Submit</button>.

⚠️ Jebakan yang sering ketemu

TL;DR: Template-driven form menulis aturannya di template: FormsModule memasang NgForm di setiap <form> secara otomatis, #f="ngForm" memberi akses ke f.value dan f.invalid, dan tiap kolom didaftarkan dengan ngModel plus name, dengan aturan berupa atribut seperti required dan pattern. Kolom tanpa name di dalam form memicu NG01352 dan gak ikut dinilai, jadi form bisa terkirim dengan kolom itu kosong. #hp="ngModel" memberi hp.errors dan hp.touched untuk pesan error. Reactive form cocok untuk form besar yang perlu dites tanpa DOM; template-driven untuk form kecil.

Yang akan kamu pelajari