Masukkan Password
66 Membaca Data Formulir dari UI
Lecture ini sangat penting, karena sekarang kita mulai benar-benar menulis aplikasi Expense Tracker dan menghubungkan teori TypeScript yang sebelumnya kita pelajari dengan DOM, form HTML, class, event, type assertion, union type, dan type safety.
Saya akan jelaskan dari awal secara bertahap, termasuk kenapa setiap syntax diperlukan, bukan hanya apa yang ditulis.
1. Tujuan Utama Lecture
Di lecture sebelumnya kita sudah tahu aplikasi yang akan dibuat:
Expense Tracker
Sekarang target kita masih sederhana:
Ketika user mengisi form lalu menekan “Add Expense”, kita membaca data dari form tersebut dan membuat sebuah object
Expense.
Misalnya user mengisi:
Type : Credit
Description : YouTube Earnings
Amount : 5500
Ketika klik:
Add Expense
TypeScript akan mengambil:
credit
YouTube Earnings
5500
kemudian membuat object:
{
id: 1,
type: "credit",
description: "YouTube Earnings",
amount: 5500
}
Untuk sementara object tersebut belum ditampilkan ke UI.
Kita hanya membuat dan melakukan:
console.log(expense);
Menampilkan ke UI akan dilakukan di lecture berikutnya.
2. Struktur Form HTML
Form yang diberikan instructor kira-kira mempunyai elemen seperti ini:
<form>
<select id="expense-type">
<option value="credit">Credit</option>
<option value="debit">Debit</option>
</select>
<input id="description" type="text">
<input id="amount" type="number">
<button class="add-expense-button">
Add Expense
</button>
</form>
Ada 4 element penting:
┌─────────────────────────────┐
│ Expense Type │
│ [ Credit ▼ ] │
│ │
│ Description │
│ [ YouTube Earnings ] │
│ │
│ Amount │
│ [ 5500 ] │
│ │
│ [ Add Expense ] │
└─────────────────────────────┘
Kita perlu mengakses keempatnya dari TypeScript:
select → expense type
input → description
input → amount
button → add expense
3. Mengakses Element dengan getElementById
Untuk select yang memiliki:
id="expense-type"
kita bisa menggunakan:
document.getElementById("expense-type")
Artinya:
“Browser, tolong carikan element yang memiliki ID
expense-type.”
Masalahnya: TypeScript Tidak Tahu Element-nya Apa
Ini konsep yang sangat penting.
Ketika kita menulis:
const expType = document.getElementById("expense-type");
TypeScript tidak otomatis berpikir:
“Oh, ini pasti
<select>.”
Karena dari perspektif TypeScript, method:
getElementById()
bisa menemukan berbagai jenis HTML element:
<input>
<button>
<div>
<p>
<select>
<form>
...
Secara umum hasilnya dianggap:
HTMLElement | null
Jadi TypeScript memiliki dua kekhawatiran:
Kekhawatiran 1
Element-nya mungkin tidak ditemukan.
Maka:
null
mungkin terjadi.
Kekhawatiran 2
Kalaupun ditemukan, TypeScript belum tahu element spesifiknya.
Apakah:
HTMLInputElement?
HTMLButtonElement?
HTMLSelectElement?
HTMLDivElement?
4. Mengapa Menggunakan !?
Instructor kemudian menggunakan:
document.getElementById("expense-type")!
Tanda:
!
disebut Non-null Assertion Operator.
Artinya:
“TypeScript, saya yakin hasil ini tidak
null.”
Misalnya:
const expType =
document.getElementById("expense-type")!;
Kita sebagai developer tahu HTML memang memiliki:
<select id="expense-type">
Jadi kita yakin element tersebut ada.
! Bukan Type Casting
Ini sangat penting.
Jangan mencampurkan:
!
dengan:
as HTMLSelectElement
Mereka mempunyai tugas berbeda.
!
element!
berarti:
“Saya yakin ini bukan
nullatauundefined.”
as HTMLSelectElement
element as HTMLSelectElement
berarti:
“Saya yakin element ini adalah HTMLSelectElement.”
Jadi:
document.getElementById("expense-type")!
as HTMLSelectElement
secara konsep melakukan dua hal:
! as HTMLSelectElement
│ │
▼ ▼
Bukan null Ini adalah <select>
5. Type Assertion
Instructor kemudian melakukan type casting/assertion:
const expType =
document.getElementById("expense-type")!
as HTMLSelectElement;
Sekarang TypeScript tahu:
expType
↓
HTMLSelectElement
Sehingga kita bisa mengakses property khusus <select> seperti:
expType.value
6. Dua Cara Type Assertion
Seperti yang sudah kita pelajari sebelumnya, ada dua syntax.
Cara 1 — as
const expType =
document.getElementById("expense-type")
as HTMLSelectElement;
Cara 2 — Angle Brackets
const expType =
<HTMLSelectElement>
document.getElementById("expense-type");
Keduanya memiliki tujuan yang sama.
Namun dalam project modern, terutama jika menggunakan JSX/React, biasanya lebih aman dan umum menggunakan:
as HTMLSelectElement
7. Mengakses Description
Untuk input description:
<input id="description">
kita menggunakan:
const expenseDescription =
document.getElementById("description")!
as HTMLInputElement;
Sekarang TypeScript mengetahui:
expenseDescription
↓
HTMLInputElement
Jadi kita bisa:
expenseDescription.value
8. Mengakses Amount
Sama seperti description:
<input id="amount">
TypeScript:
const expenseAmount =
document.getElementById("amount")!
as HTMLInputElement;
Sekarang:
expenseAmount.value
bisa digunakan.
Tapi ada satu masalah yang sangat penting.
Nanti kita bahas.
9. Mengakses Button dengan querySelector
Button tidak memiliki ID.
Misalnya:
<button class="add-expense-button">
Add Expense
</button>
Karena menggunakan class:
add-expense-button
kita bisa menggunakan:
document.querySelector(".add-expense-button")
Perhatikan titik:
".add-expense-button"
^
Titik berarti kita sedang mencari class.
Ingat aturan CSS Selector
#id
untuk ID.
Contoh:
document.querySelector("#amount")
Sedangkan:
.class
untuk class.
Contoh:
document.querySelector(".add-expense-button")
10. Type Assertion untuk Button
Karena kita tahu hasilnya adalah button:
const addExpenseButton =
document.querySelector(".add-expense-button")!
as HTMLButtonElement;
Sekarang:
addExpenseButton
↓
HTMLButtonElement
11. Jadi Sekarang Kita Punya 4 Variable
Secara konsep:
HTML
│
├── <select id="expense-type">
│ ↓
│ expType
│
├── <input id="description">
│ ↓
│ expenseDescription
│
├── <input id="amount">
│ ↓
│ expenseAmount
│
└── <button class="add-expense-button">
↓
addExpenseButton
Ini adalah jembatan:
HTML
↓
TypeScript
TypeScript sekarang bisa berinteraksi dengan form.
12. Selanjutnya Membuat Class Expense
Sekarang instructor membuat:
class Expense {
...
}
Kenapa?
Karena setiap kali user menambahkan expense, kita ingin membuat object Expense.
Misalnya:
const expense = new Expense(
"credit",
"YouTube Earnings",
5500
);
Object yang dihasilkan kira-kira:
Expense
├── id
├── type
├── description
└── amount
13. Property id
Instructor awalnya membuat:
id: number;
Artinya setiap expense memiliki ID berupa number.
Misalnya:
Expense 1 → id = 1
Expense 2 → id = 2
Expense 3 → id = 3
ID ini nanti sangat berguna untuk:
update
delete
select expense
show details
14. Kenapa ID Harus Unik?
Bayangkan kita punya:
Groceries
Movie Ticket
YouTube Earnings
Semuanya adalah object yang berbeda.
Kita perlu cara untuk membedakan:
Expense #1
Expense #2
Expense #3
Maka kita memberikan ID.
Misalnya:
id = 1
15. Static currentId
Instructor menggunakan property static:
static currentId: number = 0;
Ini konsep yang sangat penting.
static berarti property tersebut milik class, bukan milik setiap object.
Tanpa static
Misalnya:
class Expense {
currentId = 0;
}
Setiap object memiliki counter sendiri:
Expense 1
currentId = 0
Expense 2
currentId = 0
Expense 3
currentId = 0
Tidak berguna untuk membuat ID global.
Dengan static
class Expense {
static currentId = 0;
}
Maka hanya ada satu counter yang dibagikan oleh semua instance.
Expense class
│
currentId = 0
│
┌──────────┼──────────┐
▼ ▼ ▼
Expense 1 Expense 2 Expense 3
16. Bagaimana ID Dibuat?
Di constructor:
constructor(...) {
this.id = ++Expense.currentId;
}
Perhatikan:
++Expense.currentId
Ini berarti:
Naikkan
currentIdterlebih dahulu, kemudian gunakan nilainya.
Instance pertama
Awalnya:
currentId = 0
Buat expense pertama:
++Expense.currentId
menjadi:
0 → 1
kemudian:
this.id = 1
Hasil:
Expense #1
Instance kedua
Sekarang:
currentId = 1
Buat object lagi:
1 → 2
Maka:
this.id = 2
Instance ketiga
2 → 3
Maka:
this.id = 3
17. Kenapa currentId Static Tetapi id Bukan?
Ini pertanyaan yang bagus.
Karena fungsi mereka berbeda.
currentId
Adalah counter bersama:
0 → 1 → 2 → 3 → 4
Maka:
static currentId
id
Adalah ID milik masing-masing object.
Expense 1 → id = 1
Expense 2 → id = 2
Expense 3 → id = 3
Maka:
id
bukan static.
18. Property type
Instructor menggunakan:
type: "credit" | "debit";
Ini adalah kombinasi:
Literal Type
+
Union Type
Artinya:
type
↓
hanya boleh
├── "credit"
└── "debit"
Contoh valid:
type = "credit";
atau:
type = "debit";
Contoh invalid:
type = "salary";
19. Default Value debit
Instructor memberikan:
type: "credit" | "debit" = "debit";
Artinya kalau tidak ada value yang diberikan, default-nya:
debit
Jadi:
type = "debit";
20. Property description
description: string = "";
Artinya:
description
↓
string
Default:
""
yaitu empty string.
21. Property amount
amount: number = 0;
Artinya amount harus berupa:
number
dan nilai awalnya:
0
22. Bentuk Class-nya
Secara konsep class menjadi:
class Expense {
id: number;
static currentId: number = 0;
type: "credit" | "debit" = "debit";
description: string = "";
amount: number = 0;
constructor(
type: "credit" | "debit",
description: string,
amount: number
) {
this.id = ++Expense.currentId;
this.type = type;
this.description = description;
this.amount = amount;
}
}
Mari kita baca seperti manusia.
“Saya mempunyai class bernama Expense.”
Setiap Expense mempunyai:
id → number
type → credit atau debit
description → string
amount → number
Dan setiap kali Expense dibuat:
ID dinaikkan
type diisi
description diisi
amount diisi
23. Constructor
Constructor:
constructor(
type: "credit" | "debit",
description: string,
amount: number
)
Artinya ketika membuat object:
new Expense(...)
kita wajib memberikan tiga informasi:
1. type
2. description
3. amount
Contoh:
new Expense(
"credit",
"YouTube Earnings",
5500
);
24. this Itu Apa?
Misalnya:
this.type = type;
Ada dua type di sini.
this.type
↑
property milik object
type
↑
parameter constructor
Jadi:
this.type = type;
dibaca:
“Ambil parameter
type, lalu simpan ke propertytypemilik object ini.”
Begitu juga:
this.description = description;
dan:
this.amount = amount;
25. Contoh Membuat Object
Misalnya:
const expense = new Expense(
"credit",
"YouTube Earnings",
5500
);
Constructor menerima:
type = "credit"
description = "YouTube Earnings"
amount = 5500
Kemudian object menjadi:
expense
│
├── id: 1
├── type: "credit"
├── description: "YouTube Earnings"
└── amount: 5500
26. Event Listener
Sekarang kita ingin object tersebut dibuat ketika user klik tombol.
Kita menggunakan:
addExpenseButton.addEventListener(
"click",
() => {
...
}
);
Artinya:
“Ketika
addExpenseButtondiklik, jalankan function ini.”
27. Callback Function
Bagian:
() => {
...
}
adalah function.
Function tersebut disebut callback karena kita memberikannya kepada:
addEventListener()
Browser akan memanggil function tersebut ketika event terjadi.
Flow:
User
↓
klik button
↓
"click" event
↓
addEventListener
↓
callback dijalankan
↓
buat Expense
28. Masalah Pertama: Form Melakukan Submit
Ketika instructor pertama kali mencoba:
addExpenseButton.addEventListener("click", () => {
const expense = new Expense(...);
console.log(expense);
});
object memang muncul di console.
Tetapi kemudian langsung menghilang.
Kenapa?
Karena button berada di dalam:
<form>
Secara default, button di dalam form bertindak sebagai:
submit button
Jadi ketika diklik:
Click
↓
Submit form
↓
Browser mengirim request
↓
Page reload
↓
JavaScript/TypeScript dijalankan ulang
↓
Console lama hilang
29. Kenapa Page Reload?
Form HTML secara default memang dirancang untuk mengirim data ke server.
Misalnya:
<form>
...
<button>Submit</button>
</form>
ketika submit:
Browser
↓
Form submitted
↓
HTTP request
↓
Server
Tetapi dalam project ini kita belum ingin mengirim data ke server.
Kita hanya ingin:
Klik
↓
Baca data
↓
Buat object
↓
Update UI
Maka default behavior tersebut harus dihentikan.
30. event.preventDefault()
Callback kita bisa menerima event:
(event) => {
event.preventDefault();
}
Kemudian:
addExpenseButton.addEventListener("click", (event) => {
event.preventDefault();
...
});
preventDefault() berarti:
“Browser, jangan jalankan behavior default dari event ini.”
Dalam kasus ini:
Default behavior
↓
Submit form
↓
Reload page
kita hentikan.
31. Jadi preventDefault() Bukan Membatalkan Click
Ini sering disalahpahami.
event.preventDefault();
bukan berarti click-nya dibatalkan.
Click tetap terjadi.
Yang dibatalkan adalah default action setelah click.
Jadi:
Click
↓
Callback dijalankan
↓
preventDefault()
↓
Jangan submit form
Callback kita tetap berjalan.
32. Dummy Data Terlebih Dahulu
Sebelum mengambil data dari form, instructor mengetes class menggunakan dummy data:
const expense = new Expense(
"credit",
"test",
1000
);
Kemudian:
console.log(expense);
Hasilnya:
Expense {
id: 1,
type: "credit",
description: "test",
amount: 1000
}
Ini cara debugging yang bagus.
Kita memastikan dulu:
Class bekerja?
↓
Constructor bekerja?
↓
ID bekerja?
↓
Object berhasil dibuat?
Baru kemudian kita hubungkan dengan form.
33. Membuktikan ID Berjalan
Instructor kemudian membuat beberapa object:
const expense1 = new Expense(...);
const expense2 = new Expense(...);
const expense3 = new Expense(...);
Hasilnya:
expense1 → id = 1
expense2 → id = 2
expense3 → id = 3
Ini membuktikan:
static currentId
berfungsi sebagai counter global.
34. Sekarang Masalah Berikutnya: Membaca value
Setelah class dan event berjalan, kita tidak ingin lagi:
new Expense(
"credit",
"test",
1000
);
Karena itu hanya dummy data.
Kita ingin:
UI
↓
ambil value
↓
Expense
35. Mengambil Value dari Select
Kita sudah punya:
const expType =
document.getElementById("expense-type")!
as HTMLSelectElement;
Kemudian:
expType.value
Misalnya user memilih:
Credit
maka:
expType.value
bisa menghasilkan:
"credit"
Jika user memilih Debit:
"debit"
36. Tetapi Ada Masalah TypeScript
Ini bagian yang sangat penting.
Kita punya:
type: "credit" | "debit"
Tetapi:
expType.value
memiliki type:
string
Bukan:
"credit" | "debit"
Jadi kalau langsung:
new Expense(
expType.value,
...
);
TypeScript akan protes.
Kenapa?
Karena:
expType.value
↓
string
sedangkan constructor meminta:
"credit" | "debit"
37. Kenapa TypeScript Tidak Tahu Value-nya?
Karena TypeScript hanya tahu bahwa:
select.value
adalah string.
Dia tidak bisa otomatis menjamin:
string ini pasti "credit" atau "debit"
Dari sudut pandang TypeScript:
string
bisa saja:
"credit"
"debit"
"hello"
"banana"
"something"
Jadi TypeScript menjaga kita supaya tidak memasukkan sembarang string ke:
"credit" | "debit"
38. Solusinya: Kita Periksa Value
Instructor membuat variable:
let type: "credit" | "debit";
Kemudian mengecek:
if (expType.value === "credit") {
type = "credit";
} else {
type = "debit";
}
Sekarang kita memastikan bahwa variable type hanya mempunyai:
credit
atau:
debit
39. Cara Membaca Logic Ini
let type: "credit" | "debit";
Artinya:
typehanya boleh credit atau debit.
Kemudian:
if (expType.value === "credit")
Artinya:
Kalau user memilih credit…
maka:
type = "credit";
Kalau bukan credit:
type = "debit";
Jadi akhirnya:
UI value
↓
string
↓
diperiksa
↓
"credit" | "debit"
↓
masuk constructor
40. Ini Sekaligus Contoh Type Narrowing
Kita sebelumnya belajar tentang Type Guard / Type Narrowing.
Di sini:
if (expType.value === "credit")
kita sedang melakukan pengecekan terhadap value.
Kemudian kita menghasilkan value yang sesuai dengan literal union:
"credit" | "debit"
Ini adalah contoh nyata dari type safety yang sebelumnya kita pelajari secara teori.
41. Mengambil Description
Kita punya:
const expenseDescription =
document.getElementById("description")!
as HTMLInputElement;
Maka:
expenseDescription.value
menghasilkan isi input.
Misalnya user memasukkan:
Groceries
maka:
expenseDescription.value
menghasilkan:
"Groceries"
Type-nya:
string
Dan constructor memang membutuhkan:
description: string
Jadi cocok.
42. Mengambil Amount — Ada Masalah Lagi
Kita punya:
const expenseAmount =
document.getElementById("amount")!
as HTMLInputElement;
Kemudian:
expenseAmount.value
Misalnya user mengetik:
230
Kita mungkin berpikir:
230 → number
Tapi sebenarnya browser memberikan:
"230"
yaitu:
string
43. Kenapa Input Number Tetap String?
Ini konsep penting dalam DOM.
Walaupun HTML:
<input type="number">
value dari input tetap direpresentasikan sebagai string melalui property .value.
Jadi:
<input type="number" value="230">
ketika diambil:
input.value
hasilnya:
"230"
bukan:
230
44. Padahal Constructor Meminta Number
Class kita memiliki:
amount: number
dan constructor:
constructor(
type: "credit" | "debit",
description: string,
amount: number
)
Tetapi kita memberikan:
expenseAmount.value
yang bertipe:
string
Maka TypeScript akan berkata:
string tidak bisa digunakan sebagai number
45. Mengubah String Menjadi Number
Instructor menggunakan unary plus:
+expenseAmount.value
Misalnya:
expenseAmount.value
adalah:
"230"
maka:
+"230"
menjadi:
230
yang merupakan number.
46. Unary Plus +
Ini salah satu syntax JavaScript yang sangat berguna.
const value = +"100";
hasil:
100
Type:
number
Contoh:
+"10" // 10
+"500" // 500
+"3.14" // 3.14
47. Tapi Hati-Hati dengan Unary Plus
Kalau string tidak berisi angka:
+"hello"
hasilnya:
NaN
NaN = Not a Number.
Jadi pada aplikasi nyata kita sebaiknya juga melakukan validasi input.
Misalnya:
const amount = Number(expenseAmount.value);
if (Number.isNaN(amount)) {
// invalid amount
}
Tetapi untuk lecture ini, instructor menggunakan:
+expenseAmount.value
untuk menunjukkan konversi string → number.
48. Akhirnya Data dari UI Bisa Digunakan
Sekarang kita punya:
const type: "credit" | "debit" = ...
expenseDescription.value
dan:
+expenseAmount.value
Kemudian:
const expense = new Expense(
type,
expenseDescription.value,
+expenseAmount.value
);
Flow-nya:
<select>
│
└── .value
↓
"credit"/"debit"
↓
type
<input description>
│
└── .value
↓
"Groceries"
<input amount>
│
└── .value
↓
"230"
↓
+230
↓
230
Kemudian:
┌───────────────┐
type ──────────►│ │
description ──► │ new Expense() │
amount ────────►│ │
└───────┬───────┘
↓
Expense object
49. Hasil Akhir
Misalnya user memasukkan:
Type: Debit
Description: Groceries
Amount: 230
maka object:
{
id: 1,
type: "debit",
description: "Groceries",
amount: 230
}
Kalau user kemudian memasukkan:
Type: Credit
Description: YouTube Earnings
Amount: 5500
object berikutnya:
{
id: 2,
type: "credit",
description: "YouTube Earnings",
amount: 5500
}
50. Gambaran Lengkap Kode
Secara konsep, kode yang dipelajari di lecture ini kira-kira seperti:
const expType =
document.getElementById("expense-type")!
as HTMLSelectElement;
const expenseDescription =
document.getElementById("description")!
as HTMLInputElement;
const expenseAmount =
document.getElementById("amount")!
as HTMLInputElement;
const addExpenseButton =
document.querySelector(".add-expense-button")!
as HTMLButtonElement;
class Expense {
id: number;
static currentId: number = 0;
type: "credit" | "debit" = "debit";
description: string = "";
amount: number = 0;
constructor(
type: "credit" | "debit",
description: string,
amount: number
) {
this.id = ++Expense.currentId;
this.type = type;
this.description = description;
this.amount = amount;
}
}
addExpenseButton.addEventListener("click", (event) => {
event.preventDefault();
let type: "credit" | "debit";
if (expType.value === "credit") {
type = "credit";
} else {
type = "debit";
}
const expense = new Expense(
type,
expenseDescription.value,
+expenseAmount.value
);
console.log(expense);
});
Jangan khawatir kalau terlihat banyak. Kita bisa memecahnya menjadi 4 bagian besar.
51. Bagian 1 — Ambil Element HTML
const expType = ...
const expenseDescription = ...
const expenseAmount = ...
const addExpenseButton = ...
Tujuannya:
Menghubungkan TypeScript dengan HTML.
52. Bagian 2 — Membuat Class
class Expense {
...
}
Tujuannya:
Menentukan struktur sebuah expense.
Expense memiliki:
id
type
description
amount
53. Bagian 3 — Mendengarkan Click
addExpenseButton.addEventListener("click", ...)
Tujuannya:
Menjalankan logic ketika user klik Add Expense.
54. Bagian 4 — Membaca Data dan Membuat Object
const expense = new Expense(
type,
expenseDescription.value,
+expenseAmount.value
);
Tujuannya:
Mengambil data dari UI lalu mengubahnya menjadi object
Expense.
55. Kenapa TypeScript Sangat Berguna di Sini?
Ini inti pesan instructor di akhir lecture.
Bayangkan kita menggunakan JavaScript biasa.
Kita bisa saja melakukan:
const expense = new Expense(
expType.value,
expenseDescription.value,
expenseAmount.value
);
Masalahnya:
expType.value
↓
string
expenseAmount.value
↓
string
Padahal kita membutuhkan:
type
↓
"credit" | "debit"
amount
↓
number
TypeScript akan memberi tahu kita sebelum aplikasi berjalan.
56. Compile-Time Error
Misalnya:
function createExpense(amount: number) {
...
}
tetapi kita memberikan:
createExpense("230");
TypeScript langsung berkata:
Argument of type 'string'
is not assignable to parameter of type 'number'
Artinya:
“Kamu memberikan string, padahal function meminta number.”
Ini terjadi saat development/compile time.
57. TypeScript Seperti Satpam
Analoginya begini.
Tanpa TypeScript:
Developer
↓
Masukkan data apa saja
↓
JavaScript
↓
Mungkin error saat runtime
Dengan TypeScript:
Developer
↓
Masukkan data
↓
TypeScript
↓
"Eh, ini salah tipe!"
↓
Perbaiki dulu
↓
Compile
↓
Run
Jadi TypeScript bertindak seperti satpam sebelum aplikasi dijalankan.
58. Hubungan Semua Konsep yang Sudah Dipelajari
Lecture ini sebenarnya menggabungkan banyak materi sebelumnya.
Type Assertion
as HTMLInputElement
Untuk memberitahu TypeScript jenis DOM element.
Non-null Assertion
!
Untuk mengatakan element tersebut tidak null.
Literal + Union Type
"credit" | "debit"
Untuk membatasi expense type.
Class
class Expense
Untuk membuat blueprint object Expense.
Static Property
static currentId
Untuk counter ID bersama.
Constructor
constructor(...)
Untuk menginisialisasi object.
DOM
document.getElementById(...)
Untuk mengambil element HTML.
Event Listener
addEventListener("click", ...)
Untuk merespons aksi user.
preventDefault()
event.preventDefault()
Untuk mencegah form melakukan submit/reload.
Type Conversion
+expenseAmount.value
Untuk mengubah string menjadi number.
59. Satu Hal yang Sangat Penting: value dari DOM
Ini wajib kamu ingat.
Ketika mengambil:
input.value
anggap hasilnya:
string
meskipun HTML-nya:
<input type="number">
Jadi:
const amount = input.value;
hasilnya:
"230"
bukan:
230
Kalau membutuhkan number:
const amount = +input.value;
atau:
const amount = Number(input.value);
60. Perbedaan !, as, dan +
Karena ketiganya muncul dalam lecture ini, jangan sampai tertukar.
| Syntax | Fungsi |
|---|---|
! | Memberitahu TS bahwa value tidak null/undefined |
as HTMLInputElement | Memberitahu TS tipe value |
+value | Mengonversi string menjadi number |
Contoh:
const amount =
document.getElementById("amount")!
as HTMLInputElement;
Kemudian:
+amount.value
Urutannya:
getElementById()
↓
mungkin null
↓
!
"pasti ada"
↓
as HTMLInputElement
"pasti input"
↓
.value
"230"
↓
+
230
Ini salah satu bagian terpenting dari lecture.
61. Flow Keseluruhan Project Saat Ini
Sekarang aplikasi kita sudah mempunyai flow:
USER
│
▼
Isi Form
│
┌──────────┼───────────┐
▼ ▼ ▼
Type Description Amount
│ │ │
└──────────┼───────────┘
▼
Klik Add Expense
│
▼
Event Listener
│
▼
preventDefault()
│
▼
Baca form value
│
▼
Validasi / tentukan type
│
▼
Convert amount
│
▼
new Expense(...)
│
▼
Expense Object
│
▼
console.log()
Lecture berikutnya baru kemungkinan akan membawa object tersebut ke:
Expense Object
↓
Dynamic HTML
↓
Expense List di UI
62. Hal yang Perlu Kamu Pahami Sebelum Lanjut
Kalau kamu ingin benar-benar memahami lecture ini, fokus pada 7 hal berikut:
1. DOM element
document.getElementById(...)
Artinya mengambil element HTML.
2. Type Assertion
as HTMLInputElement
Artinya memberi tahu TypeScript jenis element yang kita yakini.
3. Non-null assertion
!
Artinya kita yakin element tersebut tidak null.
4. Class
class Expense
Artinya blueprint untuk membuat object expense.
5. Static ID
static currentId = 0;
Artinya counter ID dimiliki bersama oleh semua instance.
6. Event Listener
addEventListener("click", ...)
Artinya jalankan kode ketika user melakukan click.
7. Data dari HTML adalah string
input.value
biasanya menghasilkan string, sehingga:
+input.value
digunakan untuk mendapatkan number.
63. Cheat Sheet
document.getElementById()
↓
ambil element berdasarkan ID
document.querySelector()
↓
ambil element berdasarkan CSS selector
!
↓
saya yakin bukan null
as HTMLInputElement
↓
saya yakin ini input
as HTMLSelectElement
↓
saya yakin ini select
as HTMLButtonElement
↓
saya yakin ini button
input.value
↓
ambil nilai input → string
+input.value
↓
string → number
"credit" | "debit"
↓
hanya boleh salah satu
class Expense
↓
blueprint object Expense
static currentId
↓
counter yang dibagikan semua instance
constructor(...)
↓
inisialisasi object
addEventListener("click", ...)
↓
jalankan kode saat click
event.preventDefault()
↓
cegah default action form
🎯 Inti Lecture dalam Satu Kalimat
Kita mengambil data dari HTML menggunakan DOM, memberi tahu TypeScript tipe element tersebut dengan type assertion, menangani klik menggunakan event listener, mencegah form melakukan reload dengan
preventDefault(), lalu mengubah data form menjadi objectExpensemenggunakan class.
Dan ada satu pelajaran TypeScript yang sangat penting dari lecture ini:
Data yang terlihat benar di UI belum tentu memiliki type yang benar menurut TypeScript. Contohnya input amount terlihat seperti angka
230, tetapiinput.valuesebenarnya adalahstring"230". TypeScript memaksa kita menyadari perbedaan tersebut dan memperbaikinya sebelum menjadi bug.