Komponent Temelleri | Bölüm 4 | Angular in Action
Neredeyse her özellik, Angular uygulamalarının temeli olan komponentlere bağlı. Bu bölüm komponent tasarımı ve rolleri, @Component anotasyonuna ait konfigürasyonlar, Component Composition, komponentin yaşam döngüsü, az biraz lifecycle hooks, @Input özelliği ve content projection konularını içermektedir.
1. Bölüm uygulaması
Bu bölümde, komponentlerin bilgi paylaştığı ve etkileşime geçtiği bir komponent ağacı oluştucaktır. Uygulamanın dashboard ekranında veri merkezine ait kurgusal veriler olup ve 15 sn’de bir rastgele olarak yenilenmektedir. Reload butonu ile yeni veriler oluşturularak komponentlerin davranışlarının güncellendiğini göreceğiz.


1.1 Uygulamayı Git‘den Çekme
Uygulama repo’suna erişmek için aşağıdaki komut ile GitHub’dan proje çekilir. npm install diyerek paketler indirilir, npm serve komutuyla da proje çalıştırılır. Ayrıca UI framework’ü olarak ng-bootstrap kullanılmış.
git clone -b start https://github.com/angular-in-action/datacenter.git2. Component Composition ve Yaşam Döngüsü
Yaşam döngüsü (lifecycle), komponentlerin initial edilmesiyle başlar, komponent kaldırıldığında biter. Kaliteli bileşenler tasarlamaya yardımcı olacağı için bu akışın anlaşılması önemlidir.
Yüzlerce komponentten oluşan bir uygulama yapıldığın da, bu uygulamaların kalitesini büyük ölçüde komponentlerin ne kadar iyi tasarlandığı belirler. Bu yüzden komponentlerin composition’da uzmanlaşmak önemlidir.

Bir komponenti komponent yapan ana bileşenler;
- @Component anotasyonu — Angular’a komponenti register etmek için, tüm komponentler bu dekoratör ile başlamalıdır. Bu metadata, komponentin davranışını ve render şeklini etkileyen yardımcı özellikler taşır.
- Controller — @Component anotasyonu ile başlayan class, komponentteki tüm özellikleri ve metotları içerir.
- Tema — Tabiki de temasız komponent olmaz. Tema da Html kullanılarak katmanlar (layout) tanımlanır, davranışlar belirlenir ve veriyi bağlamak için controller’a bakar. Hem de kullanıcının etkileşime girebileceği bir görünüm oluşturur.
Aşağıdaki isteğe bağlı özelliklerin ilk ikisi komponente veriyi inject eder, geri kalan özellikler de komponentlerin birbiriyle etkileşimi, davranışı ve görünümünü etkiler.
- Providers and hosts — Servisler, root yerine komponent bağlamında çalışabilir. Böylece servislerin nerede kullanılacağının kontrolü bizde olur.
- Inputs — Komponentler input’ları kullanarak veri aktarımında bulunabilir.
- Styles and encapsulation — Komponentlerin içerdiği CSS stilleri, global olarak inject edilebilir ya da sadece istenen komponente özel oluşturulabilir. Böylece komponent tasarımına ait encapsülation katmanı sağlanır.
- Outputs — Veri değişikliğini dinlemek için event’lara bağlı özelliklerdir. Aynı zamanda komponent ağacında veri paylaşımı için de kullanılabilir.
- Lifecycle hooks — Bir bileşenin lifecycle süresi boyunca, bir mantık çalıştırmak üzere trigger yerleşmek için çeşitli hooklar kullanabilir.
2.1 Komponent Lifecycle

2.2 Lifecycle hook
Uygulamanın render olması ve kullanıcı girdilerine tepki verme sırasında gerekli koşul sağlandığında ya da değişiklik algılandığında kod çalıştırmak için hook’lar kullanılabilir. Yani, komponentin lifecycle’ı sırasında çağrılan belirli adlara sahip özel metotlardır. Mesela child komponent üzerinde değişiklik algılandığında kod çalıştırılabilir. Event-listener ile karıştırılmamalıdır.
Hook listesi ve rolleri
- OnChanges —Input alanına bağlanan veri değiştiğinde çalışır.
- OnInit — Komponent initial edildikten sonra bir kez çalışır. API’den veri yükleme gibi iniltialization kodunu yapmak için en iyi yer burasıdır.
- OnDestroyed — Komponent tamamen kaldırılmadan önce çalışır. Timer’ı silme, dinlenen veriyi durdurma işlemleri için kullanılabilir.
- DoCheck — Kendi Change detection’ımızı uygulamak için kullanılır.
- AfterContentInit —Bu hook, komponentin child’ları ile initial edildiğinde çağırılır.
- AfterContentChecked — Angular Content Child’ları kontrol ettiğinde çalıştırır veya isteğe bağlı olarak change detection eklenebilir.
- AfterViewInit —Tüm View Child’lar render olduktan sonra çalışır.
- AfterViewChecked — Komponentin görünümü ve View Child’ları kontrol ettiğinde, değişiklik olup olmadığını belirlemek için kullanılabilir.
Content Child ve View Child arasındaki farklılık nedir? Komponentleri iç içe yerleştirmenin iki yolu bulunur ve nasıl render olduğuna bağlı olarak adlandırılır. Başka bir komponentin içine tema ismiyle declare edilen komponentte View Child adı verilir. Bazen komponentin içine doğrudan başka bir komponent declare etmek yerine içerik eklenebilir, bu içeriğe de Content Child adı verilir. Content Child, açılıp kapanmış bir etiket içine declare edilir.
<!-- UserProfile komponentinde olduğumuzu varsayalım --><!-- Aşağıdaki komponent View Child örneğidir -->
<user-avatar [avatar]="avatar"></user-avatar> <!-- NgContent arasına yazılanlar koşula göre render olur. Buradaki içerik Content View örneğidir. NgContent elemenenti, content projection bölümünde anlatılmaktadır.-->
<ng-content></ng-content><!-- Buradaki örnekte UserProfile komponenti View Child, UserDetails komponenti ise Content Child olarak yer almaktadır. Bu durum iyi olmayan bir komponent tasarım örneğidir. -->
<user-profile [avatar]="user.avatar">
<user-details [user]="user"></user-details>
</user-profile>
3. Komponent Türleri
Temel olarak bir komponent olsa da rolleri bakımından dört kategoriye ayrılabilir. Bu bölümde bahsedilenler uygulanması gereken katı kurallar değildir, sadece rehberlik etmek için yazarın düşünceleri ve tavsiyeleri yer almaktadır.
- App komponent: Eğer değiştirilmediyse, uygulama başladığında ilk olarak App komponenti render edilir. Mümkün olduğu kadar bu komponenti basit tutmaya çalışmak önerilmektedir. Genellikle, yalnızca bir tema ve boş bir controller oluşturulmalı. Eğer App komponenti karmaşık davranışlara sahip değilse optimize etmek daha kolay olur.
- Display komponent: Komponent en çok bu rolde kullanılır, içerik oluşturmak ve verilen verileri görüntüleyen komponentlerdir. Olabildiğince encapsülated ve izole edilmesi tavsiye edilerek yeniden kullanımının kolaylaştırılması amaçlanır.
- Data komponent: Bu komponent, servisler ile çalışarak dış kaynaklar üzerinden verileri yönetir. Uygun bir hook kullanılabilir. Spesifik verileri yönettiği için yeniden kullanılabilirlik söz konusu değildir. Burada düşünülmesi gereken nokta, komponent ihtiyaç olan verileri nasıl yükleyecek ve nasıl işleyecek.
- Route komponent: Route ile yönlendirilen her bağlantı bir komponent ile ilişkilidir. Route, bağlı olduğu komponente veri yükleyebilir. Görüntülenen içeriğin id’si gibi parametreler burada yönetilebilir.
4. @Input() ile Property Özelleştirme
Komponentin lifecycle sürecini ve görünümünü yönetmek için ek olarak property tanımlanabilir. Ek property, input olarak tanımlanırsa komponente bağlanabilir. Böylece parent komponentten (buradaki senaryoda dashboard komponenti) child komponentine (metric komponenti) veri göndermiş olur
<!-- Bölüm2'deki örnekte yer alan Summary komponentine ait stock property'si, declare edildiği parent komponentteki stock değişkenine bağlanmıştır. -->
<summary [stock]="stock"></summary
Bu başlıkta, bir veri merkezi için metrik verilerinin barındırıldığı ve görüntülendiği dashboard komponenti oluşturulacak. Bu komponentin rolü, tüm veri merkezinin CPU ve belleğe ait metrik bilgilerini göstermektir.



4.1 Inputları Doğrulama
Şu an input alanlarına atanan değer doğrulanmamaktadır. Bu durum uygulama büyüdükçe veriyi izlemek zorlaştırabilir. Mümkün olduğunca inputları doğrulamak good practice'dir.
[used]="cpu.used" yerine [used]="'fail'" yani number tipindeki veriye string bir değer atanırsa hata fırlatılacaktır ve komponentin çalışması durucaktır. Değişkenin getter ve setter'ı oluşturulacak bu metotlar içierinde doğrulama ya da dönüştürme yapılabilir. Bu kriter baz alındığında aşağıdaki Metric komponentinin controller’ı aşağıdaki gibi düzenlenir.

5. Content Projection
Content projection, komponent declare edildiğinde elementlerinin içine içerik eklemeye izin verir. Bunu sağlayan elementin adı ise NgContent. Buradaki işlem tamamlandığında Resim 1'deki arayüz oluşacaktır.
<!-- Aşağıdaki komutlar çalıştırılır. Node komponenti ile tablonun header kısmı, Nodes Row komponenti ile de tablonun satırları oluşturulur. -->ng generate component nodes
ng generate component nodes-row





5.1 Multiple insertion points
Komponente birden fazla insertion point eklenmek istendiğinde, bu bölgeye isim verilir. Metric komponentindeki title ve description bilgileri için NgContent elementi kullanalım.

// metric.component.ts dosyasındaki bu veriler silinmelidir. Artık bu değerler, komponentin declare edildiği noktada verilecektir.
// @Input() title: string = '';
// @Input() description: string = '';
Şu haliyle proje çalıştırıldığında Angular derleme hatası verir. Sebebi, declare edilen NgContent elementlerini komponent sanıp, modül dosyasına register olmadığını düşünecektir. Bu hatayı gidermek için app.module.ts dosyasına bootstrap özelliğinin hemen sonrasına schemas özelliği eklenir.
bootstrap: [AppComponent],schemas: [NO_ERRORS_SCHEMA]
// Angular artık bilinmeyen elementlere karşı hata fırlatmayı bırakır ve element adlarına göre içerik eklemeye izin verir.
// NO_ERRORS_SCHEMA, '@angular/core' dan import edilir.
Bir sonraki bölümde komponentlerle daha derine inilip, change detection’ın nasıl optimize edileceğinden bahsedilecektir.
