Takip et

Angular’da ng-content ile İçerik Yansıtma (Content Projection)

Angular’da ng-content ile İçerik Yansıtma (Content Projection) Angular, modern web uygulamaları geliştirmek için güçlü ve esnek bir framewor

Angular’da ng-content ile İçerik Yansıtma (Content Projection)

Angular, modern web uygulamaları geliştirmek için güçlü ve esnek bir framework’tür. Bileşen tabanlı mimarisi sayesinde, UI elemanlarını yeniden kullanılabilir ve yönetilebilir parçalara ayırmamızı sağlar. Bu parçaların en güçlü yönlerinden biri de “içerik yansıtma” (content projection) yeteneğidir. İçerik yansıtma, bir bileşenin, başka bir bileşen tarafından sağlanan içeriği kendi şablonunda belirli bir yere yerleştirmesine olanak tanıyan bir mekanizmadır. Bu, bileşenlerin daha genel, esnek ve yeniden kullanılabilir olmasını sağlar, çünkü belirli bir içeriği sert kodlamak yerine, dışarıdan dinamik olarak alabilirler.

Bu makalede, Angular’da içerik yansıtmanın temel prensiplerini, ng-content direktifini kullanarak nasıl yapıldığını, select özniteliği ile çoklu içerik yansıtma senaryolarını, ngTemplateOutlet ile farklarını, gelişmiş kullanım durumlarını (varsayılan içerik, @ContentChild/@ContentChildren) ve en iyi uygulamaları derinlemesine inceleyeceğiz. Amacımız, Angular geliştiricilerinin bu güçlü özelliği ustaca kullanabilmeleri için kapsamlı bir rehber sunmaktır.

Temel Kavramlar ve ng-content

Angular'da içerik yansıtma, bileşenler arası iletişimin ve esnekliğin temelini oluşturur. Bir bileşenin, çağrıldığı yerden aldığı HTML içeriğini kendi şablonunda bir "yer tutucuya" yerleştirmesi prensibine dayanır. Bu yer tutucunun adı ng-content'tir.

ng-content Nedir?

ng-content, Angular'da içerik yansıtma için kullanılan özel bir etikettir. Bir bileşenin şablonunda yer aldığında, o bileşene dışarıdan (yani onu çağıran üst bileşenden) geçirilen HTML içeriğinin nerede gösterileceğini belirtir. ng-content etiketi, DOM'da gerçek bir HTML elemanı olarak yer almaz; bunun yerine, Angular'ın render süreci sırasında, yerine yansıtılan içerik ile değiştirilir. Bu, bileşenlerin daha soyut ve genel amaçlı tasarlanmasını mümkün kılar. Örneğin, bir Card bileşeni oluşturduğunuzda, kartın başlığını, gövdesini ve alt bilgisini sert kodlamak yerine, bu alanları ng-content ile tanımlayarak, her kartın farklı bir içeriğe sahip olmasını sağlayabilirsiniz.

Nasıl Çalışır?

İçerik yansıtma, bir üst bileşenin (parent component) alt bileşeni (child component) çağırırken, alt bileşenin etiketleri arasına yerleştirdiği HTML içeriğini alt bileşenin kendi şablonunda ng-content etiketinin bulunduğu yere taşıması prensibiyle çalışır.

Basit bir örnekle açıklayalım:

Önce, içeriği yansıtacak olan alt bileşenimizi (ChildComponent) oluşturalım:

// child.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-child',
  template: 
    

Alt Bileşen Başlığı

Alt bileşenin kendi içeriği.

, styles: [] }) export class ChildComponent {}

Şimdi de bu ChildComponent'i kullanacak olan üst bileşenimizi (ParentComponent) oluşturalım:

// parent.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: 
    

Üst Bileşen Başlığı

Bu içerik üst bileşenden geliyor ve alt bileşene yansıtılacak!

Merhaba dünya!
, styles: [] }) export class ParentComponent {}

Bu örnekte, app-parent bileşeni app-child bileşenini çağırırken, etiketleri arasına bir

etiketi ve bir etiketi yerleştiriyor. app-child bileşeninin şablonundaki etiketi, tam olarak bu

ve etiketlerinin gösterileceği yerdir. Sonuç olarak, tarayıcıda gördüğünüz DOM yapısı, etiketleri arasına koyduğunuz içeriğin yerine yerleştirilmiş hali olacaktır.

Tek Bir ng-content Kullanımı

Yukarıdaki örnek, ng-content'in en temel kullanım şeklidir: tek bir yer tutucu ile tüm yansıtılan içeriği tek bir noktaya yerleştirmek. Eğer bir bileşende birden fazla ng-content etiketi kullanırsanız ve bunlara herhangi bir select özniteliği vermezseniz, Angular yalnızca ilk ng-content etiketine içeriği yansıtır ve diğerlerini göz ardı eder. Bu nedenle, birden fazla içerik slotuna ihtiyacınız olduğunda select özniteliğini kullanmanız gerekir.

Tek bir ng-content genellikle basit bileşenler (örneğin, bir uyarı mesajı kutusu veya genel bir panel) için yeterlidir, burada tüm dış içeriğin tek bir blok olarak ele alınması beklenir.

// alert.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-alert',
  template: 
    
⚠️
, styles: [ .alert { display: flex; align-items: center; padding: 10px; border: 1px solid orange; border-radius: 4px; background-color: #fff3e0; } .alert-icon { font-size: 24px; margin-right: 10px; } .alert-content { flex-grow: 1; } ] }) export class AlertComponent {}

Kullanımı:



  

Bu bir önemli uyarıdır. Lütfen dikkatli okuyunuz.

Sadece basit bir bilgi mesajı.

Bu şekilde, AlertComponent içeriği nasıl biçimlendireceğine karar verirken, mesajın kendisi dışarıdan dinamik olarak sağlanır. Bu, bileşenin yeniden kullanılabilirliğini önemli ölçüde artırır.

Çoklu İçerik Yansıtma: select Özniteliği

Bazı durumlarda, bir bileşenin şablonunda tek bir içerik alanı yeterli olmayabilir. Örneğin, bir Card bileşeni tasarlarken, kartın başlığı, gövdesi ve alt bilgisi gibi farklı bölümlerine farklı içerikler yerleştirmek isteyebiliriz. İşte bu noktada ng-content'in select özniteliği devreye girer. select özniteliği, yansıtılacak içeriği belirli CSS seçicileri kullanarak filtrelememizi ve farklı ng-content slotlarına yönlendirmemizi sağlar.

Neden select'e İhtiyaç Duyarız?

select özniteliği, bir bileşenin birden fazla "içerik slotu" tanımlamasına olanak tanır. Her ng-content etiketine benzersiz bir select değeri atayarak, üst bileşenden gelen içeriği, bu seçicilere uyan elemanlara göre farklı yerlere yönlendirebiliriz. Bu, bileşenlerin daha karmaşık ve yapılandırılmış içerikleri esnek bir şekilde barındırmasını sağlar.

Örneğin, bir Panel bileşeni düşünün. Bu panelin bir başlık bölümü, bir gövde bölümü ve isteğe bağlı bir eylem düğmeleri bölümü olabilir. select olmadan, tüm içeriği tek bir ng-content'e yansıtırız ve iç yapıyı yönetmek zorlaşır. select ile ise her bölüm için ayrı bir ng-content tanımlayabiliriz.

select ile Seçim Mekanizmaları

select özniteliği, CSS seçicilerine benzer bir sözdizimi kullanır. Aşağıdaki seçicileri destekler:

1. HTML Element Adı: Belirli bir HTML etiketiyle eşleşir.
* Örnek: select=”h2″, select=”p”

2. CSS Sınıfı: Belirli bir CSS sınıfına sahip elemanlarla eşleşir.
* Örnek: select=”.card-title”, select=”.card-body”

3. ID: Belirli bir ID'ye sahip elemanla eşleşir. (Genellikle tekil elemanlar için kullanılır.)
* Örnek: select=”#my-header”

4. Nitelik (Attribute): Belirli bir HTML niteliğine sahip elemanlarla eşleşir.
* Örnek: select=”[header]” (header niteliği olan tüm elemanlar), select=”[role=’footer’]”

5. Angular Seçiciler (Component, Directive): Belirli bir Angular bileşeni veya direktifiyle eşleşir.
* Örnek: select=”app-my-component”

Önemli Not: Eğer hiçbir ng-content etiketi select özniteliği belirtmezse veya bir select özniteliği hiçbir içeriği yakalamazsa, tüm "seçilmemiş" içerik, select özniteliği olmayan ilk ng-content etiketine yansıtılır. Eğer select özniteliği olmayan bir ng-content yoksa, seçilmemiş içerik render edilmez.

Örnekler ve Uygulama

Bir Card bileşeni üzerinden farklı select mekanizmalarını inceleyelim:

// card.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-card',
  template: 
    
, styles: [ .card { border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); margin: 15px; width: 300px; } .card-header { padding: 10px 15px; border-bottom: 1px solid #eee; background-color: #f8f8f8; font-weight: bold; } .card-body { padding: 15px; } .card-footer { padding: 10px 15px; border-top: 1px solid #eee; background-color: #f8f8f8; text-align: right; } ] }) export class CardComponent {} // card-actions.component.ts (bir başka bileşen) import { Component } from '@angular/core'; @Component({ selector: 'app-card-actions', template:
}) export class CardActionsComponent {}

Şimdi bu CardComponent'i kullanalım:



  
  

Ürün Detayı

Bu ürün çok kaliteli ve dayanıklıdır. Özel indirimle satışta!

  • Özellik 1
  • Özellik 2
Stokta sınırlı sayıda!

Bu örnekte:
*

etiketi, select=”[card-header]” olan ng-content'e yansıtılır.
*
etiketi, select=”.card-content” olan ng-content'e yansıtılır.
*
etiketi, herhangi bir select ile eşleşmediği için, select özniteliği olmayan ilk ng-content'e yansıtılır (bu durumda .card-body içindeki ikinci ng-content).
*
bileşeni, select=”app-card-actions” olan ng-content'e yansıtılır. Ayrıca, app-card-actions bileşeninin içindeki butonlar da kendi ng-content'i aracılığıyla yansıtılır.

Bu yapı, bir bileşenin farklı kısımlarının tamamen dışarıdan kontrol edilebilmesini sağlar, böylece bileşen kendisi sadece düzeni ve stilini yönetir.

Şablon Referans Değişkenleri ile İçerik Yansıtma:

ngTemplateOutlet ve ng-content'in Farkları

Angular'da dinamik içerik render etme ve bileşen esnekliği sağlamak için hem

ng-content hem de ngTemplateOutlet kullanılır. Ancak, bu iki mekanizma farklı senaryolar için tasarlanmıştır ve farklı kullanım şekillerine sahiptir. Aralarındaki farkı anlamak, doğru aracı doğru yerde kullanmak için kritik öneme sahiptir.

ngTemplateOutlet'in Rolü

ngTemplateOutlet, bir ng-template etiketinde tanımlanmış bir şablonu, başka bir yerde dinamik olarak render etmek için kullanılan bir yapısal direktiftir. ng-template kendisi DOM'a hiçbir şey render etmez; sadece bir şablon bloğu tanımlar. ngTemplateOutlet ise bu şablonu alır ve belirtilen yerde render eder. En önemli özelliklerinden biri, şablona bağlam (context) geçirebilme yeteneğidir. Bu, şablonun içindeki değişkenlere dışarıdan değerler sağlamamızı sağlar.

ngTemplateOutlet genellikle, bir bileşenin iç yapısının sabit olduğu ancak bazı kısımlarının görünümünün veya içeriğinin üst bileşen tarafından tamamen kontrol edilmesi istendiği durumlarda kullanılır. Örneğin, bir liste bileşeni düşünün. Liste bileşeni, veriyi alıp döngüye sokmaktan sorumludur, ancak her bir liste öğesinin nasıl görüneceğine üst bileşen karar verir.
// list-renderer.component.ts
import { Component, Input, TemplateRef } from '@angular/core';

@Component({
  selector: 'app-list-renderer',
  template: 
    

Liste Öğeleri:

, styles: [ .list-container { border: 1px solid purple; padding: 10px; margin-top: 10px; } ] }) export class ListRendererComponent { @Input() items: any[] = []; @Input() itemTemplate!: TemplateRef; }

Kullanımı:



  
    
{{ i + 1 }}. {{ item.name }} - Fiyat: {{ item.price }} TL
Toplam {{ items.length }} ürün gösteriliyor.

Bu örnekte,

ListRendererComponent her bir item için itemTemplate'i render eder ve item ile index değerlerini şablona bağlam olarak geçirir.

Ne Zaman

ng-content, Ne Zaman ngTemplateOutlet?

Bu iki mekanizma arasındaki temel farkı özetleyelim:

*

ng-content:
Ne zaman kullanılır: Üst bileşen, alt bileşenin içine yerleştirilecek gerçek DOM içeriğini* (HTML elemanları, metinler, diğer bileşenler) doğrudan tanımladığında. Alt bileşen bu içeriği sadece kendi şablonunda bir "slot"a yerleştirir.
* İçerik kimin kontrolünde: Üst bileşenin. Alt bileşen sadece içeriğin nerede görüneceğine karar verir.
* Bağlam: Yansıtılan içerik, üst bileşenin bağlamında (scope) çalışır. Yani, yansıtılan içerik içinde kullanılan değişkenler ve olay dinleyicileri üst bileşene aittir.
* Kullanım senaryoları: Genel amaçlı kartlar, paneller, modal pencereler, uyarı kutuları.

*

ngTemplateOutlet:
Ne zaman kullanılır: Üst bileşen, alt bileşene bir şablon tanımı* (bir
ng-template) sağladığında. Alt bileşen, bu şablonu kendi içindeki verilere göre dinamik olarak render eder. Alt bileşen, şablona kendi bağlamından (verilerinden) değerler geçirebilir.
* İçerik kimin kontrolünde: Şablonun yapısı üst bileşen tarafından tanımlanır, ancak şablonun içindeki veriler ve render edilme anı alt bileşen tarafından kontrol edilir.
* Bağlam: Şablon,
ngTemplateOutlet tarafından render edildiğinde, context özniteliği ile sağlanan bağlamda çalışır. Bu, alt bileşenin verilerini şablona aktarmasını sağlar.
* Kullanım senaryoları: Dinamik listeler, tablolar, form elemanlarının özelleştirilmesi, özel render ediciler (custom renderers).

Özetle:
*

ng-content: "Bana bu içeriği ver, ben de onu buraya koyayım." (Üst bileşen içeriği, alt bileşen yerini belirler.)
*
ngTemplateOutlet: "Bana bir şablon ver, ben de kendi verilerimle onu istediğim zaman render edeyim." (Üst bileşen şablonu, alt bileşen veriyi ve render zamanını belirler.)

ng-template ve ng-container'ın Önemi

Bu iki etiket, Angular'da görünmez bir şekilde DOM yapısını manipüle etmek ve kontrol etmek için kullanılır.

ng-template: Daha önce de belirtildiği gibi, ng-template kendisi DOM'a hiçbir şey render etmeyen bir etikettir. Sadece bir şablon bloğu tanımlamak için kullanılır. ngIf, *ngFor gibi yapısal direktifler aslında ng-template'e dönüşür. ngTemplateOutlet ile birlikte kullanıldığında çok güçlüdür.

ng-container: Bu da DOM'a hiçbir şey render etmeyen bir etikettir. Genellikle birden fazla elemanı gruplamak istediğimizde, ancak bu elemanları sarmalayacak fazladan bir HTML elemanı (örneğin bir div) eklemek istemediğimizde kullanılır. ngIf veya *ngFor gibi yapısal direktifleri birden fazla elemana uygulamak için de idealdir. İçerik yansıtma senaryolarında, birden fazla elemanı tek bir ng-content slotuna yönlendirmek için de kullanılabilir, ancak ng-content'in kendisi de bir gruplama görevi gördüğü için bu daha az yaygındır.

Örneğin, üst bileşenden alt bileşene birden fazla elemanı tek bir "slot"a yansıtmak istediğinizde, bunları bir

ng-container içine alarak organize edebilirsiniz:


  
    

Grup 1, Eleman 1

Grup 1, Eleman 2

Grup dışı eleman

ChildComponent'te select=”.my-group” kullanarak bu ng-container'ı seçebilirsiniz.

Gelişmiş İçerik Yansıtma Senaryoları

ng-content sadece temel içerik yerleştirme için değil, aynı zamanda daha karmaşık ve dinamik senaryolarda da kullanılabilir.

Varsayılan İçerik

Bir

ng-content slotuna hiçbir içerik yansıtılmadığında ne olmasını istersiniz? Angular, bu durumlarda varsayılan bir içerik tanımlamanıza olanak tanır. Eğer bir ng-content etiketi içine herhangi bir HTML içeriği yerleştirirseniz, bu içerik yalnızca üst bileşenden o ng-content slotuna karşılık gelen hiçbir içerik yansıtılmadığında gösterilir.
// panel.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-panel',
  template: 
    
Varsayılan Panel Başlığı
Bu panelin içeriği sağlanmadı. Lütfen içerik ekleyin.
, styles: [ .panel { border: 1px solid #ddd; border-radius: 5px; margin: 10px; width: 300px; } .panel-header { background-color: #f0f0f0; padding: 10px; font-weight: bold; } .panel-body { padding: 10px; } ] }) export class PanelComponent {}

Kullanımı:





  

Özel Panel Başlığı

Bu özel panelin içeriği.

Sadece Başlık

Bu özellik, bileşenlerinizi daha sağlam ve kullanıcı dostu hale getirir, çünkü her zaman bir yedek içeriğin olmasını garanti eder.

Koşullu İçerik Yansıtma

Üst bileşenden yansıtılan içeriği, alt bileşen içinde koşullu olarak göstermek veya gizlemek isteyebilirsiniz. Bu,

ngIf, ngFor gibi yapısal direktifleri ng-content etiketi üzerinde kullanarak kolayca yapılabilir.
// conditional-content.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-conditional-content',
  template: 
    

Başlık Var

Gövde İçeriği:

Alt Bilgi:

, styles: [ .wrapper { border: 1px solid teal; padding: 10px; margin: 10px; } ] }) export class ConditionalContentComponent { @Input() showHeader: boolean = true; @Input() showBody: boolean = true; @Input() showFooter: boolean = true; }

Kullanımı:



  

Bu Başlık Her Zaman Görünür

Bu gövde içeriği.

Bu alt bilgi asla görünmez.

Bu Başlık Asla Görünmez

Bu gövde içeriği de görünmez.

Bu alt bilgi görünür.

Bu, bileşeninizi daha dinamik hale getirir, çünkü içeriğin varlığına veya belirli koşullara bağlı olarak yansıtılan içeriği kontrol edebilirsiniz.

Yansıtılan İçeriğe Erişme:

@ContentChild ve @ContentChildren Dekorötörleri

Bazen alt bileşenin, kendisine yansıtılan içeriği doğrudan sorgulaması veya onunla etkileşim kurması gerekebilir. Örneğin, bir form bileşeni, kendisine yansıtılan tüm form kontrol elemanlarını bulup onları yönetmek isteyebilir. Angular, bu senaryolar için

@ContentChild ve @ContentChildren dekoratörlerini sağlar. Bunlar, @ViewChild ve @ViewChildren'ın yansıtılan içerik için olan karşılıklarıdır.

*

@ContentChild: Yansıtılan içerik içindeki ilk eşleşen elemanı (bileşen, direktif veya HTML elemanı) sorgulamak için kullanılır.
*
@ContentChildren: Yansıtılan içerik içindeki tüm eşleşen elemanları sorgulamak için kullanılır ve bir QueryList döndürür.

Bu dekoratörler,

AfterContentInit ve AfterContentChecked yaşam döngüsü hook'ları ile birlikte kullanılır, çünkü yansıtılan içerik, bileşenin kendi view'ı render edildikten sonra kullanıma hazır hale gelir.
// form-group.component.ts
import { AfterContentInit, Component, ContentChild, QueryList, ContentChildren } from '@angular/core';
import { NgModel } from '@angular/forms'; // Örnek olarak NgModel'i sorgulayalım

@Component({
  selector: 'app-form-group',
  template: 
    
Lütfen geçerli bir değer girin.
1"> Birden fazla input var!
, styles: [ .form-group { margin-bottom: 15px; border: 1px solid #eee; padding: 10px; border-radius: 4px; } .input-wrapper { margin-top: 5px; } .error-message { color: red; font-size: 0.8em; margin-top: 5px; } ] }) export class FormGroupComponent implements AfterContentInit { // Yansıtılan içerikteki ilk NgModel örneğini yakala @ContentChild(NgModel) inputModel!: NgModel; // Yansıtılan içerikteki tüm input, textarea veya select elemanlarını yakala @ContentChildren(NgModel) allInputs!: QueryList; ngAfterContentInit() { if (this.inputModel) { console.log('Yansıtılan içerikteki ilk NgModel:', this.inputModel); // Bu NgModel örneği üzerinde işlemler yapabiliriz, örneğin validasyon durumunu izleyebiliriz. this.inputModel.valueChanges?.subscribe(value => { console.log('Input değeri değişti:', value); }); } if (this.allInputs) { console.log('Yansıtılan içerikteki tüm NgModel\'ler:', this.allInputs.length); this.allInputs.changes.subscribe(() => { console.log('NgModel listesi değişti, yeni sayı:', this.allInputs.length); }); } } }

Kullanımı:


Bu örnekte,

FormGroupComponent, kendisine yansıtılan
Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.