Angular direktifleri, yeniden kullanılabilir davranışları kapsüllemek için harika bir yol sunar -- direktifler bir elemana nitelikler, CSS sınıfları ve olay dinleyicileri uygulayabilir.
Direktif bileşim API'si, bileşen TypeScript sınıfı içinden bir bileşenin ana elemanına direktifler uygulamanıza olanak tanır.
Bir bileşene direktif ekleme
Bir bileşenin dekoratörüne hostDirectives özelliği ekleyerek bileşenlere direktifler uygularsınız. Bu tür direktiflere ana direktifler diyoruz.
Bu örnekte, MenuBehavior direktifini AdminMenu'nun ana elemanına uyguluyoruz. Bu, bir şablonda <admin-menu> elemanına MenuBehavior'ı uygulamaya benzer şekilde çalışır.
@Component({
selector: 'admin-menu',
templateUrl: './admin-menu.html',
hostDirectives: [MenuBehavior],
})
export class AdminMenu {}
Framework bir bileşeni render ettiğinde, Angular her ana direktifin de bir örneğini oluşturur. Direktiflerin ana bağlamaları bileşenin ana elemanına uygulanır. Varsayılan olarak, ana direktif girdileri ve çıktıları bileşenin genel API'sinin bir parçası olarak sunulmaz. Daha fazla bilgi için aşağıdaki Girdiler ve çıktıları dahil etme bölümüne bakın.
Angular, ana direktifleri derleme zamanında statik olarak uygular. Çalışma zamanında dinamik olarak direktif ekleyemezsiniz.
hostDirectives'de kullanılan direktifler standalone: false belirtemez.
Angular, hostDirectives özelliğinde uygulanan direktiflerin selector'ını yok sayar.
Input ve output'ları dahil etme
Bileşeninize hostDirectives uyguladığınızda, ana direktiflerden gelen girdiler ve çıktılar varsayılan olarak bileşeninizin API'sine dahil edilmez. hostDirectives içindeki girişi genişleterek girdileri ve çıktıları bileşeninizin API'sine açıkça dahil edebilirsiniz:
@Component({
selector: 'admin-menu',
templateUrl: './admin-menu.html',
hostDirectives: [
{
directive: MenuBehavior,
inputs: ['menuId'],
outputs: ['menuClosed'],
},
],
})
export class AdminMenu {}
Girdileri ve çıktıları açıkça belirterek, hostDirective ile bileşenin tüketicileri bunları bir şablonda bağlayabilir:
<admin-menu menuId="top-menu" (menuClosed)="logMenuClosed()"></admin-menu>
Ayrıca, bileşeninizin API'sini özelleştirmek için hostDirective'den girdileri ve çıktıları takma adlarla kullanabilirsiniz:
@Component({
selector: 'admin-menu',
templateUrl: './admin-menu.html',
hostDirectives: [
{
directive: MenuBehavior,
inputs: ['menuId: id'],
outputs: ['menuClosed: closed'],
},
],
})
export class AdminMenu {}
<admin-menu id="top-menu" (closed)="logMenuClosed()"></admin-menu>
Başka bir direktife direktif ekleme
Bileşenlere ek olarak diğer direktiflere de hostDirectives ekleyebilirsiniz. Bu, birden fazla davranışın geçişli olarak birleştirilmesini sağlar.
Aşağıdaki örnekte, Menu ve Tooltip olmak üzere iki direktif tanımlıyoruz. Ardından bu iki direktifin davranışını MenuWithTooltip'te birleştiriyoruz. Son olarak, MenuWithTooltip'i SpecializedMenuWithTooltip'e uyguluyoruz.
SpecializedMenuWithTooltip bir şablonda kullanıldığında, Menu, Tooltip ve MenuWithTooltip'in tümünün örneklerini oluşturur. Bu direktiflerin her birinin ana bağlamaları, SpecializedMenuWithTooltip'in ana elemanına uygulanır.
@Directive({
/* ... */
})
export class Menu {}
@Directive({
/* ... */
})
export class Tooltip {}
// MenuWithTooltip birden fazla başka direktiften davranışları birleştirebilir
@Directive({
hostDirectives: [Tooltip, Menu],
})
export class MenuWithTooltip {}
// CustomWidget, MenuWithTooltip'te halihazırda birleştirilmiş davranışları uygulayabilir
@Directive({
hostDirectives: [MenuWithTooltip],
})
export class SpecializedMenuWithTooltip {}
Ana direktif semantiği
Direktif çalıştırma sırası
Ana direktifler, doğrudan bir şablonda kullanılan bileşenler ve direktiflerle aynı yaşam döngüsünden geçer. Ancak, ana direktifler her zaman constructor'larını, yaşam döngüsü kancalarını ve bağlamalarını uygulandıkları bileşen veya direktiften önce yürütür.
Aşağıdaki örnek, bir ana direktifin minimal kullanımını gösterir:
@Component({
selector: 'admin-menu',
templateUrl: './admin-menu.html',
hostDirectives: [MenuBehavior],
})
export class AdminMenu {}
Buradaki yürütme sırası:
MenuBehaviorörneklendiAdminMenuörneklendiMenuBehaviorgirdileri alır (ngOnInit)AdminMenugirdileri alır (ngOnInit)MenuBehaviorana bağlamalarını uygularAdminMenuana bağlamalarını uygular
Bu işlem sırası, hostDirectives ile bileşenlerin bir ana direktif tarafından belirtilen herhangi bir ana bağlamayı geçersiz kılabileceği anlamına gelir.
Bu işlem sırası, aşağıdaki örnekte gösterildiği gibi iç içe ana direktif zincirlerine de uzanır.
@Directive({...})
export class Tooltip { }
@Directive({
hostDirectives: [Tooltip],
})
export class CustomTooltip { }
@Directive({
hostDirectives: [CustomTooltip],
})
export class EvenMoreCustomTooltip { }
Yukarıdaki örnekte yürütme sırası:
TooltipörneklendiCustomTooltipörneklendiEvenMoreCustomTooltipörneklendiTooltipgirdileri alır (ngOnInit)CustomTooltipgirdileri alır (ngOnInit)EvenMoreCustomTooltipgirdileri alır (ngOnInit)Tooltipana bağlamalarını uygularCustomTooltipana bağlamalarını uygularEvenMoreCustomTooltipana bağlamalarını uygular
Bağımlılık enjeksiyonu
hostDirectives belirten bir bileşen veya direktif, bu ana direktiflerin örneklerini enjekte edebilir ve tersi de geçerlidir.
Bir bileşene ana direktifler uygulanırken, hem bileşen hem de ana direktifler sağlayıcılar tanımlayabilir.
hostDirectives ile bir bileşen veya direktif ve bu ana direktifler aynı enjeksiyon token'ını sağlıyorsa, hostDirectives ile sınıf tarafından tanımlanan sağlayıcılar, ana direktifler tarafından tanımlanan sağlayıcılara göre öncelik kazanır.
Ana direktif tekilleştirmesi
Aynı direktif, çözümlenen ana direktif ağacında birden fazla kez yer aldığında, bir hata fırlatmak yerine otomatik olarak tekilleştirilir. Hangi eşleşmenin kalacağına karar vermek için iki deterministik kural kullanılır.
Şablon eşleşmesi öncelik kazanır
Bir direktif bir elemanı bir kez şablon seçicisi (selector) aracılığıyla eşleştirir ve aynı zamanda bir ana direktif olarak da yer alırsa, Angular yalnızca şablon eşleşmesini tutar ve tüm ana direktif eşleşmelerini atar.
Zihinsel model şudur: bir ana direktif eşleşmesi Partial<YourDirective>'i temsil eder, yani yalnızca hostDirectives içinde açıkça listelenen girdi ve çıktıların sunulduğu kısmi bir uygulamadır; bir şablon eşleşmesi ise tam genel API'siyle birlikte direktifin tamamını temsil eder.
@Directive({selector: '[hoverable]'})
export class Hoverable {}
@Component({
selector: 'app-button',
hostDirectives: [Hoverable],
})
export class Button {}
<!-- Hoverable, hem selector ile hem de Button'ın ana direktifi olarak eşleşir. -->
<!-- Angular yalnızca, tam genel API'ye sahip olan selector eşleşmesini tutar. -->
<app-button hoverable></app-button>
Birden fazla ana direktif eşleşmesi birleştirilir
Aynı direktif ana direktif olarak birden fazla kez yer alırsa, örneğin iki direktif de hostDirectives içinde ortak bir bağımlılık bildirdiğinde, Angular tüm örnekleri tek bir direktif örneğinde birleştirir. Tüm örneklerden gelen girdi ve çıktı eşlemeleri birleştirilir.
Bu, ana direktif bileşimindeki klasik elmas problemini çözer:
// Her iki tetikleyicinin de ihtiyaç duyduğu paylaşılan bir davranış
@Directive({
host: {
'[attr.data-trigger-id]': 'triggerId',
},
})
export class TriggerRef {
readonly triggerId = `trigger-${crypto.randomUUID()}`;
}
// Her biri TriggerRef'i ana direktif olarak bildiren iki ayrı tetikleyici
@Directive({
selector: '[popoverTrigger]',
hostDirectives: [TriggerRef],
})
export class PopoverTrigger {
readonly triggerRef = inject(TriggerRef);
}
@Directive({
selector: '[dropdownTrigger]',
hostDirectives: [TriggerRef],
})
export class DropdownTrigger {
readonly triggerRef = inject(TriggerRef);
}
<!-- Angular, her iki tetikleyici tarafından paylaşılan tek bir TriggerRef örneği tutar. -->
<button popoverTrigger dropdownTrigger>Actions</button>
HELPFUL: Angular paylaşılan direktifin yalnızca tek bir örneğini ürettiği için, PopoverTrigger ve DropdownTrigger enjekte ettiklerinde aynı TriggerRef örneğini alır.
Çakışan takma adlar
Angular, yinelenen ana direktif eşleşmelerini birleştirirken bunların girdi ve çıktı eşlemelerini de birleştirir. Aynı ana direktifin iki örneği aynı girdi veya çıktıyı farklı takma adlar altında sunarsa, Angular derleme zamanında bir hata fırlatır (NG8024)
@Directive({
selector: '[popoverTrigger]',
hostDirectives: [{directive: TriggerRef, inputs: ['triggerId: popoverTriggerId']}],
})
export class PopoverTrigger {}
@Directive({
selector: '[dropdownTrigger]',
hostDirectives: [
{directive: TriggerRef, inputs: ['triggerId: dropdownTriggerId']}, // farklı takma ad!
],
})
export class DropdownTrigger {}
<!-- Hata: triggerId hem "popoverTriggerId" hem de "dropdownTriggerId" olarak sunuluyor. -->
<button popoverTrigger dropdownTrigger></button>
Bunu çözmek için, her iki yolun da paylaşılan girdi veya çıktıyı aynı takma ad altında sunmasını sağlayın veya hiç sunmayın.