<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Fiska Syela on Medium]]></title>
        <description><![CDATA[Stories by Fiska Syela on Medium]]></description>
        <link>https://medium.com/@fiskasyela?source=rss-34bc4b2429f2------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*eQMqZdWWR0AGeAEJuzfyww.jpeg</url>
            <title>Stories by Fiska Syela on Medium</title>
            <link>https://medium.com/@fiskasyela?source=rss-34bc4b2429f2------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 03 Jun 2026 00:41:11 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@fiskasyela/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Yang Aku Dapet dari Ngerjain Course di LinkedIn Daripada Scrolling TikTok]]></title>
            <link>https://medium.com/@fiskasyela/yang-aku-dapet-dari-ngerjain-course-di-linkedin-daripada-scrolling-tiktok-40d52a4170b7?source=rss-34bc4b2429f2------2</link>
            <guid isPermaLink="false">https://medium.com/p/40d52a4170b7</guid>
            <category><![CDATA[eslint]]></category>
            <category><![CDATA[data]]></category>
            <category><![CDATA[babel]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[data-management]]></category>
            <dc:creator><![CDATA[Fiska Syela]]></dc:creator>
            <pubDate>Tue, 24 Jun 2025 04:17:03 GMT</pubDate>
            <atom:updated>2025-06-24T04:17:03.928Z</atom:updated>
            <content:encoded><![CDATA[<p>Scrolling medsos itu gak ada salahnya. Malahan karena capek dari kerjaan <strong>utama</strong>, scroll TikTok seharian 🤞🏻, akhirnya aku memutuskan untuk menamatkan Linkedin Learning.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OH98WRZrFuahigO6GJ2S-g.png" /></figure><p>Kali ini aku nyobain course <a href="https://www.linkedin.com/learning/javascript-best-practices-for-data/working-effectively-with-javascript-data-25664843?u=117604354"><em>JavaScript: Best Practices for Data </em>dari<em> Shasha Vodnik</em></a><em>.</em> Tapi rasanya gak afdol kalau cuma share sertifikatnya doang. Mana bukti belajarnya, kak? Jadi yaudah lah kita bahas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*504W0caNV0QxhRxEE4Rtyw.png" /><figcaption>@shakedatshi on TikTok</figcaption></figure><p>Quick review aja, menurutku course dari Mas Shasha Vodnik ini <em>worth it</em> banget buat kalian <em>enroll</em>. Videonya singkat, penjelasannya compact tapi cukup nge-cover poin-poin penting. Solid 4/5⭐️</p><p>Sama kayak di course-nya, nanti aku mau bahas gimana cara <strong>nyusun data</strong> di JavaScript biar <strong>lebih rapi</strong>, <strong>gak</strong> <strong>cluttered</strong>, dan <strong>efisien</strong>. BTW, Karena pembahasannya lumayan panjang, aku bakal pisahin jadi 3 artikel biar kalian gak bosen. I know your attention span is already fucked. Kira-kira pembahasannya bakal kayak gini:</p><ul><li>Perkenalan soal <em>best practice</em></li><li>Cara deklarasi dan assign variabel</li><li>Type casting dan perbandingan value</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*61TWgbKgDfM-K5rj1uyXIw.png" /></figure><p>Gila, banyak banget yaa? But I promise the ride will be fun for you guys. And I’ll spill some tea along the way too, soo 🍵 🍵</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/736/0*dw1MLAzbUPQtAirk.jpg" /></figure><h3>Introduction: Why Best Practices?</h3><h4>Style Guide</h4><p>Pas lagi ngoding pake JavaScript, apalagi kalau udah urusan data, pasti banyak banget pilihan mau nulis kode kayak gimana. Terus gimana sih cara nentuin mana style yang paling sesuai? Langkah pertama adalah menentukan <strong>style guide</strong>.</p><p><strong>Style guide</strong> itu kaya kesepakatan soal cara nulis kode di dalam satu codebase. Ini penting banget, soalnya setiap orang (bahkan setiap bahasa pemrograman) punya banyak gaya penulisannya sendiri-sendiri.</p><p>Style guide ini bisa macem-macem levelnya bisa di level organisasi, proyek, bahkan spesifik di satu repo aja. Tapi biasanya di industri udah banyak style guide yang sering dipake dan dianggap sebagai <strong>best practice.</strong></p><p>Meskipun namanya best practice, bukan berarti ada satu style guide untuk semua kasus. Kita tetap harus lihat kebutuhan dan tujuan proyeknya. Fungsi utama style guide itu ada dua:</p><p>✅ Biar kode kita tetap nyambung dan <em>cohesive</em> sama bagian lain di codebase.<br>✅ Biar kita bisa fokus ke logic dan fungsionalitasnya aja, tanpa harus pusing mikirin mau pakai style/syntax yang gimana.</p><p>Kalau mau cari contoh best practice populer, biasanya orang-orang referensinya ke style guide kayak:<br>→ <a href="https://github.com/airbnb/javascript">Airbnb JavaScript Style Guide</a><br>→ <a href="https://google.github.io/styleguide/">Google JavaScript Style Guide</a></p><p>Waktu awal-awal aku kerja, gak ada style guide buat standarisasi kode ini. Satu proyek bisa dikerjain 5–10 orang yang punya style masing-masing. Awalnya sih bukan masalah ya, karena emang satu orang fokus ngerjain satu fitur. Tapi waktu bug fixing dan diminta ngelanjutin fiturnya, ya Allah jujur pusing sih. Gak ada standarisasi, gak tau alur kerjanya gimana, bahkan gak ada komen? GWS.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/0*dZcKNkqAGVuTALqa.jpg" /></figure><p>Akhirnya waktu bug fixing, harus melajarin dulu ini maksud programmer yang kemarin itu apaan. Pas masuk proyek baru, aku come up buat bikin standarisasi mulai dari foldering, terus penulisan function, variable, dan sebagainya. Mungkin lain kali aku bikin pembahasan yang lebih nyeluruh dan thought process waktu itu.</p><p>Jadi, menurutku setiap project yang dikerjain lebih dari 2 orang itu wajib banget punya style guide. Selain biar lebih gampang kerja bareng, juga biar gak pusing sendiri kalau harus ngelanjutin kerjaan orang lain.</p><h4>Babel</h4><p>Selain style guide, kalian juga harus integrate kode kalian sama Babel. Babel itu JavaScript transpiler, fungsinya buat mengubah kode JavaScript versi baru (ES6+, ES2020, dst.) jadi versi biar bisa kompatibel di semua browser.</p><p>JavaScript kan berkembang pesat, sedangkan browser kadang ketinggalan fitur baru. Makanya kita perlu transpiler kayak Babel. Selain itu, ada juga polyfills potongan kode untuk mimics fitur baru JavaScript (misalnya Promise atau fetch) agar tetap bisa jalan di browser lama.</p><p>Gimana cara kerjanya?</p><ul><li>Kita menulis kode pakai fitur modern JS (chaining, async/await, arrow function, dll.</li></ul><pre>const user = { profile: { name: &quot;Andi&quot; } };<br>const username = user?.profile?.name ?? &quot;Guest&quot;;<br><br>const loadData = async () =&gt; {<br>  const res = await fetch(&quot;/data.json&quot;);<br>  return res.json();<br>};</pre><ul><li>Babel parses kodenya menjadi Abstract Syntax Tree (AST).</li><li>Babel plugins mentransform AST ke sintaks lama agar lebih kompatibel</li></ul><pre>var user = { profile: { name: &quot;Andi&quot; } };<br>var username = (user === null || user === void 0 ? void 0 : user.profile) === null || user.profile === void 0 ? void 0 : user.profile.name || &quot;Guest&quot;;<br><br>var loadData = function loadData() {<br>  return regeneratorRuntime.async(function loadData$() {<br>    var res;<br>    return regeneratorRuntime.awrap(fetch(&quot;/data.json&quot;)).then(function (res) {<br>      return res.json();<br>    });<br>  });<br>};</pre><ul><li>Polyfills seperti core-js bakal nambahin fitur baru kalau browser lama gak punya.</li></ul><p>Sebenernya kalau kalian udah pake framework modern kaya React, Vue.js atau Angular mereka mostly udah ada built in transpiler jadi kalian gak perlu ngesetup lagi.</p><p>✅<a href="https://create-react-app.dev/docs/getting-started/#get-started-immediately">React</a>/<a href="https://cli.vuejs.org/core-plugins/babel.html">Vue</a>/Svelte: Babel sudah di setup otomatis.<br>✅<a href="https://angular.dev/reference/versions#browser-support">Angular</a>: gak pakai Babel secara eksplisit, tapi pake tsc dan polyfills.ts untuk kompatibilitas browser.</p><h4>ESLint</h4><p>Waktu awal-awal kerja (lagi), setiap kali code review rasanya udah males duluan karena banyak banget PR noise. PR noise ini maksudnya file dan baris kode yang berubah, padahal gak ada logic yang beneran diubah cuma karena formatternya beda-beda, jadi Git nganggep itu perubahan.</p><p>Akhirnya, nyari bagian yang beneran diubah jadi makin males, karena harus scroll panjang-panjang buat nemuin satu-dua baris penting. PR history juga jadi gak jelas. Satu commit kelihatan gede banget, padahal cuma karena hapus spasi atau ubah indentasi doang 💀.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/276/0*uiC5T6_U9iZseQak.jpg" /></figure><p>Kadang malah merge conflict gara-gara spasi, bukan karena logicnya beda. Dan yang paling ngeselin beda file beda juga formatnya. Mungin sepele, tapi bikin kode jadi gak enak dibaca dan gak konsisten. Rasanya kayak buka buku yang setiap halamannya pakai font beda-beda.</p><p>Akhirnya kita coba beresin semua itu pakai ESLint dan Prettier, terus kita samain config-nya. Akhirnya code jadi jauh lebih rapi dan enak dilihat, semua orang pakai style yang sama, dan codebase kita akhirnya terasa lebih cohesive.</p><p><strong><em>“Terus apa ESLint dan gimana cara kerjanya? </em>☝🏻🤓<em>”</em></strong></p><p><strong>ESLint adalah alat linting untuk JavaScript dan TypeScript</strong>. ESLint ngebaca kode kamu, terus make sure sesuai dengan <strong>aturan-aturan yang kamu tentukan sendiri</strong>. Kalau ada yang melanggar aturan itu baik karena typo, gaya penulisan yang gak konsisten, atau potensi bug ESLint bakal ngasih tau.</p><p>Yang menarik, ESLint <strong>gak fixed</strong>. Dia bisa dikonfigurasi. Kamu bisa:</p><ul><li>Custom aturan-aturan yang pengen dipake</li><li>Mengatur level peringatan: mau cuma “warning” atau langsung dianggap “error”</li><li>Bahkan mengintegrasikannya dengan tools lain seperti <strong>Prettier</strong>, atau framework seperti <strong>React</strong>, <strong>Vue</strong>, atau <strong>Next.js</strong></li></ul><p>Gimana cara kerja ESLint?</p><ol><li><strong>Membaca Kode (Parsing)</strong><br>ESLint tidak membaca kode sebagai teks biasa, tapi mengubahnya menjadi struktur pohon (AST — Abstract Syntax Tree).</li><li><strong>Menjalankan Aturan-Aturan (Linting)</strong><br> Setelah punya pohon kode, ESLint mulai menjalankan aturan-aturan yang udah ditetapkan. Misalnya:<br>- “Apakah variabel ini dipakai?<br>- “Apakah perbandingan memakai == atau ===?&quot;<br>- “Apakah fungsi ini terlalu kompleks?”</li><li><strong>Memberi Peringatan atau Error</strong><br>Kalau ada pelanggaran, ESLint akan menampilkan pesan di terminal atau langsung di editor. Kadang bahkan bisa otomatis memperbaikinya (auto-fix).</li><li><strong>Integrasi ke Editor &amp; CI/CD</strong><br>ESLint bisa langsung jalan di editor (VS Code misalnya), atau jadi bagian dari proses deploy (CI/CD pipeline), untuk memastikan <strong>kode yang di-push ke production udah bersih dan konsisten.</strong></li></ol><h4>Use Strict</h4><p>If you have a strict parent that wouldn’t let anything slide, this does the same. Strict mode (&quot;use strict&quot;;) bakal ngebikin proses running jadi lebih strict. Contoh:</p><pre>nama = &quot;Fiska&quot;; // Tanpa deklarasi</pre><p>Kalau ga pake strict mode, code ini masih bisa jalan dan JavaScript bakal otomatis bikin variabel global. Tapi di strict mode, ini langsung error karena kamu belum deklarasi nama.</p><p>Kaya gini tuh perlu biar gak ada unwanted bugs yang muncul. Contoh:</p><pre>function test() {<br>  nama = &quot;Fiska&quot;; // lupa pakai let/const<br>}<br>test();<br>console.log(nama); // jadi global!</pre><pre>function jumlah(x, x) {<br>  return x + x;<br>} // nggak error tanpa strict mode padahal parameternya duplicate</pre><pre>function show() {<br>  console.log(this); // this = window (bukan undefined)<br>}<br>show();</pre><p>Framework modern kayak <strong>Nuxt, Next.js, Vite, bahkan Babel</strong> biasanya udah <strong>otomatis menjalankan kode dalam strict mode</strong>. Jadi walaupun kamu nggak eksplisit nulis &quot;use strict&quot;;, mereka udah nge-compile kodenya ke bentuk strict.</p><p>Pembahasan kali ini sampai situ dulu, yah. Selanjutnya kita bahas part 2 soal deklarasi dan assign variabel, type casting, sama nge-compare value. In the meantime, baca Medium gwe yang lain. Babai 👋😄</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/736/0*95Nx51U93eFc_cKP.jpg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=40d52a4170b7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Coba Pakai TrackBy di Angular!]]></title>
            <link>https://medium.com/@fiskasyela/coba-pakai-trackby-di-angular-56bf041e9f88?source=rss-34bc4b2429f2------2</link>
            <guid isPermaLink="false">https://medium.com/p/56bf041e9f88</guid>
            <category><![CDATA[ngfor]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[web-developer]]></category>
            <dc:creator><![CDATA[Fiska Syela]]></dc:creator>
            <pubDate>Wed, 19 Mar 2025 06:07:50 GMT</pubDate>
            <atom:updated>2025-03-19T06:12:44.276Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aRUxGe72U2d5t4Qrn1AOUw.png" /></figure><p>Looping list itu hal dasar yang sering kita lakuin. Tapi udah pernah coba pakai trackBy belum? Kalau belum, gue bakal jelasin kenapa ini penting.</p><p>TL;DR Fungsi utama trackBy itu mencegah Angular ngerender ulang seluruh elemen list setiap kali ada perubahan, dengan cara melacak elemen berdasarkan ID atau properti unik lainnya.</p><p>Tanpa trackBy nih, setiap perubahan dalam array bakal bikin Angular menghapus dan membuat ulang semua elemen di DOM. Dengan trackBy, Angular cuma memperbarui elemen yang berubah aja, jadi lebih efisien dan smooth.</p><p>Yup, intinya emang gitu doang. Kalau lo pengen denger penjelasan lebih detail dan contoh penggunaannya, let’s go kita bahas.</p><h3>Nature Javascript</h3><p>Pertama lo perlu tau dulu nature dari Javascript (languange yang dipake Angular) itu kayak gimana. Jadi, Javascript itu pake<strong> object reference</strong> (objek yang disimpan di heap memory) untuk tipe data kompleks kayak Object dan Array. Gampangnya, kalau lo assign atau oper sebuah object ke variabel lain, yang dipindahkan itu bukan nilai asli si object, tapi referensinya.</p><p>Contohnya nih:</p><pre>let arr1 = [1, 2, 3];<br>let arr2 = arr1;</pre><p>Yang terjadi di memori:</p><ul><li><strong>Heap Memory</strong>: Data array [1, 2, 3] disimpan di heap.</li><li><strong>Stack Memory</strong>: arr1 dan arr2 hanya menyimpan <strong>referensi</strong> ke lokasi heap yang sama.</li></ul><p>➡️Jadi, kalau kita <strong>memodifikasi array langsung</strong>, <strong>kedua variabel akan melihat perubahan yang sama</strong>:</p><pre>arr2.push(4);<br>console.log(arr1); // [1, 2, 3, 4]<br>console.log(arr2); // [1, 2, 3, 4]</pre><p>Karena <strong>kedua variabel merujuk ke referensi heap yang sama</strong>, perubahan pada arr2 juga mempengaruhi arr1.</p><p>JavaScript milih pakai object reference buat objek karena efisiensi memori dan performance. Kalau setiap kali kita assign objek ke variabel lain itu selalu bikin salinan baru, program bakal jadi lambat dan makan banyak memori, apalagi kalau objeknya gede.</p><h3>Default Behaviour Angular</h3><p>Sebenernya behaviour default Angular juga kayak gini. Tapi pada realitanya, kebutuhan developer sering nge-update array yang ngubah referensi dari heap memory kayak nambahin, ngehapus, sorting, dan filtering.Kita breakdown dulu kapan referensi berubah dan kapan enggak:</p><h4><strong>Kapan dan Bagaimana Referensi Baru Terbentuk?</strong></h4><p>1️⃣ Spread Operator</p><pre>let arr1 = [1, 2, 3];<br>let arr2 = [...arr1]; // Membuat array baru dengan isi yang sama</pre><pre>arr2.push(4);<br>console.log(arr1); // [1, 2, 3]<br>console.log(arr2); // [1, 2, 3, 4] ✅ Tidak mempengaruhi arr1!</pre><p><strong>🔍 Apa yang terjadi?</strong></p><ul><li>[...arr1] membuat array <strong>baru</strong> di heap memory.</li><li>arr1 dan arr2 sekarang memiliki <strong>referensi berbeda</strong>, meskipun isinya sama.</li></ul><p><strong>2️⃣ Pake </strong><strong>.map(), </strong><strong>.filter(), </strong><strong>.slice(), </strong><strong>.concat()</strong></p><p>Fungsi ini gak <strong>mengubah array asli</strong>, <strong>tapi mengembalikan array baru</strong>:</p><pre>let arr1 = [1, 2, 3];</pre><pre>let arr2 = arr1.map(num =&gt; num * 2); // [2, 4, 6]<br>let arr3 = arr1.filter(num =&gt; num !== 2); // [1, 3]<br>let arr4 = arr1.concat([4, 5]); // [1, 2, 3, 4, 5]</pre><pre>console.log(arr1); // [1, 2, 3] ✅ Tidak berubah</pre><p><strong>🔍 Apa yang terjadi?</strong></p><ul><li>map(), filter(), slice()dan concat() <strong>gak memodifikasi array lama</strong>, melainkan membuat array baru di heap.</li><li>arr1 tetap punya referensi lama, sementara arr2, arr3, dan arr4 punya referensi baru.</li></ul><h3>Masalah di Angular</h3><p>Waktu Angular merender sebuah array menggunakan *ngFor, dia harus menentukan apakah elemen dalam daftar perlu diperbarui, dihapus, atau ditambahkan ke DOM. <strong>Secara default</strong>, Angular <strong>hanya membandingkan referensi array</strong>, bukan isinya.</p><p>Masalahnya, Setiap kali kita memodifikasi array (misalnya dengan filtering atau re-assign data dari API), Angular liatnya sebagai <strong>array yang berbeda</strong> karena dia pakai <strong>reference check</strong>, bukan deep comparison.</p><p>Karena itu, Angular bakal <strong>menganggap semua elemen dalam array berubah</strong>, walaupun isinya <strong>sama aja</strong>. Akibatnya:</p><ul><li><strong>DOM akan di-update terus-menerus</strong>, bahkan untuk elemen yang nggak berubah.</li><li><strong>Performa turun drastis</strong>, terutama kalau list-nya besar (misalnya looping 50+ data).</li><li><strong>State di dalam elemen (kayak input yang lagi diketik) bisa hilang</strong> karena elemen dihapus dan dibuat ulang.</li></ul><h4>🫣 Contoh Kasus:</h4><p>Misalnya lo punya list 50 data di UI, terus lo filter jadi 25 data. Karena referensinya berubah, Angular bakal render ulang semua elemen dari nol, bukan cuma update yang berubah.</p><h3>Solusinya? Pake trackBy!</h3><p>trackBy, bakal kasih tahu Angular <strong>gimana cara mengenali elemen dalam list</strong> berdasarkan properti unik (biasanya ID). Jadi, Angular <strong>cuma update elemen yang benar-benar berubah</strong>, bukan semuanya.</p><p>✅ <strong>Hasilnya:</strong></p><ul><li><strong>DOM lebih efisien</strong>, nggak nge-refresh elemen yang nggak perlu.</li><li><strong>Performa lebih cepat</strong>, karena Angular cuma update yang perlu.</li><li><strong>State di elemen UI tetap aman</strong>, nggak tiba-tiba hilang.</li></ul><p>🔍 <strong>Apa yang terjadi di balik layar?</strong></p><ol><li>Saat pertama kali Angular merender daftar, dia <strong>menyimpan referensi ID tiap elemen</strong>.</li><li>Ketika data diperbarui (misalnya dari API), Angular akan:</li></ol><ul><li><strong>Membandingkan ID lama dan ID baru</strong>.</li><li><strong>Hanya memperbarui elemen yang ID-nya berubah</strong>.</li><li><strong>Membiarkan elemen yang ID-nya tetap sama</strong> (tidak dihapus dari DOM).</li></ul><p>Jadi, hanya elemen yang benar-benar berubah yang akan diperbarui di DOM</p><h4>Contoh:</h4><p>Misalkan lo punya data awal ini:</p><pre>this.users = [<br>  { id: 1, name: &quot;Alice&quot; },<br>  { id: 2, name: &quot;Bob&quot; },<br>  { id: 3, name: &quot;Charlie&quot; }<br>];</pre><p>Kemudian kita <strong>update array</strong> seperti ini:</p><pre>this.users = [<br>  { id: 1, name: &quot;Alice&quot; },    // Tetap sama<br>  { id: 2, name: &quot;Bob&quot; },      // Tetap sama<br>  { id: 3, name: &quot;Charles&quot; },  // Nama berubah (Charlie → Charles)<br>  { id: 4, name: &quot;David&quot; }     // Elemen baru<br>];</pre><p>👉 <strong>Tanpa </strong><strong>trackBy</strong></p><ul><li><strong>Semua elemen akan dihapus dan dibuat ulang</strong> dari nol.</li><li><strong>UI akan flickering, input di dalamnya bisa hilang.</strong></li></ul><p>👉 <strong>Dengan </strong><strong>trackBy</strong></p><ul><li>Angular <strong>melihat ID Alice (1) dan Bob (2) masih sama</strong>, jadi tidak menyentuh mereka.</li><li>Charlie diubah jadi Charles <strong>(ID tetap 3)</strong>, jadi Angular <strong>hanya update teksnya, bukan buat ulang elemen</strong>.</li><li>David <strong>elemen baru (ID 4)</strong>, jadi Angular <strong>menambahkan elemen baru saja</strong>, bukan reset semuanya.</li></ul><p><strong>Hasilnya: Render lebih efisien, lebih cepat, dan UI tetap smooth tanpa flickering.</strong></p><h3>Contoh Penggunaan</h3><h4>HTML (Template)</h4><pre>&lt;div *ngFor=&quot;let user of users; trackBy: trackById&quot;&gt;<br>  {{ user.name }}<br>&lt;/div&gt;</pre><h4>TypeScript (Component)</h4><pre>export class MyComponent {<br>  users = [<br>    { id: 1, name: &quot;Alice&quot; },<br>    { id: 2, name: &quot;Bob&quot; },<br>    { id: 3, name: &quot;Charlie&quot; }<br>  ];</pre><pre>  trackById(index: number, user: any): number {<br>    return user.id;<br>  }</pre><pre>  updateUsers() {<br>    this.users = [<br>      { id: 1, name: &quot;Alice&quot; },<br>      { id: 2, name: &quot;Bob&quot; },<br>      { id: 3, name: &quot;Charles&quot; }, // Nama berubah<br>      { id: 4, name: &quot;David&quot; }    // Elemen baru<br>    ];<br>  }<br>}</pre><p>Kesimpulan: Bagaimana trackBy Bekerja?</p><ol><li><strong>Tanpa </strong><strong>trackBy</strong>, Angular hanya membandingkan <strong>referensi array</strong>, bukan isinya.</li><li><strong>Setiap kali array berubah</strong>, Angular <strong>menghapus &amp; membuat ulang seluruh elemen</strong> di DOM.</li><li><strong>Dengan </strong><strong>trackBy</strong>, Angular membandingkan elemen berdasarkan <strong>nilai unik (misalnya ID)</strong>.</li><li><strong>Hanya elemen yang benar-benar berubah yang akan diperbarui di DOM</strong>, meningkatkan performa &amp; menjaga state UI tetap stabil.</li></ol><p>Kayaknya segitu dulu ya pembahasan singkat kali ini. Kalau ada hal yang kurang tepat atau mau nambahin, drop aja ya di kolom comment. In the meantime take care of yourself 😁👌</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=56bf041e9f88" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reactivity: Why Vue Feels Intuitive & Angular Feels Like a Puzzle to Solve]]></title>
            <link>https://medium.com/@fiskasyela/reactivity-why-vue-feels-intuitive-angular-feels-like-a-puzzle-to-solve-fe7a3751f3cc?source=rss-34bc4b2429f2------2</link>
            <guid isPermaLink="false">https://medium.com/p/fe7a3751f3cc</guid>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[angularjs]]></category>
            <dc:creator><![CDATA[Fiska Syela]]></dc:creator>
            <pubDate>Sat, 08 Feb 2025 09:23:45 GMT</pubDate>
            <atom:updated>2025-02-08T09:32:04.629Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/736/1*sBLblQWN_KlYXYj-jRCUbQ.jpeg" /></figure><p>As a developer who has worked with Vue 3, transitioning to Angular 18 it felt so complicated. Vue’s reactivity model is intuitive, declarative, and straightforward something that makes state management feel almost effortless. Angular, on the other hand, leans heavily on RxJS and reactive streams, introducing a completely different way of thinking about state, events, and data flow.</p><p>Angular’s concepts like BehaviorSubject, Observables, and pipe operations seemed unnecessarily complex compared to Vue’s ref() and computed(). However, once I got the hang of Angular, I realized that despite the steep learning curve, RxJS offers a lot of power and scalability especially for handling complex, data-driven applications.</p><p>In this article, I’ll break down the key differences in reactivity between Vue and Angular, explore why Angular embraces RxJS, and help you understand why this shift in mindset is worth the effort.</p><h4>Why Angular Feels So Different</h4><p>Vue 3’s reactivity system is designed to be intuitive and developer-friendly. At its core, it relies on ref() and reactive(), which automatically track dependencies and update the DOM when the underlying state changes.</p><p><strong>Example in Vue 3:</strong></p><pre>&lt;script setup&gt;<br>import { ref, computed } from &#39;vue&#39;;</pre><pre>const count = ref(0);<br>const double = computed(() =&gt; count.value * 2);</pre><pre>function increment() {<br>  count.value++;<br>}<br>&lt;/script&gt;</pre><pre>&lt;template&gt;<br>  &lt;p&gt;Count: {{ count }}&lt;/p&gt;<br>  &lt;p&gt;Double: {{ double }}&lt;/p&gt;<br>  &lt;button @click=&quot;increment&quot;&gt;Increment&lt;/button&gt;<br>&lt;/template&gt;</pre><p>Here, Vue automatically tracks reactivity through the ref() function. When count updates, double is recomputed, and the UI reflects the change instantly.</p><p><strong>Angular’s</strong> reactive system is fundamentally different. Instead of using direct state manipulation like Vue, Angular relies on RxJS, where data flows through streams using Observable and Subject.</p><p><strong>Example in Angular 18 (Using RxJS for State Management):</strong></p><pre>import { Component } from &#39;@angular/core&#39;;<br>import { BehaviorSubject } from &#39;rxjs&#39;;</pre><pre>@Component({<br>  selector: &#39;app-counter&#39;,<br>  template: `<br>    &lt;p&gt;Count: {{ count$ | async }}&lt;/p&gt;<br>    &lt;button (click)=&quot;increment()&quot;&gt;Increment&lt;/button&gt;<br>  `,<br>})<br>export class CounterComponent {<br>  private countSubject = new BehaviorSubject&lt;number&gt;(0);<br>  count$ = this.countSubject.asObservable();</pre><pre>  increment() {<br>    this.countSubject.next(this.countSubject.value + 1);<br>  }<br>}</pre><h4>How Angular Handles Data Flow Through Streams (Behind the Scenes)</h4><p>Angular’s reactivity is built around <strong>Observables</strong> and <strong>RxJS operators</strong>, making it fundamentally different from Vue’s dependency-based tracking.</p><ol><li><strong>Data as a Stream Instead of a Direct Value</strong></li></ol><p>In Vue, when a reactive value like ref(0) changes, Vue’s reactivity system automatically detects the update and triggers re-renders.</p><p>In Angular, however, <strong>state is not stored as a direct value but as an Observable stream</strong>. This means:</p><ul><li>Data <strong>flows</strong> through the application as an event-driven stream.</li><li>Components <strong>subscribe</strong> to these streams instead of accessing values directly.</li><li>The <strong>UI updates only when new data is emitted</strong> by the Observable.</li></ul><p><strong>2. Event-Driven Updates Instead of Dependency Tracking</strong></p><p>Vue tracks dependencies automatically and updates computed values whenever dependencies change. Angular, on the other hand, <strong>doesn’t track dependencies</strong> — instead, it relies on event-driven updates:</p><ul><li><strong>Components don’t “watch” state; they listen to Observables.</strong></li><li><strong>No implicit reactivity:</strong> Changes must be explicitly emitted through Subjects/BehaviorSubjects.</li></ul><p>Example: Counter with BehaviorSubject</p><pre>export class CounterComponent {<br>  private countSubject = new BehaviorSubject&lt;number&gt;(0);<br>  count$ = this.countSubject.asObservable(); // Exposing as an Observable</pre><pre>  increment() {<br>    this.countSubject.next(this.countSubject.value + 1);<br>  }<br>}</pre><p>📍 <strong>Behind the scenes:</strong></p><ul><li>countSubject holds the state and emits new values.</li><li>The UI subscribes to count$ using async, so it updates when new values are emitted.</li><li>No automatic tracking — everything is event-driven.</li></ul><p><strong>3. RxJS Operators Transform Data Before It Reaches Components</strong></p><p>Vue’s computed properties provide declarative transformations. In Angular, <strong>RxJS operators</strong> handle this within the stream itself.</p><p>Example: Instead of modifying values directly, you can use map() to transform data before it reaches the component.</p><pre>this.doubleCount$ = this.count$.pipe(map(value =&gt; value * 2));</pre><p>📍 <strong>Key Difference:</strong> In Vue, computed(() =&gt; count.value * 2) would do the same thing, but Angular forces you to transform within the stream itself.</p><p><strong>4. Subscriptions Control Data Flow (Explicit Lifecycle Management)</strong></p><p>In Vue, computed properties and watchers are automatically cleaned up. Angular, however, requires <strong>manual subscription management</strong> to avoid memory leaks.</p><pre>ngOnInit() {<br>  this.subscription = this.count$.subscribe(value =&gt; console.log(value));<br>}</pre><pre>ngOnDestroy() {<br>  this.subscription.unsubscribe();<br>}</pre><p>📍 <strong>Why?</strong> Because Observables can emit infinite values, they must be explicitly unsubscribed when a component is destroyed.</p><h4>Why Does Angular Choose RxJS?</h4><p>The reason Angular adopts RxJS is scalability and flexibility. While Vue’s reactivity system works well for small to medium applications, it can become challenging to manage complex state interactions and side effects in large applications. RxJS, despite its complexity, offers powerful tools for handling:</p><ul><li><strong>Asynchronous Data Streams</strong> (e.g., API responses, WebSockets)</li><li><strong>Complex Event Handling</strong> (e.g., user inputs, debouncing, combining events)</li><li><strong>State Synchronization</strong> (e.g., keeping multiple components in sync)</li></ul><p><strong>1. Asynchronous Data Streams (Handling API Calls Efficiently)</strong></p><p>Vue uses watch() or fetch inside onMounted(), but Angular can handle async calls declaratively using RxJS:</p><p><strong>Vue Example:</strong></p><pre>&lt;script setup&gt;<br>import { ref, watch } from &#39;vue&#39;;<br>import axios from &#39;axios&#39;;</pre><pre>const query = ref(&#39;&#39;);<br>const results = ref([]);</pre><pre>watch(query, async (newQuery) =&gt; {<br>  const response = await axios.get(`/search?q=${newQuery}`);<br>  results.value = response.data;<br>});<br>&lt;/script&gt;</pre><p><strong>Angular Example:</strong></p><pre>this.results$ = this.query$.pipe(<br>  debounceTime(300),<br>  distinctUntilChanged(),<br>  switchMap(query =&gt; this.http.get(`/search?q=${query}`))<br>);</pre><p>📍 <strong>Angular’s advantage:</strong> No redundant API calls — only the latest query triggers a request.</p><p><strong>2. Complex Event Handling (Debouncing User Input)</strong></p><p>Debouncing in Vue requires manual handling, whereas Angular can manage it elegantly via RxJS operators:</p><p><strong>Vue Example:</strong></p><pre>const searchQuery = ref(&#39;&#39;);<br>const debouncedQuery = ref(&#39;&#39;);<br>let timeout;</pre><pre>watch(searchQuery, (newValue) =&gt; {<br>  clearTimeout(timeout);<br>  timeout = setTimeout(() =&gt; {<br>    debouncedQuery.value = newValue;<br>  }, 300);<br>});</pre><p><strong>Angular Example:</strong></p><pre>this.searchQuery$.pipe(<br>  debounceTime(300),<br>  distinctUntilChanged(),<br>  switchMap(query =&gt; this.searchService.search(query))<br>).subscribe(results =&gt; this.results = results);</pre><p>📍 <strong>Angular’s advantage:</strong> Streams handle debouncing, ensuring performance efficiency.</p><p><strong>3. State Synchronization (Keeping Components in Sync)</strong></p><p>Vue requires manual prop drilling or Vuex/Pinia for complex state sharing, while Angular’s RxJS allows reactive state management:</p><p><strong>Vue Example (Vuex/Pinia):</strong></p><pre>const store = useStore();<br>const count = computed(() =&gt; store.count);<br>function increment() {<br>  store.increment();<br>}</pre><p><strong>Angular Example (RxJS State Management):</strong></p><pre>this.count$ = this.stateService.count$;<br>increment() { this.stateService.increment(); }</pre><p>📍 <strong>Angular’s advantage:</strong> No need for a centralized state library — RxJS handles reactivity natively.</p><h4><strong>Understanding RxJS: The Key to Angular’s Reactivity</strong></h4><p>RxJS (Reactive Extensions for JavaScript) is the backbone of Angular’s reactivity system. It introduces a declarative approach to handling data streams, making it easier to deal with asynchronous operations and event-driven programming.</p><h4><strong>Core Concepts of RxJS</strong></h4><p>RxJS is built around a few <strong>core concepts</strong> that make it a powerful tool for managing asynchronous data in a declarative way. Let’s break them down in depth so you truly understand how RxJS works at its core.</p><ol><li><strong>Observables: The Foundation of RxJS</strong></li></ol><p>An Observable is like a function that:</p><ol><li>Produces multiple values over time.</li><li>Can be <strong>subscribed</strong> to.</li><li>Can be <strong>transformed</strong> using operators.</li></ol><p>Example:</p><pre>import { Observable } from &#39;rxjs&#39;;</pre><pre>const observable = new Observable&lt;number&gt;((subscriber) =&gt; {<br>  subscriber.next(1);<br>  subscriber.next(2);<br>  subscriber.next(3);<br>  subscriber.complete();<br>});</pre><pre>observable.subscribe({<br>  next: (value) =&gt; console.log(value),<br>  complete: () =&gt; console.log(&#39;Stream completed&#39;),<br>});</pre><p>✅ <strong>Output:</strong></p><pre>1<br>2<br>3<br>Stream completed</pre><p><strong>2. Observers &amp; Subscriptions</strong></p><p>An <strong>Observer</strong> listens to an Observable. The <strong>Subscription</strong> represents the connection to the stream.</p><pre>const observable = new Observable&lt;string&gt;((subscriber) =&gt; {<br>  subscriber.next(&#39;Hello&#39;);<br>  subscriber.next(&#39;RxJS&#39;);<br>});</pre><pre>const observer = {<br>  next: (value: string) =&gt; console.log(`Received: ${value}`),<br>};</pre><pre>const subscription = observable.subscribe(observer);</pre><pre>// Later, if needed, we can unsubscribe<br>subscription.unsubscribe();</pre><p>✅ <strong>Unsubscribing prevents memory leaks</strong>, especially in Angular components.</p><p><strong>3. Subjects: Multicasting and Sharing Streams</strong></p><p>A <strong>Subject</strong> is both an <strong>Observable</strong> and an <strong>Observer</strong>, meaning it can both <strong>emit values</strong> and <strong>subscribe to them</strong>.</p><p>📍Basic Subject Example</p><pre>import { Subject } from &#39;rxjs&#39;;</pre><pre>const subject = new Subject&lt;number&gt;();</pre><pre>subject.subscribe((value) =&gt; console.log(&#39;Subscriber 1:&#39;, value));<br>subject.subscribe((value) =&gt; console.log(&#39;Subscriber 2:&#39;, value));</pre><pre>subject.next(42);<br>subject.next(100);</pre><p>✅ <strong>Output:</strong></p><pre>Subscriber 1: 42<br>Subscriber 2: 42<br>Subscriber 1: 100<br>Subscriber 2: 100</pre><p>📍BehaviorSubject: Keeping the Last Value</p><pre>import { BehaviorSubject } from &#39;rxjs&#39;;</pre><pre>const count$ = new BehaviorSubject&lt;number&gt;(0);</pre><pre>count$.subscribe((value) =&gt; console.log(&#39;Subscriber 1:&#39;, value));<br>count$.next(10);<br>count$.subscribe((value) =&gt; console.log(&#39;Subscriber 2:&#39;, value));</pre><p>✅ <strong>Output:</strong></p><pre>Subscriber 1: 0<br>Subscriber 1: 10<br>Subscriber 2: 10</pre><p>📍ReplaySubject: Replaying Multiple Past Values</p><pre>import { ReplaySubject } from &#39;rxjs&#39;;</pre><pre>const replay$ = new ReplaySubject&lt;number&gt;(2); // Buffer size = 2<br>replay$.next(1);<br>replay$.next(2);<br>replay$.next(3);</pre><pre>replay$.subscribe((value) =&gt; console.log(&#39;Subscriber:&#39;, value));</pre><p>✅ <strong>Output:</strong></p><pre>Subscriber: 2<br>Subscriber: 3</pre><p><strong>4. Operators: The Power of RxJS</strong></p><p>Operators allow you to <strong>transform</strong>, <strong>filter</strong>, <strong>combine</strong>, and <strong>manipulate</strong> Observables.</p><p>📍 Transforming Streams with map()</p><pre>import { of } from &#39;rxjs&#39;;<br>import { map } from &#39;rxjs/operators&#39;;</pre><pre>of(1, 2, 3)<br>  .pipe(map((x) =&gt; x * 10))<br>  .subscribe(console.log);</pre><p>✅ <strong>Output:</strong></p><pre>10<br>20<br>30</pre><p>📍 Filtering Streams with filter()</p><pre>import { of } from &#39;rxjs&#39;;<br>import { filter } from &#39;rxjs/operators&#39;;</pre><pre>of(1, 2, 3, 4, 5)<br>  .pipe(filter((x) =&gt; x % 2 === 0))<br>  .subscribe(console.log);</pre><p>✅ <strong>Output:</strong></p><pre>2<br>4</pre><p>📍 Handling Async Streams with switchMap()</p><pre>import { fromEvent, of } from &#39;rxjs&#39;;<br>import { debounceTime, switchMap } from &#39;rxjs/operators&#39;;</pre><pre>const searchInput = document.getElementById(&#39;search&#39;) as HTMLInputElement;<br>const search$ = fromEvent(searchInput, &#39;input&#39;).pipe(<br>  debounceTime(300),<br>  switchMap((event) =&gt; of((event.target as HTMLInputElement).value))<br>);</pre><pre>search$.subscribe(console.log);</pre><p>✅ <strong>Why </strong><strong>switchMap()?</strong></p><ul><li>Cancels the previous API call before making a new one.</li><li>Reduces unnecessary network requests.</li></ul><p><strong>5. Managing Subscriptions in Angular</strong></p><p>📍 Using async Pipe (Recommended)</p><pre>&lt;p *ngIf=&quot;user$ | async as user&quot;&gt;User: {{ user.name }}&lt;/p&gt;</pre><p>📍 Using takeUntil() for Cleanup</p><pre>import { Subject } from &#39;rxjs&#39;;<br>import { takeUntil } from &#39;rxjs/operators&#39;;</pre><pre>private destroy$ = new Subject&lt;void&gt;();</pre><pre>this.someObservable.pipe(takeUntil(this.destroy$)).subscribe();</pre><pre>ngOnDestroy() {<br>  this.destroy$.next();<br>  this.destroy$.complete();<br>}</pre><h4>Real-Life Example: Search Autocomplete with RxJS</h4><p>Imagine building a search bar that fetches results dynamically as the user types. Without RxJS, handling API calls while avoiding unnecessary requests would be tedious. With RxJS, it’s more efficient:</p><pre>import { Component } from &#39;@angular/core&#39;;<br>import { debounceTime, distinctUntilChanged, switchMap } from &#39;rxjs/operators&#39;;<br>import { Subject, Observable } from &#39;rxjs&#39;;<br>import { SearchService } from &#39;./search.service&#39;;</pre><pre>@Component({<br>  selector: &#39;app-search&#39;,<br>  template: `<br>    &lt;input type=&quot;text&quot; (input)=&quot;search($event.target.value)&quot; placeholder=&quot;Search...&quot; /&gt;<br>    &lt;ul&gt;<br>      &lt;li *ngFor=&quot;let result of results$ | async&quot;&gt;{{ result }}&lt;/li&gt;<br>    &lt;/ul&gt;<br>  `<br>})<br>export class SearchComponent {<br>  private searchSubject = new Subject&lt;string&gt;();<br>  results$: Observable&lt;string[]&gt;;</pre><pre>  constructor(private searchService: SearchService) {<br>    this.results$ = this.searchSubject.pipe(<br>      debounceTime(300),<br>      distinctUntilChanged(),<br>      switchMap(query =&gt; this.searchService.search(query))<br>    );<br>  }</pre><pre>  search(query: string) {<br>    this.searchSubject.next(query);<br>  }<br>}</pre><h4>Conclusion: Vue vs. Angular Reactivity — A Matter of Perspective</h4><p>Switching from Vue 3 to Angular 18 is more than just adapting to different syntax — it’s about embracing a completely different mental model of reactivity.</p><ul><li><strong>Vue’s reactivity</strong> is simple, automatic, and dependency-tracked, making it intuitive for most developers.</li><li><strong>Angular’s RxJS approach</strong> introduces a powerful but more explicit way of handling data flow, offering better scalability and control over complex, event-driven applications.</li></ul><p>Understanding RxJS is essential to mastering Angular’s reactive ecosystem. Though the learning curve may feel steep at first, the benefits — such as efficient data handling, real-time updates, and streamlined API communication make it a worthwhile investment for large-scale applications.</p><p>At the end of the day, both frameworks solve the same problem in different ways. If you’re coming from Vue, Angular might feel like a puzzle at first — but once the pieces click, you’ll appreciate the control and flexibility it offers.</p><p>Thanks for joining me on this deep dive! If you enjoyed this, let’s talk about another programming concept next. Maybe state management across different frameworks? Or how modern web apps handle performance optimizations? Let me know what you’d love to explore next!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fe7a3751f3cc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I Make Login Page in Angular 18]]></title>
            <link>https://medium.com/@fiskasyela/how-i-make-login-page-in-angular-18-946392adbbd6?source=rss-34bc4b2429f2------2</link>
            <guid isPermaLink="false">https://medium.com/p/946392adbbd6</guid>
            <category><![CDATA[tailwind]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[daisyui]]></category>
            <category><![CDATA[angularjs]]></category>
            <dc:creator><![CDATA[Fiska Syela]]></dc:creator>
            <pubDate>Sun, 20 Oct 2024 11:28:53 GMT</pubDate>
            <atom:updated>2024-10-20T11:31:47.818Z</atom:updated>
            <content:encoded><![CDATA[<p>I recently had to build a login feature using Angular 18 for work. It took me a week to get it right, dealing with various errors along the way. Here’s a guide to help you avoid the same pitfalls and create a working login page quickly. You can access the full code 👉🏻 <a href="https://github.com/saturniesm/login-angular">here</a> Clone it as you please as we go through how to get to that point.</p><h4><strong>What’ll you need</strong></h4><ul><li>Node.js and npm (Node Package Manager) installed on your system</li><li>Angular CLI installed globally (npm install -g @angular/cli)</li><li>A code editor (e.g., Visual Studio Code, WebStorm, or Sublime Text)</li><li>Basic knowledge of TypeScript and Angular concepts</li></ul><p>To check whether those installed or not, you can run these commands:</p><pre>code -v<br>node -v <br>npm -v<br>ng version</pre><p>If it’s unrecognizable by your machine run these commands to download the unnecessary dependencies; PS this is for macOS.</p><pre>brew update<br>brew tap homebrew/cask<br>brew install visual-studio-code<br>brew install node@20<br>npm install -g @angular/cli</pre><p>For more detail guide about downloading, you can read here:</p><ul><li><a href="https://nodejs.org/en/download/package-manager">https://nodejs.org/en/download/package-manager</a></li><li><a href="https://angular.dev/installation">https://angular.dev/installation</a></li><li><a href="https://code.visualstudio.com/download">https://code.visualstudio.com/download</a></li></ul><h4>To create a functional login page, we’ll cover:</h4><ol><li>Setting up a new Angular project</li><li>Creating the login component</li><li>Designing the login page UI</li><li>Implementing reactive forms for input handling</li><li>Adding form validation</li><li>Configuring routes for the login page</li><li>Implementing error handling and user feedback</li></ol><h4>Setting up a new Angular project</h4><p>To set up a new Angular project, run the following command in your terminal. We’ll name our project “login”:</p><pre>ng new login</pre><pre>-&gt; i use scss       <br>? Which stylesheet format would you like to use?<br>  CSS             [ &lt;https://developer.mozilla.org/docs/Web/CSS&gt;                     ]<br>❯ Sass (SCSS)     [ &lt;https://sass-lang.com/documentation/syntax#scss&gt;                ]<br>  Sass (Indented) [ &lt;https://sass-lang.com/documentation/syntax#the-indented-syntax&gt; ]<br>  Less            [ &lt;http://lesscss.org&gt;                                             ]</pre><pre>-&gt; i enable ssr<br>? Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? (y/N) y</pre><p>Let’s change our directory to the project that we’ve created and initialize git</p><pre>cd login<br>git init</pre><h4>Project Structure</h4><p>Now that we’ve successfully set up our Angular project, let’s take a moment to familiarize ourselves with its structure. Understanding the project layout is crucial before we start building components and diving deeper into development. As a fellow Angular newcomer, I’ve found that a well-organized folder structure can significantly improve our workflow and code maintainability. You can read more <a href="https://dev.to/vixero/a-simple-angular-folder-structure-that-makes-development-feel-natural-and-easy-241d">here</a>. Let’s take a quick tour of our project’s anatomy:</p><ul><li><strong>Core</strong>: This folder contains the core functionality of your application, like services, interceptors, guards, and configuration files that are essential for your app but not tied to any specific feature. It&#39;s the backbone of the application and handles global logic.</li><li><strong>Features</strong>: This is where the main modules of your app reside. Each feature, like login, dashboard, or user management, has its own dedicated folder, keeping the logic for individual functionalities well-organized and modular.</li><li><strong>Shared</strong>: The shared folder holds reusable components, directives, pipes, and services that can be used across multiple features of your app. It encourages code reuse and keeps things DRY (Don&#39;t Repeat Yourself).</li><li><strong>APIs</strong>: This folder contains API service files responsible for making HTTP calls to your backend. It&#39;s the interface for your app to communicate with external data sources, keeping API logic centralized.</li><li><strong>Types</strong>: This folder is where you define TypeScript types and interfaces. By maintaining all the data structures and models in one place, it ensures consistency and type safety across your application.</li><li><strong>Store</strong>: this folder manages your global app state, including actions, reducers, effects, and selectors, making it easier to handle complex state login</li></ul><h4>Configuring routes for the login page</h4><p>To make the login component as our login, we need to set the login router inside of the app.routes.ts file. Now, if you access the auth/login page, you’ll see login component.</p><pre>import { Routes } from &#39;@angular/router&#39;;<br>import { LoginComponent } from &#39;./features/auth/login/login.component&#39;;<br><br>export const routes: Routes = [<br>  { path: &#39;auth/login&#39;, component: LoginComponent },<br>];</pre><h4>Creating the login component</h4><p>We’re going to recreate a design I found in the Figma Community. Check it out 👉🏻 <a href="https://www.figma.com/community/file/1019155319918719973/logify-web-login-ui-kit">here</a>. It’s a slick layout that’ll really make our app pop.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SMl6p5GCEkjxxm-en4KgJw.png" /><figcaption>Login Page</figcaption></figure><p>To create the login functionality, we need a login page with a form containing email and password inputs, a captcha for added security, and a submit button. Let’s set this up by running the following command:</p><pre>ng generate component features/auth/login</pre><p>By running those command, you’ll have a directory like this:</p><pre>├── app<br>├───features<br>│   └───auth<br>│       └───login<br>│               login.component.html<br>│               login.component.scss<br>│               login.component.spec.ts<br>│               login.component.ts</pre><p>It’s necessity to understand what’s going on before doing any coding. So we will try to understand how to component works.</p><p>By default, this command creates the following:</p><ul><li>A directory named after the component</li><li>A component file, &lt;component-name&gt;.component.ts</li><li>A template file, &lt;component-name&gt;.component.html</li><li>A CSS file, &lt;component-name&gt;.component.css</li><li>A testing specification file, &lt;component-name&gt;.component.spec.ts</li></ul><pre>//Inside login.component.ts<br>@Component({<br> /*<br>   This selector will determine how the HTML tag will look like<br>   &lt;app-login&gt;&lt;/app-login&gt;<br> */<br>  selector: &#39;app-login&#39;,<br>  <br>  //to import the template file into the component<br>  templateUrl: &#39;./login.component.html&#39;,<br>  <br>  //to import the template file into the component<br>  styleUrl: &#39;./login.component.scss&#39;,<br>  <br>  /*<br>   Indicating it&#39;s stand alone<br>   (Standalone components provide a simplified way to build Angular applications.<br>   Standalone components, directives, and pipes aim to streamline the authoring experience by reducing the need for NgModule<br>  */<br>  standalone: true,<br>  <br>  //placing our imports here later<br>  imports: [],<br>  <br>})</pre><p>Next up, we’re gonna style our component. To make this quick and easy, I’m using Tailwind CSS and Daisy UI. Here’s why:</p><ul><li>Tailwind CSS: A super flexible CSS framework. It lets us style stuff right in our HTML using utility classes.</li><li>Daisy UI: It’s like a booster pack for Tailwind. It gives us ready-made components to work with.</li></ul><p>Together, these tools will help us style our app fast, keep things consistent, and save us from writing tons of custom CSS. To do that we need to install and setup those two.</p><pre>npm install -D tailwindcss postcss autoprefixer<br>npx tailwindcss init<br>npm i -D daisyui@latest</pre><p>Edit this inside of the tailwind config. I also add custom color and font that we will setup later.</p><pre>/** @type {import(&#39;tailwindcss&#39;).Config} */<br>module.exports = {<br>  darkMode: false,<br>  content: [&quot;./src/**/*.{html,ts}&quot;],<br>  theme: {<br>    extend: {<br>      fontFamily: {<br>        sans: [&quot;Afacad Flux&quot;, &quot;sans-serif&quot;],<br>        roboto: [&quot;Roboto&quot;, &quot;sans-serif&quot;],<br>        display: [&quot;Nunito Sans&quot;, &quot;sans-serif&quot;],<br>      },<br>      colors: {<br>        primary: {<br>          blue: &quot;#000842&quot;,<br>        },<br>      },<br>    },<br>  },<br>  plugins: [require(&quot;daisyui&quot;)],<br>  daisyui: {<br>    themes: [&quot;light&quot;],<br>  },<br>};</pre><p>Add this inside styles.css</p><pre>@tailwind base;<br>@tailwind components;<br>@tailwind utilities;</pre><p>To test whether the installation is success or not, you can modify app.component.html</p><pre>&lt;div&gt;<br>  &lt;span class=&quot;font-bold text-red-400&quot;&gt;Test Login&lt;/span&gt;<br>  &lt;router-outlet /&gt;<br>&lt;/div&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/538/1*geV7qixPhUuAqLf_PLMYgQ.png" /></figure><p>And, last i want to use certain font style in my code. To add them just edit the index.html and add these inside the head tag. What this code does is basically importing the necessary font and icons.</p><pre>    &lt;link<br>      href=&quot;https://fonts.googleapis.com/css2?family=Afacad+Flux:slnt,wght@9,100..1000&amp;family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&amp;family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&amp;display=swap&quot;<br>      rel=&quot;stylesheet&quot;<br>    /&gt;<br>    &lt;link<br>      href=&quot;https://fonts.googleapis.com/icon?family=Material+Icons&quot;<br>      rel=&quot;stylesheet&quot;<br>    /&gt;<br>    &lt;link<br>      href=&quot;https://fonts.googleapis.com/icon?family=Material+Icons+Outlined&quot;<br>      rel=&quot;stylesheet&quot;<br>    /&gt;</pre><p>For every dependency we’ve installed so far you can their documentation here 😁👍</p><ul><li><a href="https://tailwindcss.com/docs/installation">https://tailwindcss.com/docs/installation</a></li><li><a href="https://daisyui.com/docs/install/">https://daisyui.com/docs/install/</a></li><li><a href="https://fonts.google.com/">https://fonts.google.com/</a></li><li><a href="https://fonts.google.com/icons">https://fonts.google.com/icons</a></li></ul><p>Let’s start slicing 😆‼️</p><p>The layout use half-half approach. so we need to separate our page into two section using flex.</p><pre>//Inside login.html<br>&lt;div class=&quot;flex w-screen h-screen font-display&quot;&gt;<br>  &lt;div class=&quot;form-layout&quot;&gt;<br>    &lt;div class=&quot;image-layout image-background&quot;&gt;&lt;/div&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><pre>//inside login.scss<br>.form {<br>  &amp;-layout {<br>    @apply w-1/2 h-screen flex flex-col gap-4 px-[6%] justify-center;<br>  }<br>}<br><br>.image {<br>  &amp;-layout {<br>    @apply w-1/2 h-screen flex flex-col items-center justify-end py-[5%];<br>  }<br><br>  &amp;-background {<br>    @apply bg-primary-blue;<br>  }<br>}</pre><p>For the image adds inside the public folder.</p><pre>├── public<br>│   ├── assets<br>│   │   └── images<br>│   │       ├── illustrasi.svg<br>│   └── favicon.ico</pre><pre>&lt;div class=&quot;flex w-screen h-screen font-display&quot;&gt;<br>  &lt;div class=&quot;form-layout&quot;&gt;<br>    &lt;div class=&quot;heading-layout&quot;&gt;<br>      &lt;h1 class=&quot;heading-title leading&quot;&gt;Sign in here&lt;/h1&gt;<br>      &lt;span class=&quot;subtitle&quot;&gt;<br>        If you don’t have an account register You can<br>        &lt;span class=&quot;font-bold text-blue-800&quot;&gt;Register here !&lt;/span&gt;<br>      &lt;/span&gt;<br>    &lt;/div&gt;<br>  &lt;/div&gt;<br>  &lt;div class=&quot;image-layout image-background&quot;&gt;<br>    &lt;img<br>      src=&quot;assets/images/illustrasi.svg&quot;<br>      alt=&quot;login-illustration&quot;<br>      class=&quot;image-illustration&quot;<br>    /&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><pre>.image {<br>  &amp;-layout {<br>    @apply w-1/2 h-screen flex flex-col items-center justify-end py-[5%];<br>  }<br><br>  &amp;-background {<br>    @apply bg-primary-blue;<br>  }<br><br>  &amp;-illustration {<br>    @apply object-fill w-3/4;<br>  }<br>}<br><br>.heading {<br>  &amp;-layout {<br>    @apply flex flex-col gap-1;<br>  }<br>  &amp;-title {<br>    @apply text-4xl text-slate-700 font-bold;<br>  }<br>  &amp;-subtitle {<br>    @apply text-base font-medium text-gray-400;<br>  }<br>}</pre><h4>Implementing reactive forms for input handling</h4><p>After this, global input techniques will be covered to improve code readability and adhere to the DRY (Don’t Repeat Yourself) principle.</p><p>To handle the inputs, we’ll make reusable components to improve code readability and maintainability. To create these components, run the following commands:</p><pre>ng generate component shared/components/input/text<br>ng generate component shared/components/input/password<br>ng generate component shared/components/captcha</pre><pre>├── app<br>├───features<br>│   └───auth<br>│       └───login<br>│               login.component.html<br>│               login.component.scss<br>│               login.component.spec.ts<br>│               login.component.ts<br>│<br>└───shared<br>    └───components<br>        ├───captcha<br>        │       captcha.component.html<br>        │       captcha.component.scss<br>        │       captcha.component.spec.ts<br>        │       captcha.component.ts<br>        │<br>        └───input<br>            ├───password<br>            │       password.component.html<br>            │       password.component.scss<br>            │       password.component.spec.ts<br>            │       password.component.ts<br>            │<br>            └───text<br>                    text.component.html<br>                    text.component.scss<br>                    text.component.spec.ts<br>                    text.component.ts</pre><p>To create a highly flexible component, the label, placeholder, validation, class, and icon will be made customizable. This will be achieved by creating <strong>bindable properties</strong> using the @Input decorator. For more details, see the Angular documentation:<a href="https://angular.dev/guide/components/inputs">https://angular.dev/guide/components/inputs</a> and <a href="https://angular.dev/api/core/Input">https://angular.dev/api/core/Input</a></p><pre>import { Component, Input } from &#39;@angular/core&#39;;<br><br>@Component({<br>  selector: &#39;global-input-text&#39;,<br>  standalone: true,<br>  imports: [],<br>  templateUrl: &#39;./text.component.html&#39;,<br>  styleUrl: &#39;./text.component.scss&#39;,<br>})<br>export class TextComponent {<br>  @Input({ required: true }) name!: string;<br>  @Input() icon: string = &#39;mail_outline&#39;;<br>  @Input() type: string = &#39;text&#39;;<br>  @Input() label: string = &#39;&#39;;<br>  @Input() placeholder: string = &#39;&#39;;<br>  @Input() required: boolean = false;<br>  @Input() inputClass: string = &#39;&#39;;<br>  @Input() labelClass: string = &#39;w-full form-control&#39;;<br>}</pre><p>Next, we need to register our component so we can access the value of the component inside another component. To do that we can use ControlValueAccessor</p><ul><li><strong>ControlValueAccessor Interface: </strong>The ControlValueAccessor interface is central to making this component work with Angular forms, ensuring that values can be passed between the component and its parent form.</li><li><strong>Providers: </strong><strong>NG_VALUE_ACCESSOR:</strong>Provider register the component as a custom form control</li></ul><p><strong>How It Works</strong></p><p><strong><em>ControlValueAccessor</em></strong> Interface: This interface makes sure the component can act as a form control. It requires implementing three main functions:</p><ul><li><strong>writeValue(value: any): void</strong> This function is called by the parent form when the form control needs to update the value of the input field. It ensures that when the form has a value, it is “written” into the component:</li></ul><pre>registerOnChange(fn: any): void {<br>  this.onChange = fn;<br>}</pre><ul><li><strong>registerOnTouched(fn: any): void</strong>This registers a callback to notify the parent form when the input field is “touched” (i.e., when the user interacts with it but then moves away). It’s useful for showing validation messages when the user finishes interacting with the input:</li></ul><pre>registerOnTouched(fn: any): void {<br>  this.onTouched = fn;<br>}</pre><ul><li>When the user types in the input, this function captures the value and passes it to the parent form control:</li></ul><pre>handleInput(event: Event): void {<br>    const input = event.target as HTMLInputElement;<br>    this.value = input.value;<br>    this.onChange(this.value);<br>    this.onTouched();<br>  }</pre><p><strong><em>Providers: NG_VALUE_ACCESSOR </em></strong>These are added as providers inside the @Component decorator.</p><ul><li><strong>NG_VALUE_ACCESSOR</strong>: This provider registers the TextComponent as a custom form control in Angular’s form system. When Angular sees this component inside a form (for example, with formControlName), it will treat it like a native form control and call the functions like writeValue and registerOnChange. The forwardRef is used to reference the component in the provider declaration before it&#39;s fully defined (to avoid circular dependencies).</li></ul><p><strong>Putting It All Together</strong></p><ul><li><strong>Value Binding</strong>: The writeValue, onChange, and handleInput functions ensure that data flows between the parent form and the component, allowing the input value to be controlled by the form.</li><li><strong>Two-Way Binding</strong>: The parent form control can both set the initial value of the input and listen to changes as the user interacts with the component.</li></ul><h4>Adding form validation</h4><p>After we made custom component, we need to import them inside our login page.</p><pre>//Inside login.ts<br>import { Component, ViewChild } from &#39;@angular/core&#39;;<br>import { CommonModule } from &#39;@angular/common&#39;;<br>import { RouterModule } from &#39;@angular/router&#39;;<br>import { TextComponent } from &#39;@app/shared/components/input/text/text.component&#39;;<br>import { PasswordComponent } from &#39;@app/shared/components/input/password/password.component&#39;;<br>import { CaptchaComponent } from &#39;@app/shared/components/captcha/captcha.component&#39;;<br>import {<br>  FormsModule,<br>  ReactiveFormsModule,<br>  FormGroup,<br>  FormBuilder,<br>  Validators,<br>} from &#39;@angular/forms&#39;;<br><br>@Component({<br>  selector: &#39;app-login&#39;,<br>  standalone: true,<br>  imports: [<br>    CommonModule,<br>    FormsModule,<br>    ReactiveFormsModule,<br>    RouterModule,<br>    TextComponent,<br>    PasswordComponent,<br>    CaptchaComponent,<br>  ],<br>  templateUrl: &#39;./login.component.html&#39;,<br>  styleUrl: &#39;./login.component.scss&#39;,<br>})</pre><pre>//Inside login.html<br>&lt;div class=&quot;flex w-screen h-screen font-display&quot;&gt;<br>  &lt;div class=&quot;form-layout&quot;&gt;<br>    &lt;div class=&quot;heading-layout&quot;&gt;<br>      &lt;h1 class=&quot;heading-title leading&quot;&gt;Sign in here&lt;/h1&gt;<br>      &lt;span class=&quot;subtitle&quot;&gt;<br>        If you don’t have an account register You can<br>        &lt;span class=&quot;font-bold text-blue-800&quot;&gt;Register here !&lt;/span&gt;<br>      &lt;/span&gt;<br>    &lt;/div&gt;<br>    &lt;form<br>      class=&quot;flex flex-col gap-2&quot;<br>    &gt;<br>      &lt;global-input-text<br>        formControlName=&quot;email&quot;<br>        label=&quot;Email or NPP&quot;<br>        placeholder=&quot;Input email or NPP here&quot;<br>        labelClass=&quot;w-full form-control custom-label-class&quot;<br>        inputClass=&quot;w-full cutom-input-class text-md&quot;<br>        type=&quot;email&quot;<br>        [required]=&quot;true&quot;<br>        icon=&quot;mail&quot;<br>      &gt;&lt;/global-input-text&gt;<br>      &lt;button class=&quot;mt-2 text-white btn bg-primary-blue&quot; type=&quot;submit&quot;&gt;<br>        Login<br>      &lt;/button&gt;<br>    &lt;/form&gt;<br>  &lt;/div&gt;<br>  &lt;div class=&quot;image-layout image-background&quot;&gt;<br>    &lt;img<br>      src=&quot;assets/images/illustrasi.svg&quot;<br>      alt=&quot;login-illustration&quot;<br>      class=&quot;image-illustration&quot;<br>    /&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><pre>.image {<br>  &amp;-layout {<br>    @apply w-1/2 h-screen flex flex-col items-center justify-end py-[5%];<br>  }<br><br>  &amp;-background {<br>    @apply bg-primary-blue;<br>  }<br><br>  &amp;-illustration {<br>    @apply object-fill w-3/4;<br>  }<br>}<br><br>.form {<br>  &amp;-layout {<br>    @apply w-1/2 h-screen flex flex-col gap-4 px-[6%] justify-center;<br>  }<br>}<br><br>.heading {<br>  &amp;-layout {<br>    @apply flex flex-col gap-1;<br>  }<br>  &amp;-title {<br>    @apply text-4xl text-slate-700 font-bold;<br>  }<br>  &amp;-subtitle {<br>    @apply text-base font-medium text-gray-400;<br>  }<br>}</pre><p>The page now resembles an actual login page. However, the task isn’t complete. Form validation is necessary to validate user input. For security reasons, it’s important to restrict and validate what users can enter.</p><p>To do that add this code and pass it into our component</p><pre>  getEmailErrorMessage(): string {<br>    const emailControl = this.email;<br>    if (<br>      emailControl &amp;&amp;<br>      (emailControl?.touched || emailControl?.dirty || this.submitted == true)<br>    ) {<br>      if (emailControl.hasError(&#39;required&#39;)) {<br>        return &#39;Email is required.&#39;;<br>      }<br><br>      if (emailControl.hasError(&#39;email&#39;)) {<br>        return &#39;Please enter a valid email address.&#39;;<br>      }<br>    }<br>    return &#39;&#39;;<br>  }</pre><pre>      &lt;global-input-text<br>        formControlName=&quot;email&quot;<br>        label=&quot;Email or NPP&quot;<br>        placeholder=&quot;Input email or NPP here&quot;<br>        labelClass=&quot;w-full form-control custom-label-class&quot;<br>        inputClass=&quot;w-full cutom-input-class text-md&quot;<br>        type=&quot;email&quot;<br>        [required]=&quot;true&quot;<br>        icon=&quot;mail&quot;<br>        [errorMessage]=&quot;getEmailErrorMessage()&quot;<br>      &gt;&lt;/global-input-text&gt;</pre><p>Then inside our component we need to show the error massage that has been passed.</p><pre>  @Input() errorMessage: string = &#39;&#39;;</pre><pre><br>  &lt;span *ngIf=&quot;!!errorMessage&quot; class=&quot;mt-1 text-sm text-red-500&quot;&gt;<br>    {{ errorMessage }}<br>  &lt;/span&gt;</pre><p>Now, we need to make form works properly.</p><pre>import { Component, ViewChild } from &#39;@angular/core&#39;;<br>import { CommonModule } from &#39;@angular/common&#39;;<br>import { RouterModule } from &#39;@angular/router&#39;;<br>import { TextComponent } from &#39;@app/shared/components/input/text/text.component&#39;;<br>import { PasswordComponent } from &#39;@app/shared/components/input/password/password.component&#39;;<br>import { CaptchaComponent } from &#39;@app/shared/components/captcha/captcha.component&#39;;<br>import {<br>  FormsModule,<br>  ReactiveFormsModule,<br>  FormGroup,<br>  FormBuilder,<br>  Validators,<br>} from &#39;@angular/forms&#39;;<br><br>@Component({<br>  selector: &#39;app-login&#39;,<br>  standalone: true,<br>  imports: [<br>    CommonModule,<br>    FormsModule,<br>    ReactiveFormsModule,<br>    RouterModule,<br>    TextComponent,<br>    PasswordComponent,<br>    CaptchaComponent,<br>  ],<br>  templateUrl: &#39;./login.component.html&#39;,<br>  styleUrl: &#39;./login.component.scss&#39;,<br>})<br>export class LoginComponent {<br>  submitted = false;<br>  loginForm: FormGroup;<br><br>  constructor(private fb: FormBuilder) {<br>    this.loginForm = this.fb.group({<br>      email: [&#39;&#39;, [Validators.required, Validators.email]],<br>      password: [&#39;&#39;, [Validators.required]],<br>    });<br>  }<br><br>  @ViewChild(CaptchaComponent) captchaComponent!: CaptchaComponent; <br><br>  get email() {<br>    return this.loginForm.get(&#39;email&#39;);<br>  }<br><br>  get password() {<br>    return this.loginForm.get(&#39;password&#39;);<br>  }<br><br>  getEmailErrorMessage(): string {<br>    const emailControl = this.email;<br>    if (<br>      emailControl &amp;&amp;<br>      (emailControl?.touched || emailControl?.dirty || this.submitted == true)<br>    ) {<br>      if (emailControl.hasError(&#39;required&#39;)) {<br>        return &#39;Email is required.&#39;;<br>      }<br><br>      if (emailControl.hasError(&#39;email&#39;)) {<br>        return &#39;Please enter a valid email address.&#39;;<br>      }<br>    }<br>    return &#39;&#39;;<br>  }<br><br>  getPassErrorMessage(): string {<br>    const passControl = this.password;<br>    if (<br>      passControl &amp;&amp;<br>      (passControl?.touched || passControl?.dirty || this.submitted == true)<br>    ) {<br>      if (passControl.hasError(&#39;required&#39;)) {<br>        return &#39;Password is required.&#39;;<br>      }<br>    }<br>    return &#39;&#39;;<br>  }<br><br>  handleCaptchaVerification(isVerified: boolean) {<br>    if (isVerified) {<br>      console.log(&#39;CAPTCHA is verified!&#39;);<br>    } else {<br>      console.log(&#39;CAPTCHA verification failed.&#39;);<br>    }<br>  }<br><br>  handleVerfitication() {<br>    this.captchaComponent.verifyCaptcha();<br>    this.getEmailErrorMessage();<br>    this.getPassErrorMessage();<br>  }<br><br>  onSubmit() {<br>    this.submitted = true;<br>    this.handleVerfitication();<br>  }<br>}</pre><pre>&lt;div class=&quot;flex w-screen h-screen font-display&quot;&gt;<br>  &lt;div class=&quot;form-layout&quot;&gt;<br>    &lt;div class=&quot;heading-layout&quot;&gt;<br>      &lt;h1 class=&quot;heading-title leading&quot;&gt;Sign in here&lt;/h1&gt;<br>      &lt;span class=&quot;subtitle&quot;&gt;<br>        If you don’t have an account register You can<br>        &lt;span class=&quot;font-bold text-blue-800&quot;&gt;Register here !&lt;/span&gt;<br>      &lt;/span&gt;<br>    &lt;/div&gt;<br>    &lt;form<br>      [formGroup]=&quot;loginForm&quot;<br>      (ngSubmit)=&quot;onSubmit()&quot;<br>      class=&quot;flex flex-col gap-2&quot;<br>    &gt;<br>      &lt;global-input-text<br>        formControlName=&quot;email&quot;<br>        label=&quot;Email or NPP&quot;<br>        placeholder=&quot;Input email or NPP here&quot;<br>        labelClass=&quot;w-full form-control custom-label-class&quot;<br>        inputClass=&quot;w-full cutom-input-class text-md&quot;<br>        type=&quot;email&quot;<br>        [required]=&quot;true&quot;<br>        icon=&quot;mail&quot;<br>        [errorMessage]=&quot;getEmailErrorMessage()&quot;<br>      &gt;&lt;/global-input-text&gt;<br>      &lt;global-input-password<br>        formControlName=&quot;password&quot;<br>        label=&quot;Password&quot;<br>        placeholder=&quot;Input password &quot;<br>        labelClass=&quot;w-full form-control custom-label-class&quot;<br>        inputClass=&quot;w-full custom-input-class text-md&quot;<br>        type=&quot;password&quot;<br>        [required]=&quot;true&quot;<br>        icon=&quot;lock&quot;<br>        [errorMessage]=&quot;getPassErrorMessage()&quot;<br>      &gt;<br>      &lt;/global-input-password&gt;<br>      &lt;global-captcha<br>        label=&quot;Captcha&quot;<br>        placeholder=&quot;e.g.MUHSH&quot;<br>        labelClass=&quot;w-full form-control custom-label-class text-md&quot;<br>        inputClass=&quot;w-full custom-input-class text-md&quot;<br>        [required]=&quot;true&quot;<br>        icon=&quot;new_releases_outline&quot;<br>      &gt;&lt;/global-captcha&gt;<br>      &lt;button class=&quot;mt-2 text-white btn bg-primary-blue&quot; type=&quot;submit&quot;&gt;<br>        Login<br>      &lt;/button&gt;<br>    &lt;/form&gt;<br>  &lt;/div&gt;<br>  &lt;div class=&quot;image-layout image-background&quot;&gt;<br>    &lt;img<br>      src=&quot;assets/images/illustrasi.svg&quot;<br>      alt=&quot;login-illustration&quot;<br>      class=&quot;image-illustration&quot;<br>    /&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p><strong>Understanding </strong><strong>formControlName and Form Groups</strong></p><p>In Angular Reactive Forms, the form is managed by a FormGroup, which holds multiple FormControl instances (for each input field, like email, password, etc.).</p><p>Each form control in the FormGroup is uniquely identified by a name (e.g., email and password in your case). The formControlName directive is used to bind a specific field in the form (i.e., a FormControl) to a specific form input component.</p><p><strong>How </strong><strong>formControlName Connects the Form and Component</strong></p><p>When you use formControlName=&quot;email&quot; inside your form, Angular links this directive to the corresponding FormControl in the FormGroup. So, when the formControlName=&quot;email&quot; is applied to a custom component (like your TextComponent), Angular expects that this component can interact with the form just like a native &lt;input&gt; would.</p><p><strong>The Role of </strong><strong>ControlValueAccessor</strong></p><p>In a native HTML &lt;input&gt; field, Angular can directly read and write values using standard DOM methods (value, input events, etc.). However, for a custom component like your TextComponent, Angular needs to know how to interact with it—that&#39;s where ControlValueAccessor comes in.</p><p>By implementing ControlValueAccessor, your custom component tells Angular how to:</p><ul><li><strong>Set its value</strong>: When the parent form changes, the writeValue() method in your component is called with the new value.</li><li><strong>Get its value</strong>: When the user types something, the onChange callback function gets triggered, and Angular updates the form control’s value.</li><li><strong>Track its touched state</strong>: registerOnTouched is used to let Angular know when the user interacts with the form control, which is important for handling validation.</li></ul><p>In summary….</p><ul><li>When we use formControlName=&quot;email&quot;, we bind the email form control in your FormGroup to the TextComponent.</li><li>The TextComponent behaves like a standard form control because it implements ControlValueAccessor.</li><li>Whenever the user types in the TextComponent, it updates the corresponding form control (email or password) in the parent component’s form (loginForm).</li><li>You can access or manipulate the form values in the parent component just like you would with a regular HTML form input, using methods like this.loginForm.get(&#39;email&#39;).value.</li></ul><h4>Closing</h4><p>And that wraps up today’s tutorial! We still have some exciting things to cover, like setting up protected routes, handling HTTP requests, and diving into testing — but we’ll save those for the next episode. If you have any questions or feedback, feel free to drop them in the comments below. Thanks for tuning in, and I’ll catch you in the next one. Until then, take care and happy coding! 👋</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=946392adbbd6" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>