Angular CDK ile Breakpointleri Tespit Etme: Kapsamlı Bir Rehber
Modern web uygulamaları, masaüstü bilgisayarlar, tabletler ve akıllı telefonlar gibi çok çeşitli cihazlarda sorunsuz bir kullanıcı deneyimi sunmak zorundadır. Bu, duyarlı (responsive) tasarımın temelini oluşturur. Duyarlı tasarımın kritik bir bileşeni, uygulamanın farklı ekran boyutlarına göre davranışını ve görünümünü ayarlamasına olanak tanıyan “breakpoint”lerdir. Peki, Angular uygulamalarınızda bu breakpoint’leri dinamik olarak nasıl tespit edebilir ve uygulamanızın mantığını buna göre nasıl uyarlayabilirsiniz?
İşte bu noktada Angular Component Dev Kit (CDK) devreye giriyor. Angular CDK, Angular ekibinin geliştiricilere sunduğu, temel UI davranışları için yeniden kullanılabilir, erişilebilir ve yüksek performanslı bileşenler sağlayan güçlü bir araç setidir. Bu rehberde, Angular CDK’nın BreakpointObserver servisini kullanarak uygulamanızın mevcut ekran boyutunu nasıl algılayacağınızı ve bu bilgiye dayanarak dinamik olarak nasıl tepki vereceğinizi detaylı bir şekilde inceleyeceğiz.
Angular CDK ve BreakpointObserver’a Genel Bakış
Angular CDK, Material Design spesifikasyonlarından bağımsız olarak Angular uygulamalarında yaygın olarak kullanılan işlevsellikleri soyutlayan bir dizi araç sağlar. Bu araçlar arasında erişilebilirlik yardımcıları, sürükle-bırak işlevselliği, portal yönetimi ve layout araçları bulunur. Konumuzla ilgili olan @angular/cdk/layout paketi, uygulamanızın ekran boyutuna ve yönelimine göre düzenini dinamik olarak ayarlamak için güçlü mekanizmalar sunar.
BreakpointObserver, bu paketin kalbinde yer alan bir servistir. Temel olarak, tarayıcının yerel medya sorguları API’sini sarmalar ve belirli medya sorgularının (yani breakpoint’lerin) şu anda eşleşip eşleşmediğini gözlemlemenize olanak tanır. Bir medya sorgusu eşleştiğinde veya eşleşmeyi bıraktığında, BreakpointObserver bir Observable aracılığıyla güncellemeler yayınlar. Bu sayede, uygulamanızın bileşenleri veya servisleri, ekran boyutu değişikliklerine anında tepki verebilir.
BreakpointObserver‘ın en büyük avantajlarından biri, CSS medya sorgularının gücünü Angular’ın reaktif programlama yetenekleriyle birleştirmesidir. Bu, sadece stil değişikliklerinin ötesine geçerek, uygulamanızın iş mantığını, bileşen yapısını veya hatta veri getirme stratejilerini ekran boyutuna göre dinamik olarak değiştirebilmenizi sağlar. Örneğin, küçük ekranlarda farklı bir navigasyon menüsü göstermek, büyük ekranlarda daha fazla sütunlu bir düzen kullanmak veya belirli verileri yalnızca belirli ekran boyutlarında yüklemek gibi senaryoları kolayca yönetebilirsiniz.
BreakpointObserver Kullanımı ve Örnekleri
Angular CDK Kurulumu
BreakpointObserver‘ı kullanmaya başlamadan önce, Angular projenize CDK’yı eklemeniz gerekir. Bu, aşağıdaki komutla kolayca yapılabilir:
ng add @angular/cdk
Bu komut, CDK’yı projenize ekleyecek ve gerekli bağımlılıkları yükleyecektir.
Temel Kullanım
BreakpointObserver‘ı bir bileşen içinde kullanmak için öncelikle onu enjekte etmeniz ve ardından observe() metodunu çağırmanız gerekir. observe() metodu, izlemek istediğiniz medya sorgularını (string veya string dizisi olarak) alır ve bir Observable döndürür. BreakpointState nesnesi, hangi medya sorgularının eşleştiğini ve genel olarak bir eşleşme olup olmadığını bildiren bilgiler içerir.
Angular CDK, yaygın cihaz boyutları için önceden tanımlanmış breakpoint’ler sunar. Bunlara Breakpoints enum’u aracılığıyla erişebilirsiniz (örneğin, Breakpoints.Handset, Breakpoints.Tablet, Breakpoints.Web).
Aşağıdaki örnekte, bir bileşenin mobil (Handset) mi yoksa daha büyük bir ekran (Web) mi olduğunu nasıl tespit ettiğini görebilirsiniz:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { Observable, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-responsive-layout',
template:
Breakpoint Tespiti
Şu anda mobil (handset) boyutundasınız.
Şu anda daha büyük bir ekran boyutundasınız.
Mevcut Eşleşen Breakpointler:
- {{ bp }}
,
styles: [
div { padding: 10px; border: 1px solid #ccc; margin-bottom: 10px; }
button { padding: 8px 15px; background-color: #007bff; color: white; border: none; cursor: pointer; border-radius: 4px; }
button:hover { background-color: #0056b3; }
]
})
export class ResponsiveLayoutComponent implements OnInit, OnDestroy {
isHandset$: Observable;
matchedBreakpoints: string[] = [];
private destroy$ = new Subject();
constructor(private breakpointObserver: BreakpointObserver) {
this.isHandset$ = this.breakpointObserver.observe(Breakpoints.Handset)
.pipe(
map(result => result.matches)
);
}
ngOnInit(): void {
this.breakpointObserver.observe([
Breakpoints.Handset,
Breakpoints.Tablet,
Breakpoints.WebLandscape,
Breakpoints.WebPortrait,
'(min-width: 1200px)' // Özel bir breakpoint
]).pipe(
takeUntil(this.destroy$)
).subscribe((state: BreakpointState) => {
this.matchedBreakpoints = [];
for (const query of Object.keys(state.breakpoints)) {
if (state.breakpoints[query]) {
this.matchedBreakpoints.push(query);
}
}
console.log('Breakpoint State:', state);
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
Bu örnekte:
BreakpointObserverservisi enjekte edilir.Breakpoints.Handsetmedya sorgusunu izleyen birObservableoluşturulur.map(result => result.matches)ile sadece eşleşme durumunu (true/false) döndürürüz.- HTML şablonunda
asyncpipe kullanarakisHandset$Observable’ını doğrudan kullanırız. Bu, Angular’ın otomatik olarak abone olup aboneliği kaldırmasını sağlar. - İkinci
observeçağrısı, birden fazla ön tanımlı ve özel breakpoint’i aynı anda nasıl izleyeceğimizi gösterir.BreakpointStatenesnesinden hangi sorguların eşleştiğini bulabiliriz. takeUntil(this.destroy$)vengOnDestroyiçindedestroy$.next()ile bellekte sızıntı olmaması için Observable aboneliğinin temizlendiğinden emin oluruz.
Özel Breakpointler Tanımlama
Breakpoints enum’u kullanışlı olsa da, bazen uygulamanızın özel ihtiyaçlarına göre kendi medya sorgularınızı tanımlamanız gerekebilir. observe() metoduna doğrudan CSS medya sorgusu string’leri geçirebilirsiniz:
this.breakpointObserver.observe([
'(min-width: 768px) and (max-width: 1024px)', // Tablet boyutları için
'(orientation: portrait)', // Dikey yönelim için
'screen and (min-resolution: 2dppx)' // Yüksek çözünürlüklü ekranlar için
]).subscribe(result => {
if (result.matches) {
console.log('Özel breakpoint eşleşti:', result.breakpoints);
}
});
Bu esneklik, uygulamanızın tasarımını ve davranışını en ince ayrıntısına kadar kontrol etmenizi sağlar.
Pratik Uygulama Senaryoları ve İpuçları
Servis Olarak Kullanım
Breakpoint algılama mantığını merkezi bir serviste tutmak, uygulamanızın ölçeklenebilirliği ve bakımı açısından en iyi yaklaşımdır. Bu, farklı bileşenlerin aynı breakpoint durumuna abone olmasını ve tutarlı bir şekilde tepki vermesini sağlar.
// breakpoint.service.ts
import { Injectable } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { Observable } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class BreakpointService {
isHandset$: Observable;
isTablet$: Observable;
isWeb$: Observable;
currentBreakpoint$: Observable;
constructor(private breakpointObserver: BreakpointObserver) {
this.isHandset$ = this.breakpointObserver.observe(Breakpoints.Handset)
.pipe(
map(result => result.matches),
shareReplay() // Çoklu abonelikler için tek bir yayın sağlar
);
this.isTablet$ = this.breakpointObserver.observe(Breakpoints.Tablet)
.pipe(
map(result => result.matches),
shareReplay()
);
this.isWeb$ = this.breakpointObserver.observe(Breakpoints.Web)
.pipe(
map(result => result.matches),
shareReplay()
);
// Hangi breakpoint'in aktif olduğunu gösteren bir Observable
this.currentBreakpoint$ = this.breakpointObserver.observe([
Breakpoints.Handset,
Breakpoints.Tablet,
Breakpoints.Web
]).pipe(
map((result: BreakpointState) => {
if (result.breakpoints[Breakpoints.Handset]) {
return 'Handset';
}
if (result.breakpoints[Breakpoints.Tablet]) {
return 'Tablet';
}
if (result.breakpoints[Breakpoints.Web]) {
return 'Web';
}
return 'Unknown';
}),
shareReplay()
);
}
}
Artık herhangi bir bileşen bu servisi enjekte ederek güncel breakpoint durumuna kolayca erişebilir:
// app.component.ts
import { Component } from '@angular/core';
import { BreakpointService } from './breakpoint.service';
@Component({
selector: 'app-root',
template:
Uygulama Genelinde Breakpoint Tespiti
Mobil cihazda görüntüleniyor.
Tablette görüntüleniyor.
Masaüstü/Web cihazında görüntüleniyor.
Mevcut Breakpoint: {{ breakpointService.currentBreakpoint$ | async }}
})
export class AppComponent {
constructor(public breakpointService: BreakpointService) {}
}
Performans İpuçları
shareReplay()Kullanımı: Bir servisteBreakpointObserverkullanırken,shareReplay()operatörünü kullanmak önemlidir. Bu, birden fazla abonenin aynı Observable’a abone olması durumunda, her abonenin medya sorgusunu tekrar tekrar çalıştırmak yerine, tek bir medya sorgusu aboneliğini paylaşmasını sağlar. Bu, performansı artırır.asyncPipe: Bileşen şablonlarındaasyncpipe kullanmak, bellek sızıntılarını önlemenin en iyi yoludur, çünkü Angular Observable’a otomatik olarak abone olur ve bileşen yok edildiğinde aboneliği kaldırır.distinctUntilChanged(): Eğer bir Observable’dan gelen değerlerin yalnızca değiştiğinde emit yapmasını istiyorsanız,distinctUntilChanged()operatörünü kullanabilirsiniz.BreakpointObserverzaten dahili olarak optimize edilmiştir, ancak karmaşık dönüşümler yapıyorsanız faydalı olabilir.
Kullanıcı Deneyimi İyileştirmeleri
- Navigasyon Değişiklikleri: Küçük ekranlarda bir hamburger menüsü, büyük ekranlarda ise tam bir navigasyon çubuğu görüntülemek.
- Veri Yoğunluğu: Küçük ekranlarda daha az bilgi göstererek veya kartların düzenini değiştirerek daha okunaklı bir deneyim sunmak. Örneğin, büyük ekranlarda 3 sütunlu bir kart düzeni, mobil cihazlarda tek sütunlu bir düzen.
- Bileşen Yükleme: Yalnızca belirli ekran boyutlarında ihtiyaç duyulan ağır bileşenleri veya modülleri tembelce (lazy-load) yüklemek.
Test Edilebilirlik
BreakpointObserver‘ı test etmek genellikle bir Spy nesnesi oluşturarak veya bir mock servisi sağlayarak yapılır. TestBed‘i kullanarak BreakpointObserver‘ı mock’layabilir ve observe metodunun belirli bir Observable‘ı döndürmesini sağlayabilirsiniz.
// BreakpointObserver'ı mock'lama örneği
import { TestBed } from '@angular/core/testing';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { of } from 'rxjs';
import { ResponsiveLayoutComponent } from './responsive-layout.component';
describe('ResponsiveLayoutComponent', () => {
let component: ResponsiveLayoutComponent;
let breakpointObserverSpy: jasmine.SpyObj;
beforeEach(async () => {
breakpointObserverSpy = jasmine.createSpyObj('BreakpointObserver', ['observe']);
breakpointObserverSpy.observe.and.returnValue(of({ matches: false, breakpoints: {} })); // Varsayılan dönüş değeri
await TestBed.configureTestingModule({
declarations: [ ResponsiveLayoutComponent ],
providers: [
{ provide: BreakpointObserver, useValue: breakpointObserverSpy }
]
})
.compileComponents();
});
beforeEach(() => {
const fixture = TestBed.createComponent(ResponsiveLayoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
it('should show handset content when breakpoint matches', async () => {
// Handset breakpoint'inin eşleştiğini simüle et
breakpointObserverSpy.observe.and.returnValue(of({
matches: true,
breakpoints: { [Breakpoints.Handset]: true }
}));
component.ngOnInit(); // Observable'ı tekrar tetikle
component.isHandset$.subscribe(isHandset => {
expect(isHandset).toBeTrue();
});
});
// Diğer test senaryoları...
});
Bu şekilde, bileşenlerinizin ve servislerinizin farklı ekran boyutlarına nasıl tepki verdiğini izole bir şekilde test edebilirsiniz.
Sonuç ve Sıkça Sorulan Sorular (SSS)
Angular CDK’nın BreakpointObserver servisi, modern ve duyarlı web uygulamaları geliştirmek için vazgeçilmez bir araçtır. CSS medya sorgularının gücünü Angular’ın reaktif programlama paradigmasıyla birleştirerek, uygulamanızın UI’sını, iş mantığını ve kullanıcı deneyimini farklı ekran boyutlarına göre dinamik olarak uyarlamanıza olanak tanır. Merkezi bir servis aracılığıyla kullanıldığında, kodunuz daha temiz, daha sürdürülebilir ve daha ölçeklenebilir hale gelir.
Uygulamanızın her cihazda harika görünmesini ve hissettirmesini sağlamak için BreakpointObserver‘ı stratejik olarak kullanmak, kullanıcı memnuniyetini artıracak ve geliştirme sürecinizi kolaylaştıracaktır.
Sıkça Sorulan Sorular (SSS)
BreakpointObserversadece CSS medya sorgularını mı kullanır?- Evet,
BreakpointObservertemel olarak tarayıcının yerelwindow.matchMediaAPI’sini kullanır. Bu API, CSS medya sorgularıyla aynı sözdizimini ve kuralları takip eder. - Performans üzerinde bir etkisi var mı?
BreakpointObserver, performansı göz önünde bulundurularak optimize edilmiştir. Medya sorgularını verimli bir şekilde dinler ve yalnızca durum değiştiğinde Observable’a yeni değerler gönderir.shareReplay()gibi operatörlerin doğru kullanımıyla performans etkisi minimaldir.- Angular Material ile birlikte mi kullanmalıyım?
- Hayır,
BreakpointObserverAngular Material’dan bağımsızdır ve herhangi bir Angular projesinde kullanılabilir. Aslında, Angular Material’ın kendi bileşenleri (örneğin, side-nav) iç duyarlı davranışları içinBreakpointObserver‘ı kullanır. - Birden fazla breakpoint’i aynı anda nasıl izlerim?
observe()metoduna bir string dizisi olarak birden fazla medya sorgusu geçirebilirsiniz (örneğin,this.breakpointObserver.observe([Breakpoints.Handset, Breakpoints.Tablet])). DöndürülenBreakpointStatenesnesi, hangi sorguların eşleştiğinibreakpointsözelliği aracılığıyla gösterecektir.asyncpipe kullanmanın avantajı nedir?asyncpipe, Observable’lara otomatik olarak abone olur ve bileşen yok edildiğinde aboneliği otomatik olarak kaldırır. Bu, bellek sızıntılarını önler ve manuel abonelik yönetimine (.subscribe()ve.unsubscribe()) göre daha temiz ve az hataya açık kod yazmanızı sağlar.