Komponen Sudut 101 — Tinjauan
Diterbitkan: 2022-03-11Komponen 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.
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.
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
dantask-list.component
adalah komponen yang dapat digunakan kembali, yang ditampilkan dengan batas hijau di tangkapan layar wireframe; -
upcoming-tasks.component
dancompleted-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.component
— tasks
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); } }
task-list.component.ts
setelah menambahkan variabel outputPenggunaan 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>
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 variabelupcomingTasks
. Untuk mengizinkan pengikatan data dua arah, Anda harus membuat parameter output, yang mengikuti template “[inputParameterName]Change”, dalam hal initasksChange
. - 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 padatask-list.component
. Jika item dicentang,$event
akan menyimpan nilaitrue
, tetapi, jika tidak dicentang, itu akan menyimpan nilaifalse
.
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.
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>
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>
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>
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.
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); } }
log-button.component.ts
Dan karenanya, komponen ini disertai dengan tampilan html berikut.
<button (click)="onButtonClick(true)">{{ name }}</button>
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'); } }
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>
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() { } }
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 />
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.