Angular 9 Routing Mekanizması Nedir? Nasıl Çalışır?
Merhaba Arkadaşlar,
Bugün sizlere bir angular projesinde olmazsa olmaz lardan olan bir konudan bahsetmeye çalışacağım. Bu mühim konu , “Routing Mekanizması”.
Not : “Aaaa bu içerik çok uzun görünüyor..” diyebilirsiniz. Evet haklısınız. Fakat ben konuyu anlatırken benimle beraber kendi routing yapınızı mantığını anlayarak kurmanıza olanak tanıyacak şekilde ve bu yazıyı okuyarak çalışırken aklınıza bir şey takıldığında yine aklınıza takılan şeyi bu yazıda bulabilmeniz için yazıyı biraz uzun tutmak zorunda kaldım. Umarım sizler için faydalı olur. Hazırsanız başlıyorum…
Routing Nedir?
Tanımlara önem veren biri olarak önce “Routing Nedir?” basitçe değinelim. Routing aslında kullanıcının isteği doğrultusunda componentler arasında ki geçişine olanak tanıyan mekanizmadır. Yani bir nevi sayfalar arasındaki navigasyon işlemidir de denilebilir.
Routing Mekanizması Nasıl Oluşturulur?
Şimdi hep birlikte bu mekanizmanın nasıl çalıştığını anlamamıza yardımcı olacak bir sayfa tasarlayalım. Kodlamaya geçmeden önce yapacağımız tasarım hakkında biraz konuşalım. Aşağıda gördüğünüz üzere basit bir navbar tasarlayacağız ardından bu navbar üzerinden gerekli işlemlerimizi gerçekleştireceğiz. Navbar için projemize bootstrap ve jQuery kütüphanelerini ekleyeceğiz.
İşe Bootstrap ve jQuery kütüphanelerini ekleyerek başlayalım. Ama ondan önce projemizi oluşturmamız gerekiyor. Ben Microsoft Visual Studio Code kullanacağım. Visual Code’u açarak yeni bir terminal ekranı oluşturuyoruz. Ardından aşağıdaki adımları sırayla icra ediyoruz:
1-) ng new Routing
Bu komut bize “Routing” isminde bir angular projesi oluşturacak. Ardından terminalde bize “Would you like to add Angular routing?” sorusunu yöneltecek. Bu soru bize otomatik olarak bir angular routing modülü oluşturmak isteyip istemediğimizi soruyor. Evet deyip oluşturuyoruz.
2-)Bootstrap Ve jQuery Kütüphanelerinin Kurulumu
package.json dosyasına gidiyoruz. Bu dosya CLI(command line interface) vasıtası ile indirdiğimiz paketleri görüntülediğimiz ve ayarlarını yaptığımız yerdir.
Ardından terminal ekranına gelerek “npm install” komutu ile gerekli paketleri yüklüyoruz. Yüklediğimiz bu paketlerleri node_modules klasörü altında bulabilirsiniz. Bu klasör npm ile yüklediğimiz tüm paketlere ev sahipliği yapar. Ardından angular.json dosyasına gidiyoruz. Bu dosya ise genellikle üçüncü parti kütüphane veya bizim oluşturduğumuz css,js dosyalarını kullanmak istediğimiz zaman dosya yolunu belirttiğimiz dosyadır.
Evet kütüphaneleri başarılı bir şekilde eklediğimizi düşünüyorum. Sıra geldi nav-barımızı incelemeye:
Navbar üzerinden navigasyon yapabileceğimiz dört tane olmak üzere “Home,Categories,User ve Contact” alanları bulunuyor. “Categories” alanı ek olarak “CategoriesA,CategoriesB ve CategoriesC” şeklinde alt componentlere bölünecek ve bunun yanında “Users” içinde “user-detail” componenti bulunacak. “users/user-detail/3” dediğimizde üç numaralı kullanıcının detaylarına ulaşacağız.
Componentleri oluşturmadan önce oluşturduğumuz projedeki yapıyı inceliyelim:
Şimdi işe componentleri oluşturarak başlayalım. Terminal ekranına:
ng generate component home //home componentini oluşturur.
ng g c contact //contact componentini oluşturur.
ng g c category //category componentini oluşturur.
Şimdi “category” klasörünün içine girerek onun alt componentlerini oluşturacağız:
cd category //diyerek category klasörünün içine giriyoruz.
ng g c categoryA
ng g c categoryB
ng g c categoryC
Şimdi ise “Users” kısmını oluşturalım:
ng g c user
Ardından “users” componentinin olduğu klasör içine geliyoruz.
cd user
ng g c user-detail
Artık componentlerimiz hazır. Son hali aşağıdaki şekildeki gibi olmalıdır.
Burada “app.module.ts” bizim projemizin başlangıç modülüdür. Bu modülün başlangıç modülü olduğu “main.ts” dosyasında belirtilmiştir. “app.module.ts” dosyasına kısaca değinmek istiyorum:
Gördüğünüz üzere oluşturduğumuz tüm componentler “declaration” kısmında otomatik olarak angular tarafından bildirilmiştir. “declaration” sayesinde “app.module.ts” ‘ e gerekli componentleri tanımlamış oluyoruz. Yani artık “app.module.ts” bu componentleri tanıyor. Ayrıca “declaration” tanımı sayesinde bu tanımın içindeki tüm componentler birbirini tanıyor olacaktır. Yani ben home.component.html içerisinde veya “declaration” içindeki herhangi bir componentin “.html” dosyasından yine bu “declaration” içindeki başka bir componenti çağırabilirim.
“imports” kısmında ise “app-routing.module.ts” modülümüzü import ediyoruz ve routing mekanizmasını ana modülümüze bildiriyoruz.
Son olarak “bootstrap” kısmında projemizin başlangıç componenti bildirilmiştir.
Şimdi ise “app-routing-module.ts” dosyamıza gidiyoruz. Burada gözümüze çarpan ilk detay sayfamızda otomatik olarak tanımlı “routes” isimli sabit bir dizinin olmasıdır.
const routes: Routes = [];
Bu dizi içinde gerekli routing işlemlerimizi tanımlayacağız. Tarayıcı url’inde “/home” dediğimizde “home” sayfasına gitmek istiyoruz. Bunun için dizi için aşağıdaki gibi bir tanım yapıyoruz:
const routes: Routes = [{path:"home",component:HomeComponent}];
‘path’ kısmına “home” yazdık. Bu sayede tarayıcıda “/home” dediğimizde ‘component’ kısmında tanımlı olan “HomeComponente” yönleneceğimizi belirtiyoruz. Aynı şekilde diğer componentleri de tanımlıyoruz:
const routes: Routes = [{path:"home",component:HomeComponent},{path:"contact",component:ContactComponent},{path:"user",component:UserComponent}];
Şimdi ise, “user-detail” componentini yapalım. Ne demiştik? “user”altında “user-detail” olsun ve hangi id’li kullanıcıyı görüntülemek istiyorsak o kullanıcının sayfasına yönlenelim. Dizimize aşağıdaki şekilde bir ekleme yapıyoruz:
const routes: Routes = [{path:"home",component:HomeComponent},{path:"contact",component:ContactComponent},{path:"user",component:UserComponent},{path:"user/:id",component:UserDetailComponent}];
En altta ‘path’ kısmına bakarsak eğer “user/:id” şeklinde bir ifade göreceğiz. Bunun anlamı tarayıcıda “localhost…/user/5” gibi bir değer girdiğimizde yönlenilecek sayfa “5” id değerine sahip kullanıcının detay sayfası olacaktır. Yani 5 id değeri ile “UserDetailComponent” ‘e yönleneceğiz.
Sıra geldi “category” componentini ve onun alt componentlerini oluşturmaya:
Bunun için yukarıda yazdıklarımıza ek olarak aşağıdakileri ekleyeceğiz:
{path:"category",component:CategoryComponent,children:[{path:"categoryA",component:CategoryAComponent},{path:"categoryB",component:CategoryBComponent},{path:"categoryC",component:CategoryComponent}]}
İlk satırda “localhost…/category” dediğimizde “category” componentine yönleneceğimizi belirtiyoruz. Yani devamında hiçbir şey belirtmezsek sadece “category” componentine gideceğiz demektir. Fakat görüldüğü üzere “children” ile bu componentin altında olacak diğer componentleri de belirttik. Yani “localhost…/category/categoryA” denilirse “categoryA” componentine “localhost…/category/categoryB” denilirse “categoryB” componentine “localhost…/category/categoryC” denilirse “categoryC” componentine yönleneceğiz.
Ve “routes” dizimizin son hali aşağıdaki şekilde olacaktır:
const routes: Routes = [{path:"home",component:HomeComponent},{path:"contact",component:ContactComponent},{path:"user",component:UserComponent},{path:"user/:id",component:UserDetailComponent},{path:"category",component:CategoryComponent,children:[{path:"categoryA",component:CategoryAComponent},{path:"categoryB",component:CategoryBComponent},{path:"categoryC",component:CategoryCComponent}]},{path:"**",redirectTo:"home",pathMatch:"full"}];
En altta ki ‘ path:”**” ‘ ifadesi eğer URL’de “localhost…/no-name” gibi routing mekanizması dahilinde olmayan bir yol girilirse hangi componentin çalışacağını bildirir. ”redirectTo” ile gerekli componente yönlendirme yapılır. “Peki orada bir “patmatch” ifadesi var o nedir?” dediğinizi duyar gibiyim.
Burada işleri biraz daha detaylandırmak istiyorum. “patmatch” ifadesi ile birlikte angular’ın routing mekanizmasını nasıl yorumladığını örneklerle beraber inceliyelim:
Angular’da routing amacı kök düğümden başlayarak URL’in tüm parçaları yani segmentleriyle uyumlu bir yol bulmaktır. Eğer bulamazsa ekranda herhangi bir şey oluşmayacaktır.
Örneklerimiz boyunca kullanacağımız URL şu şekilde tanımlansın:
http://localhost:4200/user/name/comments
Örnek 1: Varsayılan URL Eşleşmesi
Aşağıdaki şekilde bir routing mekanizmamızın olduğunu vasayalım:
const routes: Routes = [{path:"home",component:HomeComponent},//1{path:"contact",component:ContactComponent},//2{path:":other",children:[ //3 {path:"secret-section",component:SecretSectionComponent}]},{path:"user",children:[ //4{path:"edit-user",component:EditUserComponent}, //5{path:":params",children:[ //6 {path:"shared",component:SharedComponent},//7 {path:"comments",component:CommentsComponent} //8]}]},];
1-) Angular önce URL’in ilk segmenti için arama yapar. Yani “user” için:
“user” != “home”
2-) Ardından bir altta bulunan “contact” için aynı işlemi yapar:
“user” != “contact”
3-) “:other” ile “user” eşleşecektir. Ne de olsa “:other” herhangi bir değeri temsil ediyor. Fakat “:other” ‘ın çocukları “user” ‘dan sonra gelen “name” ile uyuşmamaktadır. O yüzden burasıda pas geçilir.
4-) Bu adımda “user” ile URL’deki “user” eşleşir. Ardından çocuklar için kontrol başlar.
5-) ”edit-user” ile URL’in ikinci segmentindeki “name” eşleşmiyor. Bu sebebten ötürü burasıda pas geçilir.
6-) “:params” değeri ile “name” değeri eşleşir. Çünkü “:” ile buraya herhangi bir değerin geleceği belirtilmiştir. Ve routing mekanizması burayı eşler.
7-) Sıra son segmente geldi. “shared” ile URL’deki “comments” kontrol edilir. Fakat görüldüğü üzere burada herhangi bir eşleşme söz konusu değildir.
8) Son olarak “comments” ile URL’deki “comments” kontrol edilir. Ve sonuncu segmentte başarılı bir şekilde eşleşir. Ve “CommentsComponent” ‘ e yönlendirme yapılır.
Örnek 2: FULL URL Eşleşmesi
Şimdi yukarıdaki örneğimizi değiştirelim. İşeyiş 1–2 ve 3. adımlarda zaten değişmeyecek çünkü oralarda herhangi bir değişiklik yapmadık. Tek değişiklik 4. adıma ‘ pathMatch:”full” ‘ ifadesini ekledik:
const routes: Routes = [{path:"home",component:HomeComponent},//1{path:"contact",component:ContactComponent},//2{path:":other",children:[ //3{path:"secret-section",component:SecretSectionComponent}]},{path:"user",pathMatch:"full",children:[ //4{path:"edit-user",component:EditUserComponent}, //5{path:":params",children:[ //6{path:"shared",component:SharedComponent},//7{path:"comments",component:CommentsComponent} //8]}]},];
O zaman 4. adımdan itibaren incelersek:
4-) Angular path’de belirtilen “user” ifadesi ile URL’deki ilk segment olan “user”’ı karşılaştırmak yerine, URL’in tamamını yani “user/name/comments” ‘i , path’deki “user” ifadesi ile kıyaslar. Çünkü biz ‘ pathMatch:”full” ‘ diyerek URL’in tamamı ile eşleşmesini istedik. Ve neticede eşleme sağlanamadı. Burada çocuk componentlere asla bakılmayacaktır. Yani ‘ pathMatch:”full” ‘ diyerek çocuk URL’leri boşver sadece benimle eşleştirme yapmaya çalış diyoruz. Eğer benimle eşleşiyorsan devam et aksi takdirde devam etme diyoruz. Burada çocuk componentlere bakılmayacağı için eğer eşleşme olursa hangi componente gidileceğini yazmakta fayda var:
{path:"user",component:ExampleComponent,pathMatch:"full",children:[{............}]}
Eğer URL’den gelen “user/name/comments” ifadesi “path” ‘de tanımlı ifade ile eşleşseydi. “ExampleComponent” ‘e yönlendirme yapılacaktı.
Bu sebebten ötürü son 2. örnek herhangi bir sayfaya yönlendirme olmayacaktır.
Şimdi gelelim işin “.html” kısmını incelemeye:
Şekil 1.3'ü tekrar incelersek “index.html” gözümüze çarpacaktır. Bu html sayfası uygulamanın ayağa kalktığı sayfadır. Bu sayfa içerisinde “<app-root></app-root>” tag’leri ile “app.component.html” çağırılmaktadır.
“<app-root></app-root>” tag’leri “app.component.ts” ‘in “selector” kısmında tanımlanmıştır. Bu tagler “app.component.html” ‘in çağrılmasını sağlamaktadır.
Ardından “app.component.ts” ‘in “.html” sayfası olan “app.component.html” sayfasına bakalım:
Görüldüğü üzere “app.component.html” ‘in içeriği gayet basit görünüyor. Peki kırmızı kutu içine de aldığım “<router-outlet></router-outlet>” tagleri nedir? Ne işe yarıyorlar?
“<router-outlet>” tag’i ,oluşturulan component ve routing mekanizmasına bağlı olarak farklı componentleri yüklemek için bir yer tutucu olarak kullanılır. Yani routing mekanizması göz önünde bulundurularak “app” componentinin alt componentleri buraya render edilecektir.
Şekil ile daha net açıklamak gerekirse:
Buradan “app” componentinin ana componentimiz olduğu açıkca görünüyor. Şimdi aynı mantıkla “category” componentini inceliyelim:
“category” componentinin altında yer alan “categoryA ” , “categoryB” ve “categoryC” componentleri “category” componentinin çocuklarıdır. O zaman yukarıda anlattığımız mantık “<router-outlet>” mantığı onun içinde geçerli.
Hızlı bir şekilde “category.component.html” içine göz atalım:
Evet yine karşımıza “<router-outlet>” çıktı. “app.component.html” ‘in içinde yer alan “<router-outlet>” ile aynı mantıkda çalışır. Yani “category” component’ine ait alt componentler buraya render olacaktır. Olayı daha net görmek açısından hiyerarşinin son halini resim üzerinde göstermek istiyorum:
Evet… Şimdi navbarımızı projemize ekleme vakti geldi. Şimdi biraz düşünelim. Bu navbarı nasıl bir yere ekleyelim ki her sayfadan ulaşılabilsin? Aslında bunun cevabını biliyorsunuz. Navbar “app.component.html” dosyasına eklendiği takdirde her sayfadan ulaşılabilir olacaktır. Çünkü eninde sonun her sayfa “app.component.html” sayfasının içine render olmaktadır.
“app.component.html” ’in içeriğini oluşturalım. Navbar için bootstrap kullanıyorduk:
İçeriği çok basit olduğundan son halini direk aşağıdaki şekilde paylaşıyorum:
Gördüğünüz üzere navbar linklerinin attribute’larında “routerLink” kullanılmıştır. “routerLink” ‘ e değer olarak “app.routing.module.ts” ‘de kullandığımız “path”’leri veriyoruz:
Örneğin “home” için :
app.routing.module.ts
{path:”home”,component:HomeComponent}
app.component.html
<a class=”nav-link” routerLink = “home”>Home</a>
Artık navbar üzerinde “home” linkine tıklarsak “app.routing.module.ts” ’de tarif edildiği üzere “HomeComponent” ‘e yönleneceğiz.
Şimdi “category” componentinin “user.component.html” dosyasına bakalım:
Resimde gördüğünüz üzere “routerLink” ‘lere yine “app.routing.module.ts” ’de belirttiğimiz isimleri verdik. Ve alt componentlere ait linklere tıkladığımızda render edilecekleri kısmı “<router-outlet>” ile belirttik. Link üzerinden “CategoryAComponent” ‘ne gitmek istersek aşağıdaki gibi bir görüntü ile karşılaşırız :
Ve son olarak “user” componentine ait “user.component.html” , “user.component.ts” ve buna bağlı olarak “user-detail” componentine ait “user-detail.component.html” ve “user-detail.component.ts” dosyalarını inceliyelim:
“user.component.html” dosyası aşağıdaki şekildedir:
Burada user sayfasında olduğumuzu belirten bir “user works” ifadesi ”h3” etiketleri yardımıyla ekrana yazdırılmıştır. Ve “button” içinde “routerLink” kullanılarak “5” parametresi URL aracılığı ile gönderilmiştir. Biz eğer “user” sayfasındaysak URL’imiz şöyledir: http://localhost:4200/user. Ve biz bu sayfadayken kodu yukarıda verilmiş olan butona tıklarsak eğer URL şu şekilde değişir: http://localhost:4200/user/5 ve neticede “user-detail” componentine yönlendirme yapılır.
Butona tıkladığımızda “user-detail” componentine yönlendirileceğiz fakat biz burada URL ile bize gelen “ID” ‘yi “user-detail” componentinde yakalamak istiyoruz diyelim. Bunu yapmak zorunda değiliz fakat ben yinede yapacağım. Bunun için “user-detail.component.ts” dosyasında şu şekilde bir değişiklik yapılmalıdır:
Öncelikle resimi incelersek eğer, string tipinde “data” isimli bir değişken tanımladığımızı göreceğiz. Bu değişken bize URL ile gelecek olan değeri tutacağımız ve “user-detail.component.html” ‘e bu değeri yansıtmak için kullanacağımız değişkendir.
Ardından constructor’a “ActivatedRoute” servisi enjekte ediyoruz. (Servis konusunu inşallah daha ilerki konularda anlatacağım.) Oluşturduğumuz “activatedRoute” servis referansı ile URL’in parametrelerine “subscribe” oluyoruz. Yani bir nevi URL’i dinliyoruz. (“subscribe” olayını daha iyi anlamak için “RxJS” kütüphanesini araştırabilirsiniz.) Ve “app.routing.module.ts” ’ de belirlediğimiz “id” ismini kullanarak URL’deki değeri “data” isimli değişkene atıyoruz.
Aşağıdaki şekilde de “user-detail.component.html” ‘ de “data” isimli değişkeni çağırıyoruz:
Butona tıkladığımızda sayfamızın görüntüsü aşağıdaki resimdeki gibi olacaktır:
“HomeComponent” ve “ContactComponent” ‘leri için ise ekstra bir şey söz konusu değildir. Görüldüğü üzere angular’da routing mekanizması anlaşılması kolay ve gayet basit bir şekilde işlemektedir. Sadece birazcık daha pratik yaparak olayı daha iyi kavrayıp öğrendiklerinizi pekiştirebilirsiniz.
Eğer bazı yerlerde gözümden kaçan yazım hatası veya anlatım bozukluğu olduysa şimdiden kusuruma bakmayın. Yazım umarım faydalı olmuştur. Okuduğunuz için şimdiden teşekkür ederim. Bir sonraki yazıda görüşmek üzere!