Komponen Sudut 101 — Tinjauan

Diterbitkan: 2022-03-11

Komponen telah tersedia di Angular sejak awal; namun, banyak orang masih menemukan diri mereka menggunakan komponen secara tidak benar. Dalam pekerjaan saya, saya telah melihat orang tidak menggunakannya sama sekali, membuat komponen alih-alih arahan atribut, dan banyak lagi. Ini adalah masalah yang cenderung dibuat oleh pengembang Angular junior dan senior, termasuk saya sendiri. Jadi, posting ini untuk orang-orang seperti saya ketika saya belajar Angular, karena baik dokumentasi resmi maupun dokumentasi tidak resmi tentang komponen menjelaskan dengan kasus penggunaan bagaimana dan kapan menggunakan komponen.

Pada artikel ini, saya akan mengidentifikasi cara yang benar dan salah untuk menggunakan komponen Angular, dengan contoh. Posting ini akan memberi Anda gambaran yang jelas tentang:

  • Definisi komponen Angular;
  • Kapan Anda harus membuat komponen Angular terpisah; dan
  • Ketika Anda tidak harus membuat komponen Angular yang terpisah.

Gambar sampul untuk komponen Angular

Sebelum kita dapat beralih ke penggunaan komponen Angular yang benar, saya ingin secara singkat menyentuh topik komponen secara umum. Secara umum, setiap aplikasi Angular memiliki setidaknya satu komponen secara default— root component . Dari situ terserah kita bagaimana mendesain aplikasi kita. Biasanya, Anda akan membuat komponen untuk halaman terpisah, dan kemudian setiap halaman akan menyimpan daftar komponen terpisah. Sebagai aturan praktis, komponen harus memenuhi kriteria berikut:

  • Harus memiliki kelas yang ditentukan, yang menyimpan data dan logika; dan
  • Harus dikaitkan dengan template HTML yang menampilkan informasi untuk pengguna akhir.

Mari kita bayangkan skenario di mana kita memiliki aplikasi yang memiliki dua halaman: Tugas yang akan datang dan Tugas yang telah diselesaikan . Di halaman Tugas yang akan datang, kita dapat melihat tugas yang akan datang, menandainya sebagai "selesai," dan akhirnya menambahkan tugas baru. Demikian pula, di halaman Tugas yang diselesaikan , kita dapat melihat tugas yang telah diselesaikan dan menandainya sebagai “dibatalkan”. Terakhir, kami memiliki tautan navigasi, yang memungkinkan kami bernavigasi antar halaman. Karena itu, kita dapat membagi halaman berikut menjadi tiga bagian: komponen root, halaman, komponen yang dapat digunakan kembali.

Diagram tugas yang akan datang dan tugas yang diselesaikan

Contoh gambar rangka aplikasi dengan bagian komponen terpisah yang diwarnai

Berdasarkan tangkapan layar di atas, kita dapat dengan jelas melihat bahwa struktur aplikasi akan terlihat seperti ini:

 ── myTasksApplication ├── components │ ├── header-menu.component.ts │ ├── header-menu.component.html │ ├── task-list.component.ts │ └── task-list.component.html ├── pages │ ├── upcoming-tasks.component.ts │ ├── upcoming-tasks.component.html │ ├── completed-tasks.component.ts │ └── completed-tasks.component.html ├── app.component.ts └── app.component.html

Jadi mari kita tautkan file komponen dengan elemen aktual pada gambar rangka di atas:

  • header-menu.component dan task-list.component adalah komponen yang dapat digunakan kembali, yang ditampilkan dengan batas hijau di tangkapan layar wireframe;
  • upcoming-tasks.component dan completed-tasks.component adalah halaman, yang ditampilkan dengan batas kuning pada gambar rangka gambar di atas; dan
  • Terakhir, app.component adalah komponen root, yang ditampilkan dengan batas merah di tangkapan layar wireframe.

Jadi, kita dapat menentukan logika dan desain terpisah untuk setiap komponen. Berdasarkan gambar rangka di atas, kami memiliki dua halaman yang menggunakan kembali satu komponen— task-list.component . Pertanyaan yang akan muncul—bagaimana kita menentukan jenis data apa yang harus kita tampilkan pada halaman tertentu? Untungnya, kita tidak perlu khawatir tentang itu, karena ketika Anda membuat sebuah komponen, Anda juga dapat menentukan variabel Input dan Output .

Variabel Masukan

Variabel input digunakan di dalam komponen untuk meneruskan beberapa data dari komponen induk. Dalam contoh di atas, kita dapat memiliki dua parameter input untuk task-list.componenttasks dan listType . Dengan demikian, tasks akan menjadi daftar string yang akan menampilkan setiap string pada baris terpisah, sedangkan listType akan menjadi mendatang atau selesai , yang akan menunjukkan jika kotak centang dicentang. Di bawah ini, Anda dapat menemukan cuplikan kode kecil tentang bagaimana komponen sebenarnya dapat terlihat.

 // task-list.component.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'app-task-list', templateUrl: 'task-list.component.html' }) export class TaskListComponent { @Input() tasks: string[] = []; // List of tasks which should be displayed. @Input() listType: 'upcoming' | 'completed' = 'upcoming'; // Type of the task list. constructor() { } }

Variabel Keluaran

Mirip dengan variabel input, variabel output juga dapat digunakan untuk melewatkan beberapa informasi antar komponen, tetapi kali ini ke komponen induk. Misalnya, untuk task-list.component , kita dapat memiliki variabel output itemChecked . Ini akan memberi tahu komponen induk jika suatu item telah dicentang atau tidak dicentang. Variabel keluaran harus merupakan penghasil peristiwa. Di bawah ini, Anda dapat menemukan cuplikan kode kecil tentang bagaimana komponen dapat terlihat dengan variabel keluaran.

 // task-list.component.ts import { Component, Input, Output } from '@angular/core'; @Component({ selector: 'app-task-list', templateUrl: 'task-list.component.html' }) export class TaskListComponent { @Input() tasks: string[] = []; // List of tasks which should be displayed. @Input() listType: 'upcoming' | 'completed' = 'upcoming'; // Type of the task list. @Output() itemChecked: EventEmitter<boolean> = new EventEmitter(); @Output() tasksChange: EventEmitter<string[]> = new EventEmitter(); constructor() { } /** * Is called when an item from the list is checked. * @param selected---Value which indicates if the item is selected or deselected. */ onItemCheck(selected: boolean) { this.itemChecked.emit(selected); } /** * Is called when task list is changed. * @param changedTasks---Changed task list value, which should be sent to the parent component. */ onTasksChanged(changedTasks: string[]) { this.taskChange.emit(changedTasks); } }
Kemungkinan konten task-list.component.ts setelah menambahkan variabel output

Penggunaan Komponen Anak dan Pengikatan Variabel

Mari kita lihat bagaimana menggunakan komponen ini di dalam komponen induk dan bagaimana melakukan berbagai jenis pengikatan variabel. Di Angular, ada dua cara untuk mengikat variabel input—pengikatan satu arah, yang berarti bahwa properti harus dibungkus dalam tanda kurung siku [] , dan pengikatan dua arah, yang berarti bahwa properti harus dibungkus dalam tanda kurung siku dan kurung bulat. [()] . Lihat contoh di bawah ini dan lihat berbagai cara di mana data dapat diteruskan antar komponen.

 <h1>Upcoming Tasks</h1> <app-task-list [(tasks)]="upcomingTasks" [listType]="'upcoming'" (itemChecked)="onItemChecked($event)"></app-task-list>
Kemungkinan konten upcoming-tasks.component.html

Mari kita lihat setiap parameter:

  • Parameter tasks dilewatkan menggunakan pengikatan dua arah. Artinya, jika parameter tugas diubah dalam komponen turunan, komponen induk akan mencerminkan perubahan tersebut pada variabel upcomingTasks . Untuk mengizinkan pengikatan data dua arah, Anda harus membuat parameter output, yang mengikuti template “[inputParameterName]Change”, dalam hal ini tasksChange .
  • Parameter listType diteruskan menggunakan pengikatan satu arah. Itu berarti dapat diubah dalam komponen anak, tetapi tidak akan tercermin dalam komponen induk. Ingatlah bahwa saya dapat menetapkan nilai 'upcoming' ke parameter di dalam komponen dan meneruskannya sebagai gantinya—tidak ada bedanya.
  • Terakhir, parameter itemChecked adalah fungsi listener, dan akan dipanggil setiap kali onItemCheck dijalankan pada task-list.component . Jika item dicentang, $event akan menyimpan nilai true , tetapi, jika tidak dicentang, itu akan menyimpan nilai false .

Seperti yang Anda lihat, secara umum, Angular menyediakan cara yang bagus untuk meneruskan dan berbagi informasi di antara banyak komponen, jadi Anda tidak perlu takut untuk menggunakannya. Pastikan untuk menggunakannya dengan bijak dan tidak menggunakannya secara berlebihan.

Kapan Membuat Komponen Sudut Terpisah

Seperti yang disebutkan sebelumnya, Anda tidak perlu takut untuk menggunakan komponen Angular, tetapi Anda harus menggunakannya dengan bijak.

Diagram komponen Sudut beraksi

Manfaatkan komponen Angular dengan bijak

Jadi kapan Anda harus membuat komponen Angular?

  • Anda harus selalu membuat komponen terpisah jika komponen tersebut dapat digunakan kembali di banyak tempat, seperti dengan task-list.component kami. Kami menyebutnya komponen yang dapat digunakan kembali .
  • Anda harus mempertimbangkan pembuatan komponen terpisah jika komponen tersebut akan membuat komponen induk lebih mudah dibaca dan memungkinkan mereka menambahkan cakupan pengujian tambahan. Kita dapat menyebutnya sebagai komponen organisasi kode .
  • Anda harus selalu membuat komponen terpisah jika Anda memiliki bagian halaman yang tidak perlu sering diperbarui dan ingin meningkatkan kinerja. Hal ini terkait dengan strategi deteksi perubahan. Kita bisa menyebutnya komponen optimasi .

Ketiga aturan ini membantu saya mengidentifikasi apakah saya perlu membuat komponen baru, dan mereka secara otomatis memberi saya visi yang jelas tentang peran komponen tersebut. Idealnya, ketika Anda membuat sebuah komponen, Anda seharusnya sudah mengetahui apa perannya di dalam aplikasi.

Karena kita telah melihat penggunaan komponen yang dapat digunakan kembali , mari kita lihat penggunaan komponen organisasi kode . Mari kita bayangkan bahwa kita memiliki formulir pendaftaran dan, di bagian bawah formulir, kita memiliki kotak dengan Syarat dan Ketentuan . Biasanya, legalese cenderung sangat besar, memakan banyak ruang—dalam hal ini, dalam template HTML. Jadi, mari kita lihat contoh ini dan kemudian lihat bagaimana kita berpotensi mengubahnya.

Pada awalnya, kami memiliki satu komponen— registration.component —yang menampung semuanya, termasuk formulir pendaftaran serta syarat dan ketentuan itu sendiri.

 <h2>Registration</h2> <label for="username">Username</label><br /> <input type="text" name="username" [(ngModel)]="username" /><br /> <label for="password">Password</label><br /> <input type="password" name="password" [(ngModel)]="password" /><br /> <div class="terms-and-conditions-box"> Text with very long terms and conditions. </div> <button (click)="onRegistrate()">Registrate</button>
Status awal sebelum memisahkan </code>registration.component</code> menjadi beberapa komponen

Template sekarang terlihat kecil, tetapi bayangkan jika kita mengganti "Teks dengan syarat dan ketentuan yang sangat panjang" dengan teks sebenarnya yang memiliki lebih dari 1000 kata—itu akan membuat pengeditan file menjadi sulit dan tidak nyaman. Kami memiliki solusi cepat untuk itu—kami dapat menciptakan komponen baru terms-and-conditions.component . komponen , yang akan menampung semua yang terkait dengan syarat dan ketentuan. Jadi mari kita lihat file HTML untuk terms-and-conditions.component .

 <div class="terms-and-conditions-box"> Text with very long terms and conditions. </div>
Template HTML </code>terms-and-conditions.component</code> yang baru dibuat

Dan sekarang kita dapat menyesuaikan registration.component dan menggunakan terms-and-conditions.component di dalamnya.

 <h2>Registration</h2> <label for="username">Username</label><br /> <input type="text" name="username" [(ngModel)]="username" /><br /> <label for="password">Password</label><br /> <input type="password" name="password" [(ngModel)]="password" /><br /> <app-terms-and-conditions></app-terms-and-conditions> <button (click)="onRegistrate()">Registrate</button>
Template </code>registration.component.ts</code> yang diperbarui dengan komponen organisasi kode

Selamat! Kami baru saja mengecilkan ukuran registration.component sebanyak ratusan baris dan membuatnya lebih mudah untuk membaca kodenya. Dalam contoh di atas, kami membuat perubahan pada template komponen, tetapi prinsip yang sama dapat diterapkan pada logika komponen.

Terakhir, untuk komponen pengoptimalan , saya sangat menyarankan agar Anda membaca posting ini, karena ini akan memberi Anda semua informasi yang diperlukan untuk memahami deteksi perubahan, dan pada kasus spesifik mana Anda dapat menerapkannya. Anda tidak akan sering menggunakannya, tetapi mungkin ada beberapa kasus, dan jika Anda dapat melewati pemeriksaan rutin pada beberapa komponen saat tidak diperlukan, ini adalah win-win untuk kinerja.

Karena itu, kita tidak harus selalu membuat komponen terpisah, jadi mari kita lihat kapan Anda harus menghindari membuat komponen terpisah.

Kapan Harus Menghindari Pembuatan Komponen Sudut Terpisah

Ada tiga poin utama yang saya sarankan untuk membuat komponen Angular terpisah, tetapi ada beberapa kasus di mana saya juga akan menghindari membuat komponen terpisah.

Ilustrasi terlalu banyak komponen

Terlalu banyak komponen akan memperlambat Anda.

Sekali lagi, mari kita lihat poin-poin yang akan memungkinkan Anda untuk dengan mudah memahami kapan Anda seharusnya tidak membuat komponen terpisah.

  • Anda tidak boleh membuat komponen untuk manipulasi DOM. Untuk itu, Anda harus menggunakan arahan atribut.
  • Anda tidak boleh membuat komponen jika itu akan membuat kode Anda lebih kacau. Ini adalah kebalikan dari komponen organisasi kode .

Sekarang, mari kita lihat lebih dekat dan lihat kedua kasus dalam contoh. Mari kita bayangkan bahwa kita ingin memiliki tombol yang mencatat pesan saat diklik. Ini bisa salah dan komponen terpisah dapat dibuat untuk fungsi khusus ini, yang akan menahan tombol dan tindakan tertentu. Pertama-tama mari kita periksa pendekatan yang salah:

 // log-button.component.ts import { Component, Input, Output } from '@angular/core'; @Component({ selector: 'app-log-button', templateUrl: 'log-button.component.html' }) export class LogButtonComponent { @Input() name: string; // Name of the button. @Output() buttonClicked: EventEmitter<boolean> = new EventEmitter(); constructor() { } /** * Is called when button is clicked. * @param clicked - Value which indicates if the button was clicked. */ onButtonClick(clicked: boolean) { console.log('I just clicked a button on this website'); this.buttonClicked.emit(clicked); } }
Logika komponen yang salah log-button.component.ts

Dan karenanya, komponen ini disertai dengan tampilan html berikut.

 <button (click)="onButtonClick(true)">{{ name }}</button>
Templat komponen yang salah log-button.component.html

Seperti yang Anda lihat, contoh di atas akan berfungsi, dan Anda dapat menggunakan komponen di atas di seluruh tampilan Anda. Itu akan melakukan apa yang Anda inginkan, secara teknis. Tetapi solusi yang tepat di sini adalah dengan menggunakan arahan. Itu akan memungkinkan Anda tidak hanya mengurangi jumlah kode yang harus Anda tulis tetapi juga menambahkan kemungkinan untuk menerapkan fungsi ini ke elemen apa pun yang Anda inginkan, bukan hanya tombol.

 import { Directive } from '@angular/core'; @Directive({ selector: "[logButton]", hostListeners: { 'click': 'onButtonClick()', }, }) class LogButton { constructor() {} /** * Fired when element is clicked. */ onButtonClick() { console.log('I just clicked a button on this website'); } }
direktif logButton , yang dapat ditetapkan ke elemen apa pun

Sekarang ketika kita telah membuat arahan kita, kita cukup menggunakannya di seluruh aplikasi kita dan menetapkannya ke elemen apa pun yang kita inginkan. Misalnya, mari kita gunakan kembali registration.component kita.

 <h2>Registration</h2> <label for="username">Username</label><br /> <input type="text" name="username" [(ngModel)]="username" /><br /> <label for="password">Password</label><br /> <input type="password" name="password" [(ngModel)]="password" /><br /> <app-terms-and-conditions></app-terms-and-conditions> <button (click)="onRegistrate()" logButton>Registrate</button>
direktif logButton yang digunakan pada tombol formulir pendaftaran

Sekarang, kita dapat melihat kasus kedua, di mana kita tidak boleh membuat komponen terpisah, dan itu adalah kebalikan dari komponen pengoptimalan kode . Jika komponen yang baru dibuat membuat kode Anda lebih rumit dan lebih besar, tidak perlu membuatnya. Mari kita ambil contoh registration.component kita. Salah satu kasus tersebut adalah membuat komponen terpisah untuk label dan bidang input dengan banyak parameter input. Mari kita lihat praktik buruk ini.

 // form-input-with-label.component.ts import { Component, Input} from '@angular/core'; @Component({ selector: 'app-form-input-with-label', templateUrl: 'form-input-with-label.component.html' }) export class FormInputWithLabelComponent { @Input() name: string; // Name of the field @Input() id: string; // Id of the field @Input() label: string; // Label of the field @Input() type: 'text' | 'password'; // Type of the field @Input() model: any; // Model of the field constructor() { } }
Logika form-input-with-label.component

Dan ini bisa menjadi tampilan untuk komponen ini.

 <label for="{{ id }}">{{ label }}</label><br /> <input type="{{ type }}" name="{{ name }}" [(ngModel)]="model" /><br />
Tampilan form-input-with-label.component

Tentu, jumlah kode akan berkurang di registration.component , tetapi apakah itu membuat keseluruhan logika kode lebih mudah dipahami dan dibaca? Saya pikir kita dapat melihat dengan jelas bahwa itu membuat kode menjadi lebih kompleks daripada sebelumnya.

Langkah Selanjutnya: Komponen Sudut 102?

Untuk meringkas: Jangan takut untuk menggunakan komponen; pastikan Anda memiliki visi yang jelas tentang apa yang ingin Anda capai. Skenario yang saya sebutkan di atas adalah yang paling umum, dan saya menganggapnya sebagai yang paling penting dan umum; namun, skenario Anda mungkin unik dan terserah Anda untuk membuat keputusan yang tepat. Saya harap Anda sudah cukup belajar untuk membuat keputusan yang baik.

Jika Anda ingin mempelajari lebih lanjut tentang strategi deteksi perubahan Angular dan strategi OnPush, saya sarankan membaca Deteksi Perubahan Sudut dan Strategi OnPush. Ini terkait erat dengan komponen dan, seperti yang telah saya sebutkan di posting, ini dapat meningkatkan kinerja aplikasi secara signifikan.

Karena komponen hanya bagian dari arahan yang disediakan Angular, akan sangat bagus juga untuk mengetahui arahan atribut dan arahan struktural . Memahami semua arahan kemungkinan besar akan memudahkan programmer untuk menulis kode yang lebih baik secara bersamaan.