Açısal Bileşenler 101 — Genel Bakış
Yayınlanan: 2022-03-11Bileşenler en başından beri Angular'da mevcuttur; ancak, birçok kişi hala bileşenleri yanlış kullanıyor. Çalışmamda, onları hiç kullanmayan, nitelik yönergeleri yerine bileşenler oluşturan ve daha fazlasını yapan insanlar gördüm. Bunlar, ben de dahil olmak üzere, hem genç hem de kıdemli Angular geliştiricilerinin yapma eğiliminde olduğu sorunlardır. Ve bu nedenle, bu gönderi, Angular'ı öğrenirken benim gibi insanlar içindir, çünkü ne resmi belgeler ne de bileşenlerle ilgili resmi olmayan belgeler, bileşenlerin nasıl ve ne zaman kullanılacağını kullanım durumları ile açıklamaz.
Bu yazıda Angular bileşenlerini kullanmanın doğru ve yanlış yollarını örneklerle belirleyeceğim. Bu gönderi size aşağıdakiler hakkında net bir fikir vermelidir:
- Açısal bileşenlerin tanımı;
- Ayrı Angular bileşenleri oluşturmanız gerektiğinde ; ve
- Ayrı Angular bileşenleri oluşturmamanız gerektiğinde .
Angular bileşenlerinin doğru kullanımına geçmeden önce, genel olarak bileşen konusuna kısaca değinmek istiyorum. Genel olarak konuşursak, her Angular uygulamasının varsayılan olarak en az bir bileşeni vardır: root component
. Oradan, uygulamamızı nasıl tasarlayacağımız bize kalmış. Genellikle, ayrı sayfalar için bir bileşen oluşturursunuz ve ardından her sayfada ayrı bileşenlerin bir listesi bulunur. Genel bir kural olarak, bir bileşen aşağıdaki kriterleri karşılamalıdır:
- Veri ve mantığı tutan bir sınıf tanımlı olmalıdır; ve
- Son kullanıcı için bilgileri görüntüleyen bir HTML şablonuyla ilişkilendirilmelidir.
İki sayfadan oluşan bir uygulamamız olduğu bir senaryo düşünelim: Yaklaşan görevler ve Tamamlanan görevler . Yaklaşan görevler sayfasında, yaklaşan görevleri görüntüleyebilir, onları "bitti" olarak işaretleyebilir ve son olarak yeni görevler ekleyebiliriz. Benzer şekilde, Tamamlanan görevler sayfasında tamamlanan görevleri görüntüleyebilir ve bunları "geri alınmadı" olarak işaretleyebiliriz. Son olarak, sayfalar arasında gezinmemizi sağlayan gezinme bağlantılarımız var. Bununla birlikte, aşağıdaki sayfayı üç bölüme ayırabiliriz: kök bileşen, sayfalar, yeniden kullanılabilir bileşenler.
Yukarıdaki ekran görüntüsüne dayanarak, uygulamanın yapısının şöyle görüneceğini açıkça görebiliriz:
── 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
Öyleyse, bileşen dosyalarını yukarıdaki tel çerçevedeki gerçek öğelerle bağlayalım:
-
header-menu.component
vetask-list.component
, tel kafes ekran görüntüsünde yeşil bir kenarlıkla görüntülenen yeniden kullanılabilir bileşenlerdir; -
upcoming-tasks.component
vecompleted-tasks.component
, yukarıdaki tel kafes ekran görüntüsünde sarı bir kenarlıkla görüntülenen sayfalardır; ve - Son olarak,
app.component
, tel kafes ekran görüntüsünde kırmızı bir kenarlıkla görüntülenen kök bileşendir.
Bununla birlikte, her bileşen için ayrı bir mantık ve tasarım belirleyebiliriz. Yukarıdaki tel çerçevelere dayanarak, bir bileşeni yeniden kullanan iki sayfamız var task-list.component
. Soru ortaya çıkar: belirli bir sayfada ne tür verileri göstermemiz gerektiğini nasıl belirleriz? Neyse ki, bunun için endişelenmemize gerek yok, çünkü bir bileşen oluşturduğunuzda, Girdi ve Çıktı değişkenlerini de belirtebilirsiniz.
Giriş Değişkenleri
Giriş değişkenleri, ana bileşenden bazı verileri iletmek için bileşenler içinde kullanılır. Yukarıdaki örnekte, task-list.component
için iki giriş parametresine sahip olabiliriz - tasks
ve listType
. Buna göre, tasks
, her bir dizeyi ayrı bir satırda görüntüleyen bir dize listesi olurken, listType
, onay kutusunun işaretli olup olmadığını gösteren ya yaklaşan ya da tamamlanan olacaktır. Aşağıda, gerçek bileşenin nasıl görünebileceğine dair küçük bir kod parçacığı bulabilirsiniz.
// 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() { } }
Çıkış Değişkenleri
Girdi değişkenlerine benzer şekilde, çıktı değişkenleri de bileşenler arasında bazı bilgileri aktarmak için kullanılabilir, ancak bu sefer ana bileşene. Örneğin, task-list.component
için itemChecked
çıktı değişkenine sahip olabiliriz. Bir öğenin kontrol edilip edilmediğini veya işaretlenmemişse, ana bileşene bilgi verir. Çıkış değişkenleri olay yayıcılar olmalıdır. Aşağıda, bileşenin bir çıktı değişkeni ile nasıl görünebileceğine dair küçük bir kod parçacığı bulabilirsiniz.
// 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
içeriğiAlt Bileşen Kullanımı ve Değişken Bağlama
Bu bileşenin ana bileşen içinde nasıl kullanılacağına ve farklı türde değişken bağlamalarının nasıl yapılacağına bir göz atalım. Angular'da, girdi değişkenlerini bağlamanın iki yolu vardır: özelliğin köşeli parantez []
içine alınması gerektiği anlamına gelen tek yönlü ciltleme ve özelliğin köşeli ve yuvarlak parantez içine alınması gerektiği anlamına gelen iki yönlü ciltleme [()]
. Aşağıdaki örneğe bir göz atın ve verilerin bileşenler arasında iletilmesinin farklı yollarını görün.
<h1>Upcoming Tasks</h1> <app-task-list [(tasks)]="upcomingTasks" [listType]="'upcoming'" (itemChecked)="onItemChecked($event)"></app-task-list>
upcoming-tasks.component.html
içeriğiHer parametreyi inceleyelim:
-
tasks
parametresi, iki yönlü bağlama kullanılarak geçirilir. Bu, alt bileşen içinde görevler parametresinin değiştirilmesi durumunda, üst bileşenin bu değişiklikleriupcomingTasks
değişkenine yansıtacağı anlamına gelir. İki yönlü veri bağlamaya izin vermek için, "[inputParameterName]Change" şablonunu izleyen bir çıktı parametresi oluşturmanız gerekir, bu durumdatasksChange
. -
listType
parametresi, tek yönlü bağlama kullanılarak geçirilir. Bu, alt bileşen içinde değiştirilebileceği, ancak ana bileşene yansıtılmayacağı anlamına gelir. Bileşen içindeki bir parametreye'upcoming'
değerini atamış olabileceğimi ve bunun yerine bunu iletebileceğimi unutmayın; bu hiçbir fark yaratmaz. - Son olarak,
itemChecked
parametresi bir dinleyici işlevidir vetask-list.component
üzerinde onItemCheck yürütüldüğünde çağrılacaktır. Bir öğe işaretlenirse,$event
true
değerini tutar, ancak işaretlenmemişsefalse
değerini tutar.
Gördüğünüz gibi, genel olarak Angular, birden çok bileşen arasında bilgi aktarmak ve paylaşmak için harika bir yol sağlar, bu nedenle bunları kullanmaktan korkmamalısınız. Sadece onları akıllıca kullandığınızdan ve aşırı kullanmadığınızdan emin olun.
Ayrı Açısal Bileşen Ne Zaman Oluşturulur?
Daha önce de belirtildiği gibi, Angular bileşenlerini kullanmaktan korkmamalısınız, ancak bunları kesinlikle akıllıca kullanmalısınız.
Peki ne zaman Angular bileşenleri oluşturmalısınız?
- Bileşen,
task-list.component
gibi birden çok yerde yeniden kullanılabiliyorsa, her zaman ayrı bir bileşen oluşturmalısınız. Biz bunlara yeniden kullanılabilir bileşenler diyoruz. - Bileşen, ana bileşeni daha okunabilir hale getirecek ve ek test kapsamı eklemelerine izin verecekse, ayrı bir bileşen oluşturmayı düşünmelisiniz . Bunlara kod organizasyon bileşenleri diyebiliriz.
- Sayfanın sık güncellenmesi gerekmeyen ve performansı artırmak isteyen bir bölümü varsa , her zaman ayrı bir bileşen oluşturmalısınız. Bu, değişiklik algılama stratejisiyle ilgilidir. Bunlara optimizasyon bileşenleri diyebiliriz.
Bu üç kural, yeni bir bileşen oluşturmam gerekip gerekmediğini belirlememe yardımcı oluyor ve otomatik olarak bileşenin rolüne ilişkin net bir görüş sağlıyor. İdeal olarak, bir bileşen oluşturduğunuzda, uygulama içindeki rolünün ne olacağını zaten bilmelisiniz.

Yeniden kullanılabilir bileşenlerin kullanımına zaten bir göz attığımız için, kod düzenleme bileşenlerinin kullanımına bir göz atalım. Diyelim ki bir kayıt formumuz var ve formun altında Şartlar ve Koşullar içeren bir kutumuz var. Genellikle, hukuk dili çok büyük olma eğilimindedir ve çok yer kaplar - bu durumda, bir HTML şablonunda. Şimdi bu örneğe bakalım ve sonra onu potansiyel olarak nasıl değiştirebileceğimizi görelim.
Başlangıçta, kayıt formunun yanı sıra şartlar ve koşulların kendisi de dahil olmak üzere her şeyi içeren bir bileşenimiz var: registration.component
.
<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>
Şablon artık küçük görünüyor, ancak "Çok uzun şartlar ve koşullar içeren metni" 1000'den fazla kelimeden oluşan gerçek bir metin parçasıyla değiştirdiğimizi hayal edin - bu, dosyanın düzenlenmesini zor ve rahatsız edici hale getirecektir. Bunun için hızlı bir çözümümüz var—şartlar ve koşullarla ilgili her şeyi içeren yeni bir terms-and-conditions.component
bileşeni icat edebiliriz. Öyleyse, terms-and-conditions.component
için HTML dosyasına bir göz atalım.
<div class="terms-and-conditions-box"> Text with very long terms and conditions. </div>
Ve şimdi registration.component
ayarlayabilir ve içindeki terms-and-conditions.component
kullanabiliriz.
<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>
Tebrikler! Sadece registration.component
boyutunu yüzlerce satır azalttık ve kodun okunmasını daha kolay hale getirdik. Yukarıdaki örnekte, bileşenin şablonunda değişiklikler yaptık, ancak aynı ilke bileşenin mantığına da uygulanabilir.
Son olarak, optimizasyon bileşenleri için, bu gönderiyi gözden geçirmenizi şiddetle tavsiye ederim, çünkü size değişiklik tespitini anlamak için gereken tüm bilgileri ve bunu hangi özel durumlara uygulayabileceğinizi sağlayacaktır. Sık kullanmayacaksınız, ancak bazı durumlar olabilir ve gerekli olmadığında birden fazla bileşen üzerinde düzenli kontrolleri atlayabilirseniz, performans için bir kazan-kazan olur.
Bununla birlikte, her zaman ayrı bileşenler oluşturmamalıyız, bu yüzden ayrı bir bileşen oluşturmaktan ne zaman kaçınmanız gerektiğine bir bakalım.
Ayrı Açısal Bileşen Oluşturulmasından Ne Zaman Kaçınılmalı?
Ayrı bir Açısal bileşen oluşturmayı önerdiğim üç ana nokta var, ancak ayrı bir bileşen oluşturmaktan kaçındığım durumlar da var.
Yine, ayrı bir bileşen oluşturmamanız gerektiğini kolayca anlamanızı sağlayacak madde işaretlerine bir göz atalım.
- DOM manipülasyonları için bileşenler oluşturmamalısınız. Bunlar için öznitelik yönergelerini kullanmalısınız.
- Kodunuzu daha kaotik hale getirecekse bileşenler oluşturmamalısınız. Bu, kod organizasyon bileşenlerinin tam tersidir.
Şimdi daha yakından bakalım ve her iki durumu da örneklerle inceleyelim. Diyelim ki, tıklandığında mesajı günlüğe kaydeden bir düğmemiz olsun istiyoruz. Bu yanlış olabilir ve bu özel işlevsellik için belirli bir düğmeyi ve eylemleri tutacak ayrı bir bileşen oluşturulabilir. Önce yanlış yaklaşımı kontrol edelim:
// 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
mantığıVe buna göre, bu bileşene aşağıdaki html görünümü eşlik eder.
<button (click)="onButtonClick(true)">{{ name }}</button>
log-button.component.html
şablonuGördüğünüz gibi, yukarıdaki örnek işe yarayacaktır ve yukarıdaki bileşeni görüşleriniz boyunca kullanabilirsiniz. Teknik olarak ne istersen onu yapardı. Ancak buradaki doğru çözüm direktifleri kullanmak olacaktır. Bu, yalnızca yazmanız gereken kod miktarını azaltmanıza değil, aynı zamanda bu işlevi yalnızca düğmelere değil, istediğiniz herhangi bir öğeye uygulama olanağı eklemenize olanak tanır.
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
yönergesi Artık yönergemizi oluşturduğumuzda, onu uygulamamızda kullanabilir ve istediğimiz herhangi bir öğeye atayabiliriz. Örneğin, registration.component
yeniden kullanalım.
<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
yönergesi Şimdi, ayrı bileşenler oluşturmamamız gereken ikinci duruma bir göz atabiliriz ve bu, kod optimizasyon bileşenlerinin tam tersidir. Yeni oluşturulan bileşen, kodunuzu daha karmaşık ve daha büyük hale getiriyorsa, onu oluşturmaya gerek yoktur. Örnek olarak registration.component
ele alalım. Böyle bir durum, bir ton giriş parametresiyle etiket ve giriş alanı için ayrı bir bileşen oluşturmak olabilir. Gelin bu kötü uygulamaya bir göz atalım.
// 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
Ve bu, bu bileşenin görünümü olabilir.
<label for="{{ id }}">{{ label }}</label><br /> <input type="{{ type }}" name="{{ name }}" [(ngModel)]="model" /><br />
form-input-with-label.component
Elbette, registration.component
içindeki kod miktarı azaltılacaktır, ancak bu, kodun genel mantığını daha kolay anlaşılır ve okunabilir hale getiriyor mu? Sanırım kodu gereksiz yere eskisinden daha karmaşık hale getirdiğini açıkça görebiliyoruz.
Sonraki Adımlar: Açısal Bileşenler 102?
Özetlemek gerekirse: Bileşenleri kullanmaktan korkmayın; sadece elde etmek istediğiniz şey hakkında net bir vizyona sahip olduğunuzdan emin olun. Yukarıda sıraladığım senaryolar en yaygın olanlardır ve bence en önemli ve yaygın olanları; ancak senaryonuz benzersiz olabilir ve bilinçli bir karar vermek size kalmış. Umarım iyi bir karar vermek için yeterince öğrenmişsindir.
Angular'ın değişiklik algılama stratejileri ve OnPush stratejisi hakkında daha fazla bilgi edinmek isterseniz, Angular Change Detection ve OnPush Stratejisini okumanızı tavsiye ederim. Bileşenlerle yakından ilgilidir ve gönderide daha önce bahsettiğim gibi, uygulamanın performansını önemli ölçüde artırabilir.
Bileşenler, Angular'ın sağladığı yönergelerin yalnızca bir parçası olduğundan, nitelik yönergelerini ve yapısal yönergeleri de tanımak harika olurdu. Tüm yönergeleri anlamak, büyük olasılıkla programcının hep birlikte daha iyi kod yazmasını kolaylaştıracaktır.