Masukkan Password
74 Membuat Dekorator Lain
Lecture ini mulai masuk ke contoh nyata penggunaan Decorator, khususnya bagaimana decorator bisa digunakan untuk memanipulasi DOM. Ini penting karena di sini kita tidak hanya belajar syntax decorator, tetapi mulai melihat kenapa decorator berguna dalam aplikasi nyata.
Saya akan jelaskan dari konsep dasarnya sampai alur eksekusinya.
1. Gambaran besar lecture
Di lecture sebelumnya kita sudah belajar:
Decorator
↓
Decorator Factory
Sekarang konsep tersebut dipakai untuk membuat decorator yang bisa:
- menerima template HTML
- menerima ID elemen DOM
- mencari elemen tersebut di HTML
- memasukkan template ke dalam elemen
- mencari
<h2> - mengisi
<h2>dengan nama user
Jadi secara sederhana:
Decorator Factory
↓
terima template + element ID
↓
cari element di DOM
↓
masukkan HTML
↓
ambil data dari User
↓
ubah isi <h2>
Lecture ini menggunakan decorator factory untuk membuat semacam template rendering sederhana.
2. Kita mulai dari Decorator Factory
Lecture membuat function:
function template(template: string, elementId: string) {
return function(target: any) {
// decorator logic
};
}
Perhatikan bahwa template() adalah Decorator Factory.
Kenapa?
Karena function tersebut:
template(...)
mengembalikan function:
function(target) {
// ...
}
Jadi:
template()
↓
return function(target)
↓
Decorator
3. Kenapa ada dua parameter?
Decorator factory ini menerima:
template: string
elementId: string
Jadi kita bisa memberikan:
Parameter pertama
template
Misalnya:
<h2>Dynamic Header</h2>
Parameter kedua
elementId
Misalnya:
container
Sehingga penggunaannya bisa seperti:
@template(
"<h2>Dynamic Header</h2>",
"container"
)
class User {
}
Artinya:
“Gunakan template HTML ini dan masukkan ke element yang memiliki ID
container.”
4. Kita lihat HTML-nya
Di index.html, lecture menambahkan:
<div id="container"></div>
Jadi DOM kita kira-kira:
HTML
│
└── body
│
└── div#container
id="container" sangat penting karena nanti TypeScript akan mencari element ini menggunakan:
document.getElementById(...)
5. Apa itu document.getElementById()?
Kode:
const container = document.getElementById(elementId);
artinya:
Cari element HTML yang memiliki ID sesuai dengan
elementId.
Misalnya:
elementId = "container"
maka kode tersebut secara efektif mencari:
<div id="container"></div>
Hasilnya adalah reference ke element tersebut.
Secara visual:
document
│
└── getElementById("container")
↓
<div id="container">
</div>
6. Kenapa sebelumnya memakai !?
Lecture awalnya menggunakan:
const container = document.getElementById(elementId)!;
Tanda:
!
adalah non-null assertion.
Kita sudah pernah membahas ini.
TypeScript tahu bahwa:
document.getElementById(...)
bisa menghasilkan:
HTMLElement | null
Karena mungkin saja ID tersebut tidak ditemukan.
Contohnya:
const container = document.getElementById("container");
TypeScript berpikir:
container:
HTMLElement | null
Tetapi dengan:
!
kita mengatakan kepada TypeScript:
“Saya yakin element ini ada.”
Sehingga:
document.getElementById(elementId)!
dianggap bukan null.
7. Tapi lecture kemudian menghapus !
Ini keputusan yang lebih aman.
Karena sekarang:
elementId
berasal dari parameter.
Artinya kita tidak tahu apakah user memberikan ID yang benar atau tidak.
Misalnya:
@template(
"<h2>Hello</h2>",
"container"
)
Ada container → aman.
Tetapi bisa saja:
@template(
"<h2>Hello</h2>",
"something"
)
Padahal:
<div id="something"></div>
tidak ada.
Maka:
document.getElementById("something")
bisa menghasilkan:
null
Karena itu lecture melakukan pengecekan:
if (container) {
// ...
}
8. if (container) maksudnya apa?
Ini adalah pengecekan truthy/falsy.
if (container) {
}
Artinya:
Kalau
containerberhasil ditemukan, jalankan kode di dalamnya.
Kalau:
container === null
maka kondisi:
if (container)
bernilai false.
Kalau berhasil mendapatkan DOM element:
container !== null
maka kondisi bernilai true.
Visual:
getElementById(elementId)
│
▼
┌───────────┐
│ ditemukan?│
└─────┬─────┘
│
┌───┴───┐
│ │
YES NO
│ │
▼ ▼
container null
│
▼
if(container)
│
▼
jalankan
9. Memasukkan template ke DOM
Setelah mendapatkan container:
if (container) {
container.innerHTML = template;
}
Ini bagian yang sangat penting.
Misalnya:
template = "<p>This is dynamic paragraph</p>";
Maka:
container.innerHTML = template;
akan menghasilkan:
<div id="container">
<p>This is dynamic paragraph</p>
</div>
Jadi decorator sekarang bisa memasukkan HTML secara dynamic.
10. Kenapa namanya template?
Karena kita memberikan HTML sebagai sebuah template/string.
Misalnya:
const html = `
<h2>Dynamic Header</h2>
`;
html tersebut bukan element DOM.
Dia masih berupa:
string
Contohnya:
"<h2>Dynamic Header</h2>"
Kemudian:
container.innerHTML = template;
mengubah string HTML tersebut menjadi isi DOM.
Jadi:
String HTML
↓
innerHTML
↓
DOM
11. Contoh sederhana dari lecture
Misalnya kita menggunakan:
@template(
"<p>This is dynamic paragraph</p>",
"container"
)
class User {
}
Maka decorator factory mendapatkan:
template = "<p>This is dynamic paragraph</p>"
elementId = "container"
Kemudian:
document.getElementById("container")
mencari:
<div id="container"></div>
Setelah ditemukan:
container.innerHTML = template;
Hasilnya:
<div id="container">
<p>This is dynamic paragraph</p>
</div>
12. Bisa juga menggunakan button
Lecture kemudian mencoba:
<button>Dynamic Button</button>
Maka:
@template(
"<button>Dynamic Button</button>",
"container"
)
menghasilkan:
<div id="container">
<button>Dynamic Button</button>
</div>
Artinya decorator tersebut tidak peduli template-nya apa.
Bisa:
<p>...</p>
atau:
<button>...</button>
atau:
<h2>...</h2>
Selama diberikan sebagai string HTML.
13. Tapi ada tujuan lain
Nah, lecture tidak berhenti pada:
container.innerHTML = template;
Lecture ingin mengambil:
<h2>
yang ada di dalam template.
Kenapa?
Karena kita ingin memasukkan nama user ke dalam <h2>.
Misalnya template:
<h2>Dynamic Header</h2>
ingin diubah menjadi:
<h2>Hello Mr John</h2>
14. Mengambil <h2> dengan querySelector
Kode yang digunakan:
const h2 = container.querySelector("h2");
Artinya:
Cari element
<h2>di dalamcontainer.
Misalnya DOM:
<div id="container">
<h2>Dynamic Header</h2>
</div>
Maka:
container.querySelector("h2")
mengambil:
<h2>Dynamic Header</h2>
15. Kenapa menggunakan querySelector("h2")?
Karena querySelector() memungkinkan kita mencari element berdasarkan CSS selector.
Contoh:
container.querySelector("h2")
→ cari <h2>
container.querySelector(".title")
→ cari element dengan class title
container.querySelector("#header")
→ cari element dengan ID header
Dalam lecture ini yang dicari adalah:
"h2"
16. Lagi-lagi kita harus mengecek null
querySelector() juga bisa gagal menemukan element.
Misalnya:
<div id="container">
<p>Hello</p>
</div>
Tidak ada <h2>.
Maka:
container.querySelector("h2")
menghasilkan:
null
Karena itu lecture melakukan:
if (h2) {
// ...
}
Artinya:
Kalau
<h2>ditemukan, lakukan sesuatu terhadapnya.
17. Mengubah text dengan textContent
Setelah <h2> ditemukan:
h2.textContent = "Hello Mr " + u.name;
Misalnya:
u.name = "John";
maka hasilnya:
<h2>Hello Mr John</h2>
Jadi alurnya:
User
│
└── name = "John"
│
▼
"Hello Mr " + u.name
│
▼
"Hello Mr John"
│
▼
h2.textContent
18. Dari mana u.name berasal?
Ini bagian yang sangat penting untuk memahami hubungan decorator dengan class.
Lecture memiliki target.
target merupakan reference ke class yang diberi decorator.
Kemudian lecture melakukan:
const u = new target();
Artinya:
Buat instance dari class yang sedang diberi decorator.
Misalnya:
class User {
name = "John";
}
dan:
target
merepresentasikan User.
Maka:
new target()
secara konsep sama seperti:
new User()
Hasilnya:
u
↓
User instance
↓
name = "John"
19. Kenapa target diberi tipe any di lecture?
Sebelumnya lecture menggunakan:
target: Function
Tetapi kemudian ingin melakukan:
new target()
dan mengakses hasil instance.
Dalam contoh lecture ini, tipe tersebut menyebabkan masalah TypeScript, sehingga sementara digunakan:
target: any
Dengan:
any
TypeScript menjadi lebih longgar dan kode:
const u = new target();
bisa dilakukan.
Catatan penting: lecture memang menggunakan any untuk contoh sederhana ini; ini bukan berarti any selalu merupakan pilihan terbaik dalam kode production.
20. Sekarang gabungkan semuanya
Secara konsep decorator-nya menjadi seperti:
function template(template: string, elementId: string) {
return function(target: any) {
const u = new target();
const container =
document.getElementById(elementId);
if (container) {
container.innerHTML = template;
const h2 = container.querySelector("h2");
if (h2) {
h2.textContent = "Hello Mr " + u.name;
}
}
};
}
Kemudian:
@template(
"<h2>Dynamic Header</h2>",
"container"
)
class User {
name = "John";
constructor() {
console.log("User class Constructor called");
}
}
21. Mari jalankan langkah demi langkah
Ini bagian terpenting.
Kita punya:
@template(
"<h2>Dynamic Header</h2>",
"container"
)
class User {
name = "John";
}
Step 1 — Factory dipanggil
template(
"<h2>Dynamic Header</h2>",
"container"
)
Maka:
template =
"<h2>Dynamic Header</h2>"
elementId =
"container"
Step 2 — Factory menghasilkan decorator
Factory mengembalikan:
function(target) {
// ...
}
Step 3 — Decorator mendapatkan User
Secara konsep:
target → User
Step 4 — Buat instance
const u = new target();
Secara konsep:
const u = new User();
Sekarang:
u.name
↓
"John"
Step 5 — Cari container
document.getElementById("container")
Mendapat:
<div id="container"></div>
Step 6 — Masukkan template
container.innerHTML =
"<h2>Dynamic Header</h2>";
DOM menjadi:
<div id="container">
<h2>Dynamic Header</h2>
</div>
Step 7 — Cari h2
container.querySelector("h2")
Mendapat:
<h2>Dynamic Header</h2>
Step 8 — Ambil nama User
u.name
hasil:
John
Step 9 — Ubah text
h2.textContent = "Hello Mr " + u.name;
hasil:
<h2>Hello Mr John</h2>
22. Flow keseluruhan
Kalau digambarkan:
@template(
"<h2>Dynamic Header</h2>",
"container"
)
│
▼
template(...)
│
▼
return decorator
│
▼
target
│
▼
new target()
│
▼
User instance
│
▼
u.name
│
▼
"John"
elementId
│
▼
getElementById()
│
▼
<div id="container">
│
▼
innerHTML = template
│
▼
<h2>Dynamic Header</h2>
│
▼
querySelector("h2")
│
▼
<h2>
│
▼
textContent = "Hello Mr John"
23. Jadi decorator ini sebenarnya melakukan apa?
Decorator ini sebenarnya seperti sebuah mini rendering system.
Kita hanya menulis:
@template(
"<h2>Dynamic Header</h2>",
"container"
)
class User {
name = "John";
}
Tetapi di belakang layar decorator melakukan:
1. Buat User instance
2. Cari container
3. Masukkan template HTML
4. Cari h2
5. Ambil User.name
6. Masukkan nama ke h2
Jadi class User tidak perlu secara langsung mengurus DOM tersebut.
Itulah salah satu ide besar di balik decorator:
memisahkan behavior tertentu dari kode utama.
24. Hubungannya dengan Angular
Di akhir lecture, instructor menghubungkannya dengan Angular.
Kalau kamu pernah melihat Angular, kamu mungkin pernah melihat:
@Component({
selector: 'app-user',
template: '...',
styles: [...]
})
export class UserComponent {
}
Secara konsep, ini memiliki pola yang mirip:
@Component(...)
↓
Decorator Factory
↓
Component class
Parameter seperti:
selector
template
styles
adalah konfigurasi.
Jadi konsep yang baru kita buat:
@template(
"...",
"container"
)
class User {}
memiliki ide yang mirip:
Decorator
+
Configuration
+
Class
Lecture memang menyebut contoh ini sebagai versi yang jauh lebih sederhana dibanding decorator @Component di Angular.
25. Kenapa ini disebut “practical use”?
Sebelumnya kita hanya melakukan:
function logger(target: Function) {
console.log("logging");
}
Itu bagus untuk memahami mekanismenya, tetapi belum terlalu berguna.
Sekarang decorator melakukan sesuatu yang benar-benar berhubungan dengan aplikasi:
Decorator
↓
DOM manipulation
↓
HTML berubah
↓
Data User ditampilkan
Jadi kita mulai melihat potensi sebenarnya.
Decorator bisa digunakan untuk melakukan berbagai hal seperti:
logging
validation
authorization
metadata
registration
configuration
DOM manipulation
framework behavior
Lecture ini secara spesifik mendemonstrasikan DOM manipulation.
26. Hal yang paling penting: Factory vs Decorator
Ini sering membingungkan.
Dalam:
function template(template: string, elementId: string) {
return function(target: any) {
// ...
};
}
template
adalah:
Decorator Factory
karena menerima configuration dan mengembalikan decorator.
function(target)
adalah:
Decorator
karena function inilah yang akhirnya diterapkan kepada class.
Jadi:
template()
│
│ factory
▼
function(target)
│
│ decorator
▼
User
27. Jangan salah memahami template
Ada dua hal yang sama-sama bernama template dalam contoh.
Misalnya:
function template(template: string, elementId: string)
Yang pertama:
template
adalah nama function → factory.
Yang kedua:
template: string
adalah parameter → HTML string yang ingin dimasukkan.
Jadi kalau agak membingungkan, secara mental bisa kita ubah namanya menjadi:
function templateFactory(
htmlTemplate: string,
elementId: string
) {
return function(target: any) {
// ...
};
}
Konsepnya menjadi jauh lebih jelas:
templateFactory()
│
├── htmlTemplate
└── elementId
28. Kenapa innerHTML dan textContent berbeda?
Lecture menggunakan keduanya, dan penting untuk membedakannya.
innerHTML
container.innerHTML = template;
Digunakan untuk memasukkan HTML.
Misalnya:
container.innerHTML =
"<h2>Hello</h2>";
Browser membuat:
<h2>Hello</h2>
textContent
h2.textContent = "Hello Mr John";
Digunakan untuk mengubah teks dari element.
Hasil:
<h2>Hello Mr John</h2>
Jadi:
innerHTML
↓
HTML content
textContent
↓
Text content
29. Satu hal yang harus kamu perhatikan
Kode lecture:
container.innerHTML = template;
kemudian:
container.querySelector("h2");
berarti decorator mengasumsikan bahwa template yang diberikan memiliki <h2>.
Kalau kita memberikan:
@template(
"<p>Hello</p>",
"container"
)
maka:
container.querySelector("h2")
akan menghasilkan:
null
dan bagian:
if (h2) {
...
}
tidak akan dijalankan.
Jadi decorator ini memiliki asumsi:
template
↓
harus memiliki h2
Ini adalah contoh sederhana untuk memahami konsep, bukan template engine yang lengkap.
30. Kesimpulan besar
Lecture ini menggabungkan semua yang sudah dipelajari sebelumnya:
Decorator
+
Decorator Factory
+
Class
+
DOM
+
TypeScript
Kita membuat:
@template(
"<h2>Dynamic Header</h2>",
"container"
)
class User {
name = "John";
}
Kemudian decorator:
1. menerima template
2. menerima element ID
3. mendapatkan target class
4. membuat instance User
5. mencari element DOM
6. memasukkan template
7. mencari h2
8. mengambil user.name
9. mengubah h2.textContent
Sehingga:
SEBELUM
<div id="container"></div>
↓ decorator
SESUDAH
<div id="container">
<h2>Hello Mr John</h2>
</div>
🧠 Cheat Sheet
| Konsep | Fungsi |
|---|---|
@template(...) | Menggunakan decorator factory |
template(...) | Decorator factory |
return function(target) | Decorator yang dihasilkan factory |
template parameter | HTML template string |
elementId | ID element DOM target |
document.getElementById() | Mencari element berdasarkan ID |
if (container) | Mengecek element ditemukan |
innerHTML | Memasukkan HTML |
querySelector("h2") | Mencari <h2> |
if (h2) | Mengecek <h2> ditemukan |
new target() | Membuat instance dari class target |
u.name | Mengambil nama dari instance User |
textContent | Mengubah teks element |
Dan mental model utama untuk lecture ini adalah:
@template(...)
│
▼
Decorator Factory
│
┌─────────┴─────────┐
│ │
HTML template Element ID
│ │
└─────────┬─────────┘
▼
Decorator
│
▼
target = User
│
▼
new User()
│
▼
User.name
│
▼
Manipulate DOM
Jadi inti lecture ini bukan sekadar “cara mengubah HTML dengan decorator”, tetapi memperlihatkan bagaimana Decorator Factory bisa menerima konfigurasi, kemudian decorator yang dihasilkan menggunakan konfigurasi tersebut untuk melakukan suatu behavior pada class.