TypeScript ile Angular — Türkçe kaynak
Söz uçar injection kalır
Merhaba,
React ve Redux ile ilgili makale yazdıktan sonra, baktım ki öğrenme aşamasında olduğum bir şeyi yazıya döktükçe daha iyi öğreniyorum, bundan sonra her öğrendiğim şeyin makalesini yazmaya karar verdim. Zaten çalışılmamış bir sınavın öncesinde sabahlamanın tuzu biberi olan, iğrenç kola kahve karışımı eşliğinde meze olarak hazırlanan kopya kağıtlarının sınav esnasında hiç cebinden çıkmamasının sebebi de buna bağlıdır :) Yazdıkça öğreniyor insan.
Normalde bilirsiniz hikaye kısmını hiç sevmem ama özet geçecek olursak; 13 yıldır Google’a kazık çakmış Misko Hevery adında bir abimizin “ya ben sürekli aynı kodu, fonksiyonu yazıyorum, dur ben işimi kolaylaştıracak bir javascript kütüphanesi yazayım” demesiyle Dünya’ya yayılan ve birden patlama yaratan Angular Js’nin(Angular 1) sonradan Google’daki amcaların “hacı iyi güzel yazdın, insanlar da yıllardır kullanıyorlar ama olmamış bu. biz daha iyisini yazdık” diye ayar çekip Angular’ı (Angular2+) yaratmasıdır. AngularJs ve Angular arasındaki farklılıkları ileride başka bir makalede yazacağım. Şuan sadece şunu bilmeniz önemli; Daha önce Angular 1 (AngularJS) kullandıysanız ve “Angular JS den Angular’a rahat geçerim, nasıl olsa biliyorum“ diye düşünüyorsanız, şimdiden söyleyeyim hayal kırıklığına uğrayacaksınız.
Angular yazarken SPA(Single Page Application) yapmak için TypeScript kullanmanız zorunlu değildir. Ancak biz bu makalede yapacağımız örneklerin içinde TypeScript kullanacağımız için az da olsa bilmeniz gerekiyor.
Makale boyunca Angular’ın kullanımlarını bir sepet uygulaması örneği üstünden, neyin ne işe yaradığından ve nerede nasıl kullanıldığına kadar mümkün oldukça detaylı değinmeye çalışacağız. Öncelikle bir giriş yapalım sonrasında kurulumları yapıp uygulamamıza geçeceğiz.
Angular mimarisi
Angular hakkında araştırma yaparken en çok karşılaşacağınız görsel bu ve en çok karşılaşacağınız kelimeler de Component, Template ve Injector olacaktır.
Peki nedir bunlar ?
Component’ler javascript class’ından oluşan yapılardır. Kendi içinde servislerle (injector) beslenir.(Servisler burada uygulama mantığını eklediğimiz yerler olarak düşünebilirsiniz). Component’lar sayesinde template’leri kontrol ederiz. Template üzerindeki elemanlara değişken atar ve template üzerinden tetiklenen kod parçacıklarını çalıştırırız. Template’imizin nerede konumlanacağını yani bir sayfa mı, yoksa sayfa içerisinde gösterilecek bir eleman mı? soruların cevabını metadatalar sayesinde buluruz. Bu alışveriş bittikten sonra uygulama önyükleyici (bootstrapping) root modülü bu yaptıklarımızı tek bir çatı altında toplar ve tarayıcıda gösterir. Component’ler ağaç yapısı şeklindedir. Bir tane ana component vardır ve diğer component’ler onun altında gelir.
MVC yapısını düşünürsek; Template’leri View, Component’leri Controller ve Servisleri de Modal olarak adlandırabiliriz.
Modüler ise kendi içinde bir yapıya sahiptir.İçerisinde birbiri ile ilgili componentler gibi nesneleri barındırır. Bir uygulamanın koordineli bloklar halinde düzenlenmesine yardımcı olur. Kendine ait modülerlik bir sistemine sahiptir. Angular uygulamaları geneldeAppModule isminde, root module olan en az bir NgModule sınıfı içerir. Siz bir uygulamada birden fazla modül oluşturabilirsiniz. Ancak, Birden fazla modül ile çalışmak için yine bir başlangıç modülü vermelisiniz ama kuracağınız yapı başlangıç modülünün başka modülleri çağırması şeklinde olabilir. Bu şekilde çoklu modül ile çalışabilirsiniz. Genellikle orta ölçekli uygulamalarda tek bir modülle çalışılır.
Bu şekilde tanımlarla kafa karıştırmak yerine örneğimiz üzerinden anlatmak daha anlaşılır olacaktır.
Kurulum
React’da da olduğu gibi Angular kurulumunun bir kolay bir de zor yolu var. Zor yolda uygulama dizininin oluşturulması, tsconfig, package, typing json dosyalarının oluşturulması ve bunların her birinin ayarlanması gibi bir sürü adım var. Zor yolu merak edip, denemeler yaptım ancak baktım Angular’dan soğuyorum “en iyisi kolay yoldan ilerlemek” diye düşündüm. Biz kolay yoldan kurulum olan Angular CLI’ı (Commet line interface) kullanacağız.
NodeJs bilgisayarınızda mutlaka kurulu olmalı.
npm install -g @angular/cling //Adım 1
cd C:\..* //Adım 2
ng new sepetUygulamasi //Adım 3
cd sepetUygulamasi //Adım 4
code . //Adım Bonus
ng serve //Adım 4
1: global olarak CLI’ı bilgisayarınıza kuruyorsunuz.
2 : cd ile projenizi nereye kuracaksanız yolunu veriyorsunuz.
3 : uygulamamızın ismini belirliyoruz ve Angular CLI kurulumunu yapıyoruz. 4: Oluşturduğumuz uygulamanın içine giriyoruz.
Bonus : Bu kod bilgisayarınızda Visual Studio code kuruluysa oluşturduğunuz uygulamayı otomatik olarak açacaktır. Ancak siz başka IDE kullanmak isterseniz oluşturduğunuz uygulamayı Open Folder’dan seçerek açabilirsiniz.
5: Uygulamanızın içine girdikten sonra bu kodu çalıştırarak projenizi ayağa kaldırıyorsunuz. Ardından localhost:4200'den ya da ng serve — open diyerek otomatik browser’da açılmasını sağlayarak projenizi ayağa kaldırabilirsiniz.
Sonuççç veee iişteee karşınızdaaaa Angular’ın mükemmel tasarım abidesi karşılama sayfasııııı :)
Projenizi olur da Explorer’da ayağa kaldıracak olursanız ilk anda çalışmayacaktır. Ufak bir ayar yapmanız gerekiyor. CLI kurulumu sonrası projenizde “src” altında “polyfills.ts” adında bir type script dosyası bulunuyor.
normalde resimde görünen importlar yorum satırı ile kapalı olarak gelecektir. Eğer yorum satırından çıkartıp açarsanız projeniz IE’de de çalışacaktır.
Projemizi tanıyalım
CLI, size daha önce bahsettiğim zor yolu bizim yerimize yaptı ama neyin ne olduğunu bilmeden ilerlemeyelim. Klasör yapısını ve içindekilerin üstünden kısaca geçelim.
e2e (end to end) : Burası test alanıdır. Unit testlerin ve diğer testlerin ayarlarının yapıldığı yerdir.
node_modules : Bizim node aracılığı ile yüklediğimiz paketlerin dosyalarını içeren yerdir.
src : Uygulamanın çalıştığı klasördür. Angular adına göreceğiniz uygulamaya yönelik her şey burada olacaktır.
app : Burası bizim çatımız olduğundan detaylı inceleyeceğiz.
assets (varlıklar) : Resim gibi dosyaların konulduğu klasördür.
environments : Geliştirme ortamında (environment.ts) ve yayına alma ortamında (environment.prod.ts) ayarlamaların yapıldığı alandır.
browserslist : Uygulamanın destek verdiği browser’ları gösterdiği yerdir.
index.html : Ana html sayfamızdır. Hatırlarsanız Angular’ın çalışma mantığını incelerken sizlere her şeyin tek bir root çatısı altında toplandığından bahsetmiştik. Index.html’in içinde <app-root> tag’i göreceksiniz. Bu html tag app component’imiz ile ilgili direktifi çağırır. Yani ana çatıyı burada göstermemizi sağlar. App.component’i birazdan inceleyeceğiz.
karma.conf.js : Unit test ile alakalı bir dosyadır.
main.ts : Uygulamamızın giriş noktasıdır. Uygulamanın compile (derlenmesi) veya başlangıç modülüne dair bilgiler verir.
polyfills.ts : Bizim farklı tarayıcılarda çalışmamamızı sağlayan ayarların yapıldığı yerdir.
test.ts : Unit testlerin yazılmadığı, sadece unit test ayarlarının yapıldığı ve testlerin başlangıç noktasının belirlendiği yerdir.
tsconfig.app.json,
tsconfig.spec.json,
tslint.json : Test ile ilgili dosyalardır. Test dosyalarını hızlı geçiyorum. Çünkü burada bir işimiz yok.
.editorconfig : Bizim kullandığımız editöre temel bilgilerin verildiği yerdir.
.gitignore : Git’den çekildiği için temel ayarları verir.
angular.json : Uygulama içerisinde ayarları yaptığımız yerdir. Third party kütüphane veya bizim yarattığımız css, js dosyaları kullanacağımızda ilgili kütüphaneleri ve dosyalarının yolunu buraya ekleriz.
package.json : CLI vasıtası ile indirdiğimiz paketleri görüntülediğimiz ve ayarlarının yapıldığı yerdir. Hangi paketleri istiyorsak onları yazarız ardından npm install ile onları kurarız.
Dependencies : Bu alan uygulama içinde lazım olan paketlerin olduğu yerdir. Yani son kullanıcıya lazım olan şeylerdir.
devDependencies : Son kullanıcıyı ilgilendirmeyen, örneğin bizim unit test yapmamız için ilgili paketleri içeren yerdir.
tsconfig.json : Type script’in compiler ayarlarının yapıldığı yerdir.
tslint.json : Visual studio’da Angular ile kod yazarken bizim kullanım açısından kolaylık sağlayan ayarların yapıldığı yerdir.
APP klasörü
Burası bizim başlangıç component’imizdir. Angular çalışma mantığındaki görselde bulunan component, template ve injector gibi yapıları burada göreceğiz. Önce kısaca tanıyalım ardından detayına girelim.
app-routing.module.ts : Routing (yönlendirme) sayfalama ile ilgili bir dosyadır. Routing konusunda detaylı inceleyeceğiz.
app.component.spec.ts : Bu component’in unit test’i.
app.component.css : Klasörlerin dışında bulunan style.css’den bağımsız sadece bu component özelinde etkisi olan css’dir.
app.component.html : İlgili component’in view alanıdır. Component’te bind ettiğiniz şeyleri burada tanımlarsınız. Örneğin yukarıda incelediğimiz Angular’ın muhteşem karşılama tasarımı buradan gelmektedir. Burada dikkat ederseniz “tittle” “{{title}}” şeklinde yazılmış. Bu şekilde yazılmasının sebebi tek yönlü binding işleminin yani one way binding yapıldığını gösterir. Yani “bu html’in ilgili dökümanı app.component’e git ilgili title değişkenini bul ve değerini yaz demek” oluyor.
app.component.ts : İlgili component’imizin kendisidir. Yani bir html çıktısına hizmet eden class’tır. App.component.html’deki (component’in view katmanı) statik ve dinamik nesnelerini kullanacağımız zaman ilgili datayı çektiğimiz alandır. Yukarıda hatırlarsanız bir resim url’i örneği vermiştim. App.component.html’de kullanacağınız değişkenler, fonksiyonlar, void metotlar gibi şeyler burada bulunur. Bir component’in component olabilmesi için @Component’den deklare ediliyor (özellik alıyor) olmanız gerekir.
selector : Bu component’in html’in içerisinde hangi isimle çağrılacağını anlattığınız yerdir. Yukarıda CLI’ı tanıtırken index.html bölümünde bahsettiğimiz <app-root> buradan gelmektedir.
templateUrl : Bu component’in html’i nasıl olacağının bilgisini verir.
styleUrls : İlgili component’in css dosyasını gösterir.
export class AppComponent: Component’imizi dışa aktardığımız alandır. Örneğin burada bulunan “tittle” nesnesini dışa aktararak html kısmında one way binding ile gösterimini sağlıyoruz. Burada “sepetUygulamasi” yazısını değiştirdiğimizde sayfayı kaydettiğinizde browser’ınızda da değiştiğini göreceksiniz.
./ (nokta bölme işareti) bulunduğu klasörü temsil ediyor.
app.module.ts : Bizim root modülümüzdür. Main.ts’yi anlatırken bahsettiğimiz başlangıç modülü burasıdır. Burası bizim uygulama boyunca kullanacağımız farklı modülleri entegre ettiğimiz, bu component ile ilgili importları yapabildiğimiz yada başka modüllerdeki nesnelere ulaşabildiğimiz yerdir. Bir modülün modül olabilmesi için @NgModule’den deklare ediliyor (özellik alıyor) olması gerekir. NgModule aslında @NgModule şeklinde ifade edilen bir sınıftır. NgModül’ü tek bir objesi olan ve içeriğindeki elemanlar ile modülü tanımlayan bir constructor olarak düşünebilirsiniz.
Her Angular kütüphanesi “@angular” öneki ile başlar. Npm paket yöneticisi ile indirip javascript “import” ifadesi ile onları ekleyebiliriz.
declaration : Bu uygulama boyunca bu modüle dahil etmek istediğimiz component’leri çağırdığımız yerdir. Component’ler tasarlarken bu hangi modüle dahil etmek istiyorsanız o component’in ismini o modüle yazarsınız ve içine import ederek kullanırsınız. Burayı kısaca view alanlarının tanımlandığı yer olarak düşünebilirsiniz.
İmports : Bizim component’imizde kullanmak istediğimiz thirt party veya Angular’da tanımlı modülleri içeren yerdir. Bu alana yazdığınız ve import ettiğiniz modüllerin artık nesnelerine erişerek kullanabilirsiniz. Kısacası başka bir modüle ait olan view class’larının mevcut modül içinde kullanılmak istenildiğinde kullanılan elemandır.
exports : Burada yazılmamış ama export olarak tanımladığımız, declaration’ların alt kümesi olarak modüllerin template’lerinin de görülebilir veya kullanılır hale getiren bir alan daha mevcut. Bu alan başka bir deyişle modül içersinde kullanılacak olan component class‘larının tanımlandığı yerdir. Kısacası app component’iniz import edildiğinde bizim hangi componentlerimize erişebileceğinizi gösterir.
Export’u ihraç, import’u da ithal ettiğiniz şeyler olarak düşünebilirsiniz.
providers : Injectable Service Class’ıdır. Servis sağlayıcıları olarak adlandırılabilir. Bizim uygulama boyunca kullanacağımız bir servis varsa yani global olacaksa onu app.component’inin içine tanımlarsınız. Eğer local bir servis kullanıp sadece ilgili component çalıştığında kullanmak isterseniz provider’ı o component’in modülünün içine tanımlarsınız.
bootstrap : Bu modülün başlangıç component’inin ne olduğunu belirttiğiniz noktadır. Bootstrap özelliği sadece root component’ine atanır.
export class AppModule : Diğer dillerdeki public’e denk gelir. Yani başkaları referans edebilmesi için export yazarız. Class onun bir sınıf olduğunu. AppModule ise ismini gösterir.
Component yaratmak
ng g component **buraya component'in ismi**
(biz component olarak product yazdık)
Angular CLI ile basit bir şekilde component yaratabiliyoruz. Component’iniz oluştuktan sonra app.module.ts ye bakarsanız CLI’ın bizim yerimize app.component ve product.component arasında bağlantı kurduğunu göreceksiniz.
Product Component
Bu component’de northwindapi.azurewebsites.net/api/products adresinde bulunan bir servisi kullanacağız. Servislerle uğraşırken kullanacağız objeleri type safe kapsamında bir süzgeçten geçirerek kontrol altında yazmanız hem sizden sonra kodlayan kişinin kodu anlaması hem de servis işlemlerinde nerede hata yaptığınızı type script ile farketmeniz için büyük önem taşımaktadır.
Bu noktada servisin bize sağladığı objeleri kontrol altında tutmak için typescript ile bir Product nesnesi oluşturduk.
Service oluşturmak
Angular mimari görselinde hatırlarsanız servis ile component’leri beslediğimizden bahsetmiştik. Bunun için öncelikle component’imiz servisi kullanabilmesi için bir servis dosyası oluşturmalıyız. CLI bunu da bizim için kolay hale getirmiş. cd src/app/product ‘ın altına service diye bir klasör oluşturarak ng g service product komutu ile bir servis dosyası oluşturuyoruz.
Burada dikkat ederseniz component’larda nasıl @Component olarak süsleme varsa servislerde de @Injectable adında bir süsleme var. Bir class’ın servis olabilmesi için @Injectable deklarasyonu deklare ediliyor (özellik alıyor) olmanız gerekir. Yani bizim servisimiz product component’ine product servisini enjekte edilebiliriz demiş oluyoruz.
Burada okurken kendinize şunu sorabilirsiniz “Neden servis çekmek için ayrı bir dosya oluşturuyorum?” haklısınız. Bu işlemi component’inizin içerisinde de yapabilirsiniz.Yada tek bir servis oluşturup tüm servislerinizi buradan da kontrol altında tutabilirsiniz. Ancak yazılım geliştirmenin çeşitli prensipleri vardır. Bunlardan en önemlisi single responsibility (SOLID) ‘dir. Hatta hep bunun için İsveç çakısı resimleri kullanılır. Bir yerde sadece bir işi yapmalısınız.
Http service
Bir servis ile uğraşabilmek için @angular’ın http servislerini kullanmanız gerekir. İlk olarak app.modul’e bunu import etmeniz gerekmektedir.
artık bu servisleri product.servis.ts dosyamızda kullanabiliriz.
import { Injectable } from '@angular/core';import { Product } from '../product'; //Adım 1
import { Http, Response } from '@angular/http'; //Adım 2
import { Observable } from 'rxjs'; //Adım 3
import { map,catchError,tap } from "rxjs/operators"; //Adım 4@Injectable({
providedIn: 'root'
})
export class ProductService {constructor(private http:Http) { }// Adım 6getProduct():Observable<Product[]>{ //Adım 5
// Adım 7
return this.http.get("http://northwindapi.azurewebsites.net/api/products")
.pipe(map(Response => Response.json()))
}
}
product.servis.ts
product.servis.ts’i adım adım inceleyelim.
1: Type safe için oluşturduğumuz product nesnesini kullanacağımız için servisimize dahil ettik.
2: Http servisine ulaşabilmek için yapılan importlardır. Http istek yapabilmek için response ise bize gelen yanıta karşılık gelecek nesneleri içerir.
3: Angular http servislerinde reactjs dediğimiz paketleri kullanır. Bu paketler bize observable yani asenkron bir şekilde servis data’sına ulaşmamızı sağlar.
Observable(izlenebilir) kavramından biraz bahsetmek istiyorum. Observable şunun için kullanılır; Örneğin bir servise “get” yaptığınızda o servisi sadece izlersiniz. Yani observal olur. Ne zaman siz bu servisin sonunda subscribe (abone) olursunuz, işte o zaman servise bağlanmış olursunuz. Geriye sadece response kullanarak içerideki dataya ulaşmanız kalır.
Map : Gelen response datayı bizim istediğimiz bir nesneye map etmesi için kullanılır.
Tap (do) : Data geldiğinde yapmasını istediğimiz işlemi anlatır.
CatchError (catch): Bir hata olduğunda yapılmasını istediğim bir şey varsa onu yazarız.
RxJS yeni 6. sürümünü çıkarttı. 6. sürüm öncesinde observable,map,do ve catch’i aşağıdaki gibi sayfanıza dahil ediyordunuz.
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/do'
import 'rxjs/add/operator/catch'...
return this.http.get("...").map(...);
...
ancak güncelleme sonrası “.map” kullanırken başına “pipe” yazmanız, import edecekseniz “rxjs/operators” in altından nesne olarak eklemeniz, “catch()” yerine “catchError()” ve do yerine de “tap()” kullanmanız gerekmektedir. Detaylı bir şekilde bu linkten inceleyebilirsiniz. Ben çok aradım, siz de benim gibi uğraşmayın.
https://www.academind.com/learn/javascript/rxjs-6-what-changed/
5: Burada amacımız servise observable olarak bağlamak ve component’imizin içerisinde subscribe olup dataya ulaşmak. Bu işlemi yaparken datanın bizim product nesnemizin süzgecinden geçmesini ve bize bir array dönmesini istiyoruz.
6 : Amacımız http servisine get yaparak component’imize göndermemiz gerekiyor. Ancak bizim önce http global servisine ulaşmamız gerekiyor. Burada Angular’a http için bize bir instance(özellik) üretmesini söylüyoruz.
7 : Yukarıda http’yi talep ettikten sonra artık “this.http” diyerek biz instance’larına ulaşabiliyoruz. Artık get ile dataya ulaşıyor, map ile de gelen response datayı bizim istediğimiz bir nesneye eklemesini istiyoruz. Return diyerek nesnemizi component’imize gönderiyoruz. Burada json yazmamızın sebebi servisi çektiğimiz data’nın XML olması. Biz data’yı response ederken onu json formatına dönüştürüyoruz.
// Adım 1
import { Component, OnInit } from '@angular/core';
import { Product } from './product';
import { ProductService } from "./service/product.service";@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css'],
providers:[ProductService] // Adım 2
})
export class ProductComponent implements OnInit { // Adım 1products: Product[]; // Adım 3
constructor(private productService:ProductService) {} // Adım 4ngOnInit() {
this.getProduct();
}getProduct(){ // Adım 5
this.productService.getProduct().subscribe(response =>{
this.products = response
})
}
}
product.component.ts
1: Oluşturduğumuz product nesnesini ve oluşturduğumuz observable servisi kullabilmek için iki adet import gerçekleştirdik. {Component,OnInit} CLI ile component oluşturduktan sonra gelen kavramlar. Component’in zaten ne olduğu hakkında fikriniz vardır ama OnInit’i biraz açıklayalım. Angular CLI İçinde bulunan ngOnInit() adında fonksiyonunu kullanmanız için bize OnInit adında bir arayüzü(interface) component’timize dahil ediyor (implemets). ngOnInit component açıldığında çalışan ilk fonksiyondur. ngOnChance’den sonra gelen ikinci lifecycle eventidir. Burada getProduct’ı OnInit’e tanımlayarak component açıldığı anda ilk bu fonksiyonu çalıştırmasını istiyoruz.
2: Servisimizi kendimiz local olarak çektiğimiz için provider’i componentimizin içerisine yazdık. Eğer observable olan asenkron data’mızı her yerde kullanmak isteseydik, app.modul’ün içerisinde bulunan provider kısmına yazacaktık.
3: Bizim servisten gelen değerlere ulaşmamız için oluşturduğumuz product array nesnesini product adında bir değişkene atadık.
4: Çektiğimiz servisi component’in içerisinde kullanmak için onu constructor bloğuna enjekte etmemiz gerekiyor. Angular kendi içerisinde dependency injection(bağımlılık enjeksiyonu) mekanizmasına sahip. Dolayısıyla http’de yaptığımız gibi productService değişkenine ProductService’i atadık. Bu sayede artık “this.productService” diyerek servisimizde bulunan “getProducts” ‘a erişebilliriz.
5: “.subscribe” ile asenkron olan operasyonumuza abone olduk. Gelen response datasını bizim product’ımızın içine atadık. Artık servis ve component arasındaki bağlantı hazır. getProduct() fonksiyonunu düzenli görünmesi için bu şekilde yazdık.
<div>
<ul>
<li *ngFor="let product of products">
{{product.productName}}
</li>
</ul>
</div>
product.component.html
Component’a çektiğimiz servisi view katmanına aktarmamız gerekiyor. Burada servisten gelen bütün data’ları tek tek yazmak yerine angular ngFor özelliğinden yararlanıyoruz. ngFor bir Angular direktifidir. Aslında bilinen forEach mantığında çalışan bir koddur. Bir dizin (array) veya koleksiyon(collection) içinde bulunan tüm elemanların içinde dönebilmek için kullanılır. Kısacası kendini tekrar eden bir yapıyı tekrar tekrar yazmaktansa bir kere yazıp döngüye sokarak kendisinin yazmasını sağlarsınız. “let (*) of products” ‘da bulunan “products” bizim component’imizden gelen “products”tır. Kısacası burada “component’den gelen tüm products’ları gez ve her bir objeye product adını ver” demiş olduk. Let ise sadece bu scope’da tanımlandığı ve dışarıdan gelecek diğer product’lardan etkilenmemesi için yazıldı. Artık {{product.productName}} diyerek data’mızda bulunan productNmalere ulaşabiliriz.
<app-product></app-product>
app.component.html
Artık son adımız olan meta data sayesinde view katmanımızın nerede konumlanacağını belirtiyoruz. Bunun için Angular’ın muhteşem tasarımını silip bizim templatimizi tanımladık.Sonuç olarak tüm dataları listelemiş olduk.
Parametreler Üzerinde Dependency Injection
Servislerinizi sabit bir API den alacağınız zaman her yere o adresi yazmak yerine bir değişkene atayıp bunu component’lerinizde de kullanabilirsiniz.
...
providers: [
{
provide: 'apiUrl',
useValue: "http://northwindapi.azurewebsites.net/api"
}
],
...
app.module.ts
Root modulünüzde global nesneler tanımlayabileceğiniz gibi kendi tanımlayacağımız local bir nesne de tanımlayabilirsiniz. Yukarıda provide edeceğimiz nesnenin ismini “apiUrl” , değerini de servisimizin adresi olarak tanımladık.
import { Injectable, Inject } from '@angular/core';
...
export class ProductService {constructor(private http:Http, @Inject('apiUrl') private apiUrl) { }getProducts():Observable<Product[]>{
return this.http.get(this.apiUrl + "/products")
...
}
} product.service.ts
Ardından product servisimize gelerek “@Inject” ile parametre Injectable’ı kullanacağımızı belirttik. Bu parametrenin isminin “apiUrl” olduğunu ve bizim servis dosyamızda bunu “apiUrl” (buraya istediğiniz ismi verebilirsiniz) ismi ile kullanacağımızı yazdık. Yani bir değişkene atadık. Geriye sadece direkt olarak adresi yazmak yerine “this” ile değişkenimize ulaşıp “+” ile nereye ulaşmak istediğimizi yazmak yeterli oldu.
Third party paket eklemek (bootstrap & jquery)
npm install ngx-bootstrap bootstrap jquery --save
Terminalden ilk olarak paketleri indiriyorsunuz. Bu kodu çalıştırdığınızda package.json dosyanızda paketlerin indiğini göreceksiniz. Ardından angular.json dosyanıza indirdiğiniz paketlerin yolunu vereceksiniz. Bu kadar basit.
Versiyonlar siz bu makaleyi okuduğunuz zamana göre değişiklik gösterebilir.
uygulamamızın biraz daha güzel görünmesi için https://getbootstrap.com/docs/4.1/getting-started/introduction/ sayfasından bir tane hazır nav bar
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">{{title}}</a>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto"></ul>
<div class="btn-group">
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sepet
</button>
<div class="dropdown-menu dropdown-menu-right">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here</button>
</div>
</div>
</div>
</nav><app-product></app-product> app.component.html
ve grid yapısından yararlandık.
<div class="col mt-3">
<ul class="list-group">
<li class="list-group-item" *ngFor="let product of products">
{{product.productName}}
</li>
</ul>
</div> product.component.html
Event Binding ve ngIf Direktifi
Tıklandığında sepete ekleme yapacak (Event Binding) bir sepete ekleme butonu yapmayı planlıyoruz. Product.component’i sayfamızın biraz daha dolu görünmesi için productName’in yanına, quantityPerUnit ve unitPrice’ı da ekledik.
<div class="col">
<h4 class="page-header">Ürünler</h4>
</div>// Adım 2
<div *ngIf="addedProduct" class="alert alert-success"> // Adım 3
{{addedProduct}} added to cart!
</div><div class="col mt-3">
<ul class="list-group">
<li class="list-group-item" *ngFor="let product of products"> // Adım 1
<button (click)="addToCard(product)" type="button" class="btn btn-sm btn-primary float-right">
Sepete ekle
<i class="far fa-shopping-cart"></i>
</button> <h5><b>{{product.productName}}</b></h5>
<p>{{product.quantityPerUnit}}</p>
<h6>{{product.unitPrice}}</h6>
</li>
</ul>
</div> product.component.html
1: Bu adımdan bahsetmeden önce dikkat etmeniz gereken bir durum var; *ngFor çalıştırdığımız li tagının açılış ve kapanışı bizim için nesnelere ulaşabileceğimiz sınır alanıdır. Yani bu alan içerisinde data’yı çektiğim için tüm product nesnelerine ulaşabiliriz. Bu nedenle, butona click olduğunda addToCard metodunu çağırıyoruz ve içine product nesnesini tanımlıyoruz. Bu sayede her döndürdüğümüz product nesnesini alıyoruz ve sepete ekleme metoduna yerleştiriyoruz. Yani bir bakıma ngFor bizim yerimize index işlemini de yapmış. Geri kalan kodların hepsi tasarım ile alakalı yazdık.
let in ve let for diye iki kullanım vardır. Let in size array içindeki elemanların index’ini , let for ise array içinde bulunan elemanların kendisini verir.
2: Burada amacımız butona tıkladıktan sonra tıkladığımız ürünün isminin bootstrap’in uyarı tasarımı ile tepede gösterilmesi. Bunun için product component.ts’ye bir göz atalım ardından devam edeceğim.
...
addedProduct: string; // Adım 2
constructor(private productService: ProductService) { }
ngOnInit() {
...
}
getProduct(){
...
}
addToCard(product: Product){ // Adım 2
this.addedProduct = product.productName
}
} product.component.ts
Uyarı metnini oluşturmak için addedProduct adında bir değişken tanımladık. Bu değişkeni event binding yaparak oluşturduğumuz addToCard metodunun içinde ürün isimleriyle (productName) eşitledik. “product: Product” yazmamızın sebebi; butona tıkladığımız zaman bize parametre olarak product gelecek. Yani “parametre product olacak ve alacağım parametre tipini de product olarak bekliyorum” demek istedik.
3: Burada ngIf kullanmadan da bu uyarı metnini yapardık ama, o zaman karşımıza butonlara tıklamadığımızda uyarı metni sürekli ekranda görünürdü. Bu da kullanıcıya sürekli sanki bir sorun varmış izlenimi yaratmış olurdu. *ngIf kullanımlarını ileride daha detaylı inceleyeceğiz ama burada kullanım sebebimiz; bir kontrol mekanizması oluşturmak. Yani burada “addedProduct isimli değişken tanımlı ise, bu değişkenin içinde değer varsa veya addedProduct undifine değilse uyarı alanını göster” demiş oluyoruz.
Bilinenin aksine ngIf’in Angular’da farklı bir görevi vardır. Belirtilen öğeye verilen şart sağlanmazsa öğeyi gizlemek yerine DOM yapısından tamamen kaldırır. Bu sayede o koşul sağlanana kadar DOM’a yüklenmez.
Global servisler
Şimdi sepete ekleme senaryomuzda ilk aşamayı tamamladık. Artık sepete ekleme çalışmasını yapmaya başlayalım. Önce app klasörümüzün altına sepet elemanları ile işlem yapmak için cart componenti oluşturuyoruz.
import {Product} from '../product/product'
export class CartItem{
quantity:number;
product:Product;
} cart-item.ts
Ayrıca sepetteki elemanları tutmak için cart.item.ts’yi oluşturuyoruz. Burada ürünlere ihtiyacımız olacağı için product nesnesini içeri import ediyoruz.
import {CartItem} from './cart-item'export const CART_ITEM_LIST:CartItem[]=[]; cart-item-list.ts
Bir de tuttuğumuz ürünleri listelemek için cart-item-list.ts ihtiyacımız var. İçerisinde cart item’ları tutacağımız için cartItemları buraya çekiyoruz. Burada sabit bir değişken tanımlıyoruz ve içerisinde “CartItem[]=[]” array’i tanımlayacağımız ve bu array’in şuan için boş olacağını belirtiyoruz. Sonradan bu array’i çekeceğimiz servis ile dolduracağız.
Şimdi bizim bir kart servisine ihtiyacımız var. Bunun için cart klasörünün içine bir “service” klasörü oluşturduk. Ardından service klasörünün içerisinde “ng g service cart” kodu ile yeni servis yarattık.
// Adım 1
import { Injectable } from '@angular/core';
import { Product } from '../../product/product'
import { CartItem } from '../cart-item'
import { CART_ITEM_LIST } from '../cart-item-list'@Injectable()
export class CartService {// Adım 2
cartItems: CartItem[];
constructor() { }// Adım 3
addToCart(product: Product): void {
var addedItem = CART_ITEM_LIST.find(t => t.product.productId == product.productId);
if (addedItem) {
addedItem.quantity += 1;
}
else {
let cartItem = new CartItem();
cartItem.product = product;
cartItem.quantity = 1;
CART_ITEM_LIST.push(cartItem);
}
}// Adım 4
list(): CartItem[] {
return CART_ITEM_LIST;
}// Adım 5
removeFromCart(product: Product) {
var addedItem = CART_ITEM_LIST.find(t => t.product.productId == product.productId);
var indexNo = CART_ITEM_LIST.indexOf(addedItem);
if (indexNo != -1) {
CART_ITEM_LIST.splice(indexNo, 1);
}
}} cart.service.ts
1: Çalışacağımız nesneleri servise dahi ettik.
2: cartItem isminde bir değişken tanımladık. İçine de özellik olarak cartItem array’i tanımladık.Bu sayede cartItem değişkenini kullanarak altında bulunan özelliklere ulaşacağız.
3: Sepete ekleme işlemimiz için ön hazırlıkları tamamladık. AddToCart ismiyle fonksiyon tanımladık. Buraya bir ürün gönderip ekleme işlemi yapmak için product parametresi tanımladık ve bunu bizim product nesnemizden geleceğini belirttik. ”addedItem” değişkenine şunu tanımladık; “bir sabit olan CART_ITEM_LIST’teki datayı bul ve datanın altında bulunan product.id benim gönderdiğim productId ile eşitse o zaman bu ürün sepette vardır” dedik. Bu noktadan sonra geri kalan sadece “if” ile sepete ürün ekli olup olmamasına göre etkileşime girmek.
“if” alanında ilk olarak eğer aynı üründen sepette varsa ilgili ürünün birimini bir arttırması gerektiğini else kısmında eğer sepetimizde aynı üründen yoksa yeni bir cartItem değişkeni yaratıyoruz. Bu cartItem’ın product’ı bizim gönderdiğimiz product’tır. Bu cartItem’ın adedi birdir dedik ve yeni güncellemeyi CART_ITEM_LIST ‘e gönderdik.
4: Sepetteki elemanları listelemek için “CART_ITEM_LIST’i geri döndür” dememiz gerekyor.
5: Müşterinin isterse sepetten ürün silebilmesi için removeFromCart adında metot tanımladık. Bu alanda sadece product id ile iş yapacağımız için product:number olarak parametre tanımladık. Burada 3 numaralı adımda yaptığımız gibi ürüne erişmemiz gerekiyor. Ardından silme işlemi yapabilmek için addItem’ın kaçıncı sırada olduğunu bulmamız lazım. Bu nedenle ilk olarak addItem’ın indexini bulup indexNo değişkenine atadık. “if” ile “eğer indexNo yoksa bir işlem yapma yani -1, eğer varsa indexNo’nun bulunduğu index’den sonra bir tanesini yani bastığımızı sil” dedik.
Bu işlemlerden sonra sepet servisimiz artık hazır. Normalde bu işlemin devamında product.component’de servisimizi tanımlayıp işlemimizi bitirebilirdik ancak bu sepetin sürekli hayatta kalması yani global olması lazım.
...
import { CartService } from "./cart/service/cart.service";...
providers: [
{
provide: 'apiUrl',
useValue: "http://northwindapi.azurewebsites.net/api"
},CartService
],
... app.module.ts
Bunun için ilk olarak app.modul’üne bizim sepetimizi import etmemiz gerekiyor. Sepetimizi global tanımladıktan sonra artık product.component’imizde servisimizi çağırabiliriz.
...
import { CartService } from "../cart/service/cart.service"; //Adım 1
...
export class ProductComponent implements OnInit {
...
constructor(
...,
private cartService:CartService //Adım 2
) {}...
addToCard(product:Product){
...
this.cartService.addToCart(product); //Adım 3
}
} product.component.ts
1: Burada aslında bize bir cart service instance’ı vermesini istiyoruz. sayfaya çağırıyoruz.
2: Çağırdığımız CartService nesnesini cartService değişkenine atıyoruz. Artık servisimizi istediğimiz yerde kullanabiliriz.
3: Burada oluşturduğumuz product nesnesini addToCard’atıyoruz. Bu yöntemle ürünü gönderip ürünün sepete eklenmesini işlemini sağlamış oluyoruz.
Buraya kadar anlattığımız, bir servisin global tanımlanması ve bir ürünün global servise parametre olarak geçilmesi inceledik. Şimdi uygulamamızı bir ileri seviyeye taşıyacağız. Sepet kısmında eklediğimiz ürünleri, ürünlerin adedini, ne kadar tuttuğunu ve bastığımızda tüm ürünlerin listesine gitmeyi yapacağız.
ngDoCheck kullanımı
Öncelikle cart klasörümüzün altına cart-summary adında bir component oluşturuyoruz ve aşağıdaki işlemleri yapıyoruz.
...
// Adım 1import { Component, OnInit, DoCheck } from '@angular/core';
import { CartService } from "../service/cart.service";
import { CartItem } from "../cart-item";...export class CartSummaryComponent implements OnInit,DoCheck {
constructor(private cartService:CartService) { } // Adım 2totalCartItem:number; // Adım 3
totalCartItemPrice:number // Adım 4
cartItem:CartItem[]; // Adım 5ngOnInit() {
this.cartItems = this.cartService.list();// Adım 6
}ngDoCheck() {
// Adım 7
this.totalCartItem = this.cartService.list().reduce((a,b)=>a+b.quantity,0);// Adım 8
this.totalCartItemPrice= this.cartService.list().reduce((a,b)=>a+b.quantity*b.product.unitPrice,0);
}} cart-summary.component.ts
Burada dikkat ederseniz, normalde ngOnInit yazdığımız alanın yerine ngDoCheck diye bir fonksiyon yazdık. ngOnInit’in görevi daha öncede bahsettiğimiz gibi; component açıldığında çalışan ilk fonksiyondur demiştik. Yalnız bizim sepet alanında yapacağımız işlem sadece ilk anda çalışıp durması değil, tam aksine dinamik olarak her değişiklik anında çalışmasını beklemekteyiz. Bunun için ngOnInit’den sonra doCheck denilen bir event kullanmamız gerekti. doCheck içersinde bulunduğu işlemlerde değişiklik olduğunda bulunduğu alanı yenileyen bir event’tir.
1: Servis data’sına erişeceğimiz için cartService’yi sayfamıza dahil ettik. Ayrıca sepet elemanlarıyla çalışacağımız için CartItem’ı da dahil ediyoruz.
2: CartService’mizin özelliklerini kullanabilmek için private bir değişkene atadık. Burada dikkat ederseniz provider tanımlamadık. Çünkü CartService’imiz global ve sadece sayfaya bu şekilde atamak yeterli oluyor.
3: Sepetimizde kaç adet ürün olduğunu tutmak istiyoruz. Bu nedenle totalCartItem adında bir değişken tanımladık ve bir number beklediğimizi belirttik.
4: Ayrıca burada sepet tutarını görüntülemek istediğimiz için totalCartItemPrice değişkeni tanımladık.
5: Burada amacımız sepetteki ürünlerin isimlerini koymak. Bu nedenle basit bir operasyon ile cartItem array’ı yani sepetteki ürünleri çekeceğimizi belirtiyoruz.
6: CartItems array’imizin içerisine cartservice listesini çektik.
7: TotalCartItem ve totalCartItemPrice’ı getirmek için ngOnInit olduğunda sepetteki ürün adedini bulmak istiyoruz. Bunun için her bir elemanın miktarını toplamamız gerekiyor. Bu nedenle burada “reduce” kullanıyoruz. Reduce(a,b)’de “a” bizim dönüş değerimiz yani totalCartItem’a atayacağımız değişken, “b” ise listedeki her bir elemanı temsil ediyor. Eğer ki burada bir lamda yapıp (=>) “a” dönüş değeri ve başlangıç değerini sıfır olarak tanımlar, “b” ‘nin her bir elemanın miktarını “a” ile topla ve dönüş değerini totalCartItem’a aktar dersek, eklediğimiz her bir eleman sepette görünecektir.
8: Aynı şekilde ürünlerin fiyatını yazmak için de yukarıdaki koddan yararlanıyoruz. Burada tek fark her ürünün miktarını “a” ile toplamadan önce product’ın birim fiyatı ile çarpmamız gerekiyor.
cart-summary.component’imizi oluşturduktan sonra bizim data’nın olduğu yerde bir değişiklik yaparak view katmanını oluşturmamız gerekiyor.
Bunun için app.component’e yerleştirdiğimiz üst menünün dropdown ile ilgili olan kodlarını cart-summary.component.html’in içine yerleştiriyoruz.
...
<app-cart-summary></app-cart-summary>;
...
Ancak sayfayı açtığımızda sepet alanının görünmesi için html olarak aldığımız sepet kodlarının yerine yani app.component’in içerisine cart summary’i component’ini çağırmamız gerekiyor.
<div class="btn-group">
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sepetim : {{totalCartItemPrice}}{{totalCartItem}} // Adım 1
</button>
<div class="dropdown-menu dropdown-menu-right">
<button *ngFor="let item of cartItems" class="dropdown-item" type="button">
{{item.product.productName}} - ({{item.quantity}}) // Adım 2
</button>
</div>
</div>
cart-summary.component.html
Kodları cart-summary’e çekerken yapımızın dinamik olması için bir iki ekleme yaptık.
1: Sepetimizin toplam tutarını ve adedini one way binding ile bu alana çektik.
2: Ayrıca sepette eklediğimiz ürünler kendini tekrar eden bir yapı sergileyeceği için ürün isimleri ve ürün adedinden kaç tane olduğunu ngFor ile ürünleri sepetimizin içerisine listeledik.
Pipe and Built-in Pipe
Aslında pipe “|” bu işaretten geliyor ama tanım olarak ele alacak olursak; Kullanıcıya gösterdiğiniz datayı daha faklı formatlarda göstermenize yarayan tekniklerdir diyebiliriz. Örneğin oluşturduğumuz cart-summary-component’imizde bulunan totalCartItemPrice’ın yanında para biriminin de görünmesi için hazır pipe’lardan | currency ’i kullanabiliriz.
...
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sepetim : {{totalCartItemPrice | currency:'USD':true}} - ({{totalCartItem}})
...
cart-summary.component.html
burada currency’yi tek başına kullansaydınız otomatik USD’yi getirecekti. Biz burada göstermek için USD yi yazdık yalnız yanında bulunan “true” ‘nın ayrı bir özelliği var. Kullandığınız para biriminin iconunu göstermek isterseniz true, sadece yazı olarak yazmasını isterseniz false yazmalısınız.
Hazır pipe’lara başka bir örnek daha verelim. Örneğin sepetimizde bulunan ürün isimlerinin büyük harf yapmak için | uppercase yazmamız yeterli olacaktır.
<div class="dropdown-menu dropdown-menu-right">
<button *ngFor="let item of cartItems" class="dropdown-item" type="button">
{{item.product.productName | uppercase}} - ({{item.quantity}})
</button>
</div>
</div>
cart-summary.component.html
Bu tür pipe’lar tabiki Angular’ın içerisinde hazır olarak gelmektedir. Şimdi, asıl konumuz olan “kendimiz nasıl pipe yaratırız ?” sorusuna cevap arayalım. Örneğin listelediğimiz ürünlerin KDV oranlarını da bize gösterecek ve ürünlerin listelenmesinde bize filtre sağlayacak bir arama pipe’ı yapalım. Pipe yaratırken aynı component yaratır gibi oluşturuyoruz.
ng g pipe product-vat
ng g pipe product-filter
Projenizde düzenli görünmesi açısından service, pipe gibi dosyalarınızı klasör içerisinde tanımlarsanız projenizin okunurluğu açısından artı sağlamış olursunuz.
KDV ve filter pipe’ını product’da kullanacağımız için bulunduğu alana pipe adında klasör ve bu klasörün cd ile içerisine girerek pipe’larımızı oluşturduk.
import { Pipe, PipeTransform } from '@angular/core';@Pipe({
name: 'kdv'
})
export class KdvPipe implements PipeTransform {transform(value: any, args?: any): any {
return null;
}
}
kdv.pipe.ts
CLI ile bir pipe oluşturduğunuzda en saf hali yukarıdaki gibidir. Bir class’ın pipe olabilmesi için pipe deglerasyonu ile beslenmesi ve pipeTransform isimli interface’i implemente ediyor olması gerekir. Name yazan alan ise bizim component’lerde yaptığımız gibi view alanında kullanacağımız ismi temsil eder. PipeTransform’un eklenmesi sayesinde özelliği olan transform’u kullanır ve datamızı değiştiririz. Transform fonksiyonunu inceleyecek olursak “value” burada gelen datanın türü, “args” parametreler demektir. “args?” sonuna soru işareti kullanıldığında “ben parametre istemiyorum aynı zamanda boş da gelebilir” demiş oluyoruz. Burada parantezlerin en sonunda bulunan “:any” dönüş tipini temsil eder. Yani tüm işlem bittiğinde bize ne döneceğini buraya yazarız.
import { Pipe, PipeTransform } from '@angular/core';@Pipe({
name: 'kdv'
})
export class KdvPipe implements PipeTransform {transform(value: number, args?: number): number { // Adım 1
var vatPercentage:number=18; // Adım 2
if(args){
vatPercentage = args;
}
return value +(value/100*vatPercentage);
}
}
kdv.pipe.ts
1: KDV ile işlem yapacağımız için bizim burada ihtiyacımız olan value , göndereceğimiz parametre(args) number ve dönüş tipimizin(any) number olması gerekir.
2: vatPercentage adında bir değişken tanımladık ve default olarak değerini 18 olarak belirliyoruz. Bu bizim standart KDV oranımızı temsil ediyor. Sonrasında eğer KDV olarak 18 den farklı bir değer tanımlanırsa o değeri okumasını, eğer farklı bir değer tanımlanmamışsa KDV oranını hesaplamasını istediğimiz bir koşul oluşturuyoruz.
...
<h5><b>{{product.productName | uppercase}}</b></h5>
<p>{{product.quantityPerUnit}}</p>
<h6>{{product.unitPrice | currency:'USD':true}}</h6>
<span>
<b>KDV'li tutar :</b>
({{product.unitPrice | kdv | currency:'USD':true}})
</span>
</li>
...
product.component.html
Pipe’ımızı oluşturduktan sonra product.component.html sayfamızda fiyat alanının yanına pipe’ımızı çağırmamız yeterli olacaktır.
Yukarıda hatırlarsanız “if” ile bir koşul oluşturmuştuk. Eğer sabit %18 KDV oranından başka bir değer tanımlamak istersek KDV’nin yanına o oranı yazmamız gerekir.
...
<b>KDV'li tutar :</b>
({{product.unitPrice | kdv:100 | currency:'USD':true}})
</span>
</li>
...
product.component.html
Şimdi ikinci pipe’ımız olan filtre’ye başlayalım.
import { Pipe, PipeTransform } from '@angular/core';
import { Product } from "../product"; @Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
// Adım 1
transform(value: Product[], filterText?: string): Product[] {
// Adım 2
filterText = filterText ? filterText.toLocaleLowerCase() : null;return filterText ? value.filter((p: Product) => p.productName.toLocaleLowerCase().indexOf(filterText) !== -1) : value;
}
}
filter.pipe.ts
1: İşimiz product listesi ile ilgili olduğu için product’ı sayfamıza dahil ediyor, aynı zamanda value alanınına kullanacağımız değerler product olduğu için product array’i tanımlıyoruz. FilterText bizim parametre olarak göndereceğimiz değerdir. Filtre alanında kullanacağımız textbox’a yazacağımız string ifadeler bizim aşağıda bulunan listemizi şekillendirecektir. Bu nedenle parametre olarak filterText ve değer olarak da string yazıyoruz. Son olarak dönüş değerimiz filtrelenen değerler olacağı için product array olarak tanımlıyoruz.
2: Burada iki koşul oluşturuyoruz.İlk koşul filtre alanına bir giriş yapılıp yapılmadığına bakıyor. Filtreleme sırasında büyük küçük harf ayrımı yapmakla uğraşmamak için eğer giriş varsa girilen değeri küçük harfe çeviriyoruz. İkinci koşul ise; eğer filterText varsa o zaman gelen değer yani asıl listeyi javascript filter metodu ile filtre işlemi gerçekleştiriyoruz. Filtreleme yaparken product array’in içerisinde bulunan her bir product için gezerek bu product nesnelerine p ismini veriyoruz. P artık bizim ürün nesnelerimizi ifade ettiği için onların productName’lerine ulaşabiliyoruz. Yapacağımız arama küçük harf ile olacağı için aramaya karşılık gelecek şekilde productName’lerimizi küçük harfe çeviriyoruz ve hata olmasının önüne geçmiş oluyoruz. Ardında arama yaptığımız productName bizim yukarıda belirttiğimiz filterText’imizin içerisinde olup olmadığını(indexOf) kontrol ediyoruz.Bu ürün array’imizin içerisinde varsa bu ürünü value’ya yani product array’e döndürüyoruz. Eğer bulamazsan index numarasını -1 döndürmesini yani bir şey yapmamasını belirtiyoruz. Sonda bulunan “: value” kullanıcı hiçbir yazı yazmazsa yada yazdıktan sonradan tüm textbox’ı temizlerse, kısacası hiçbir şey yapmazsa tüm product array ‘i yazdırması için oluşturuyoruz.
Two way binding ve ngModel
Şu ana view alanında süslü parantezler içine yazdıklarımızda sadece one way binding yani sadece okuma yaptık. Eğer bir değişkenin view katmanında okunmasını aynı zamanda bir değişiklik olduğunda kullandığımız değişkenin de aynı zamanda değişmesini istiyorsanız, o zaman two way binding işlemi yapmanız gerekmektedir. Two way yapacağınızda banana(muz) notation denilen “[(ngModel)]” bir yöntem kullanıyoruz. Bu yöntemde kullanılan input, select, textarea..gibi form denetimlerinde ngModel’i dahil ettiğinizde ngModelController oluşturmuş oluyorsunuz. Yani elementlerin değerlerini başka elementlere bağlama imkanı oluşturuyorsunuz.
...
import { FormsModule } from "@angular/forms";imports: [
...
FormsModule
],
...
app.module.ts
two way binding’in çalışabilmesi için bizim ngModel’i app.modul’e tanımlamamız gerekiyor. Bu işlem sonrasında ngModel’i artık product sayfamızın içerisinde kullanabiliyoruz.
...
// Adım 1
<div class="col">
<input class="form-control" type="text" placeholder="type something..." [(ngModel)]="filterText">
</div>// Adım 2
<div class="col" *ngIf=filterText>
<b>{{filterText}}</b> için arama yaptınız.
</div>// Adım 3
<div class="col mt-3">
<ul class="list-group">
<li *ngFor="let product of products | filter:filterText" class="list-group-item">
...
product.component.html
1: Oluşturduğumuz filter.pipe’ını product sayfamıza entegre etmek için öncellikle bir input oluşturuyoruz. Input alanımız yukarıda bahsettiğimiz two way işlemi ile filterText’e bağlıyoruz.
2: Burası sadece sayfamızı güzelleştirmemiz için kullanıcı arama yaptığında yapılan aramanın ne olduğu kalın yazı ile göstermek için yaptığımız bir adım. ngIf eğer arama yapılırsa bu yazının görüntülenmesi, yazılmazsa o alanın çıkmaması için yazıyoruz.
3: Burada aynı KDV pipe’ımızı bağladığımız gibi filter pipe’ımızı bağlıyoruz. Ardından “args” olarak kullandığımız filterText değerimizi tanımlıyoruz.
Şimdi uygulamamızı bir ileriye taşıyalım ve sayfamızda sol tarafta ürünlerin kategorilerinin geldiği, kategorileri tıklanıldığında ilgili datanın gösterildiği bir filtre alanı yapalım.
Öncelikle bir category.component ve kategori datalarımıza erişebilmek için içerisine category.servis oluşturalım. Ardından kategorilerimizi bir product gibi tutacak bir nesneye ihtiyacımız var bu nedenle category.ts oluşturuyoruz.
Service nesnesi oluşturma;
export class Category{
categoryId:number;
categoryName:string;
seoUrl:string;
}
category.ts
Kategoriler için önceden kullanmış olduğumuz nortwindapi adresinden yararlanacağız “http://northwindapi.azurewebsites.net/api/categories". Category.ts de ilk iki satırın ne olduğunu zaten anlamışsınızdır, ancak son satırda bulunan seoUrl size biraz farklı gelebilir. SeoUrl normalde id üzerinden yaptığımız çalışmaları bu sefer isim üzerinden ilerleyebilmemiz için kullanacağız.
Service oluşturma;
import { Injectable,Inject } from '@angular/core';
import { Http, Response } from '@angular/http'
import { Observable } from 'rxjs';
import { map,catchError, tap } from "rxjs/operators";
import {Category} from '../category';@Injectable()
export class CategoryService {
constructor(
private http:Http, // Adım 1
@Inject("apiUrl") private apiUrl // Adım 2
) { }
getCategories():Observable<Category[]> // Adım 3
{
return this.http.get(this.apiUrl+"/categories").pipe(
map(response=>response.json())
);
}
}
category.service.ts
1: Http request işlemlerini yapabilmek için “http” yi sayfamıza dahil ederek http’ye tanımlıyoruz.
2: Burası daha önce product.servis’i yazarken app.modul’ün içerisine tanımladığımız,“apiUrl” adında local nesnemizi, category.service’imizin içerisine de parametre olarak inject ettik.
3: getCategories adında asenkron bir işlem yapacak ve sonucunda observable bir category array’i döneceğini belirttiğimiz bir fonksiyon tanımladık.
Component oluşturma;
// Adım 1
import { Component, OnInit } from '@angular/core';
import { Category } from './category';
import { CategoryService } from '../category/service/category.service'@Component({
selector: 'app-category',
templateUrl: './category.component.html',
styleUrls: ['./category.component.css'],
providers: [CategoryService] // Adım 2
})
export class CategoryComponent implements OnInit {constructor(private categoryService: CategoryService) { } // Adım 3
categories: Category[];getCategories() {// Adım 4
this.categoryService.getCategories().subscribe(response => this.categories = response);
}
ngOnInit() {
this.getCategories();
}}
category.component.ts
1: Servisimizi kullanabilmek için ilgili nesneleri sayfamıza dahil ettik.
2: Category.service’miz local bir service olduğu için provider olarak sayfamıza ekledik.
3: Servis işlemleri için categoryService değişkene ve kategori işlemleri için categories değişkenine atamalar yapıyoruz.
4: Product.service’de yaptıklarımız gibi burada da observable servisimize subscribe oluyoruz ve bağlandığımız datadaki nesneleri categories değişkeninin içine atıyoruz.
View oluşturma(html);
<h4 class="page-header">Kategoriler</h4>
<ul class="list-group">
<li *ngFor="let category of categories" class="list-group-item">
{{category.categoryName}}
</li>
</ul>
category.component.html
component’imizde subscribe ile ulaştığımız dataları en son categories değişkenine atamıştık. Bu dataların view katmanında categoryName’lerini *ngFor ile listeliyoruz.
Ana root’dan çağırma;
...
<div class="col">
<div class="row">
<div class="col-3">
<app-category></app-category>
</div>
<div class="col-9">
<app-product></app-product>
</div>
</div>
</div>
app.component.html
Son olarak yaptığımız tüm işlemleri ana root’da yayınlamak için app.component’de category component’imiz buraya dahil ettik.
Routing
Routing Single Page uygulamalarda, farklı birden çok sayfanın görüntülenmesi için kullanılan bir özelliktir. Sayfalama yapısı sayesinde projede dağınık bir mimari oluşturmayı, yönetilebilirliği ve erişim kolaylığı sağlar. Kısaca uygulamayı çeşitli sayfalara ayırarak, her birini farklı controllerlara bağlar. Route’lar aslında bize bir route geldiğinde hangi sayfaya gideceğini, yani isminden de anlaşılacağı gibi yönlendirme yapan bir ayarlamadır.
Sepet uygulamamızın kategori alanında, bastığımız her kategorinin ilgili ürünlerini çağıracağı bir yapı oluşturacağız. Bu yapı için Angular’ın routing yapısından yararlanacağız. Yapımızı oluştururken mvc projelerde olduğu gibi ana bir layout veya master page oluşturacağız. Bizim uygulamamızda ürünlerin listelendiği alan sürekli değiştiği için, bu alan haricinde kalan alanları master page gibi düşünebilirsiniz. Router’ı kullanabilmeniz için app.module’e eklemeler yapmanız gerekiyor.
...
import {Routes, RouterModule} from '@angular/router'
...
const appRoutes: Routes = [
...
]
@NgModule({
...
imports: [
...
RouterModule.forRoot(appRoutes),
...
],
...
})
app.component.ts
Bu ayarların ardından bize kalan burada sadece routing ayarlarını yapmamız yani const kısmının içini doldurmamız kalıyor. Burada route’ları appRoutes adında bir değişkene tanımlıyoruz. Ayrıca router’ı tanımlarken “forRoot” adında özel bir kod tanımlıyorsunuz. ForRoot, bizim tanımladığımız root ayarlarını uygulamamızın kullanmasını için zorunlu kılmış oluyorsunuz.
const appRoutes: Routes = [
{
path: "",
redirectTo: "products",
pathMatch: "full"
},
{
path: "products",
component: ProductComponent
},
{
path: "products/:seoUrl",
component: ProductComponent
}
]
app.component.ts
path : Yönlendirmenin sağlanabilmesi için tetiklenecek olan adres belirtilmektedir. Yani “ana sayfadaki adresimize bir istek geldiğinde, şuraya git” demiş oluyoruz. Path alanını boş bırakılıp herhangi bir adres tetiklenmiyorsa bu ana dizin manasına gelmektedir. Biz yukarıda redirectTo ile ana sayfamızın product olacağını belirttik.
redirectTo : Bir tür tetiklemedir. Bu özellik ana dizin ile tetiklendiğinde sanki kendisindeki adres tetiklenmiş gibi davranır ve ilgili adresin belirtilen component’ine yönlendirme sağlar.
pathMatch : Full ile kullanıldığında adresin tam olarak belirtilen şekilde olmasına zorluyorsunuz.
component : path kısmında yazan her ne ise ilgili component’ini açması için kullanılır. Yani adres kısmına “../products” yazdığımızda product.component’i çağıracaktır.
Path’lerimizin arasında “products/:seoUrl” şeklinde bir tanımlama yaptık. Bu tanımlama daha önce hatırlarsanız category component’i ile uğraşırken normalde id üzerinden yaptığımız çalışmaları bu sefer isim üzerinden ilerleyeceğimizi bu nedenle seoUrl oluşturduğumuzu söylemiştik. Buradaki path alanında eğer categori datamızdan ilgili seoUrl parametresini yazarsak, o kategorinin datasının gelmesini sağlayacağız. Bunun için category.component.html de geliştirme yapmamız gerekiyor.
<h4 class="page-header">Kategoriler</h4>
<ul class="list-group">
<li *ngFor="let category of categories"
class="list-group-item"
routerLinkActive="active"
routerLink="products/{{category.seoUrl}}">
{{category.categoryName}}
</li>
</ul>
category.component.html
Burada routerLink direktif tanımlaması yapıyoruz. Router buraya geldiğinde “products/{{category.seoUrl}}” seoUrl’i adres çubuğunda product’ın altında gibi görünecek şekilde datamızı çekiyoruz. Kısacası RouterLink özelliği ile linklere adreslerini belirliyoruz. Ayrıca routerLinkActive direktifi ile tıklandığında yani aktif olduğunda class ataması yapıyoruz. Biz burada bootstrap’ın özelliği olan active class’ından yaralanıyoruz. Bu işlemlerin sonuca ulaştıracak son aşama app.component.html’e root’umuzu tanımlamak olacaktır.
...
<div class="col">
<div class="row">
<div class="col-3">
<app-category></app-category>
</div>
<div class="col-9">
<router-outlet></router-outlet>
</div>
</div>
</div>
app.component.html
Eskiden bu alanda product component’imizi çağırıyorduk. Ancak şuan sayfamız zaten bizim product component’imiz olduğu için bu alana router’ımızı tanımlak bizim zaten aynı sonuca ulaşmamızı sağlamaktadır.
Buraya kadar yaptıklarımızla sadece sayfalama yapımızın temelini oluşturmuş olduk. Şimdi tıkladığımız kategorinin ilgili datalarının gelmesini sağlayabiliriz. Bu işlemin başlangıcı için olan category.component.html’de çalışma yaparak event kodunu yazmaya başlayalım.
<h4 class="page-header">Kategoriler</h4>
<ul class="list-group">
<li *ngFor="let category of categories"
class="list-group-item"
(click)="onSelect(category)"
routerLinkActive="active"
routerLink="products/{{category.seoUrl}}">
{{category.categoryName}}
</li>
</ul>
category.component.html
Herhangi bir click olduğunda onSelect isimli fonksiyonumuzu çağırıyoruz. Bu fonksiyonu çağırken her “a” tagında bir event oluşturmak için fonksiyonu gönderirken evet olarak kategorileri gönderiyoruz.
...
categories: Category[];
selectedCategory: Category;
...
ngOnInit() {
...
}
onSelect(category?: Category) {
if (category) {
this.selectedCategory = category;
} else {
this.selectedCategory = null;
}
}}
category.component.ts
Gönderdiğimiz click event’ini karşılamak için category.component.ts’de onSelect fonksiyonu tanımladık. Bu fonksiyonun öncesinde selectedCategory diye bir değişken ve içine category’leri tanımladık. OnSelect metodunda eğer bir kategori seçili ise o kategoriyi getirmesini, eğer seçili değilse tüm kategorileri göstermesini sağlayacak koşul yapısını oluşturduk. Bu koşul yapısı bize bir url oluşturmuş olacak. Bu işlemlerin sonrasında product’ın bizim yapımızı karşılayabilmesi için birkaç güncelleme yapmamız gerekiyor.
...
import { ActivatedRoute } from "@angular/router"; // Adım 2
...
export class ProductComponent implements OnInit {...
constructor(
private activatedRoute:ActivatedRoute, // Adım 3
...
) {}ngOnInit() {
this.activatedRoute // Adım 4
this.activatedRoute.params.subscribe(params=>{
this.getProduct(params["seoUrl"]);
})
}getProduct(seoCategory:string){ // Adım 1
this.productService.getProducts(seoCategory).subscribe(response =>{
this.products = response
})
}
...
}
product.component.ts
1. getProducts’a seoCategory adında string bir kategori yollayacağız. Yalnız bu işlem sonrasında getProducts’ımız hata verecektir. Çünkü, getProduct’ımızda hem seoCategory yok, hem de ngOnit’de bulunan getProduct’ımızdan seoCategory’yi yollamıyoruz. Bu durumu çözmek için bizim seoCategory’yi router’dan almamız gerekmektedir.
2. Router’dan dataları okuyabilmemiz için activatedRouter servisinden yararlanıyoruz.
3. ActivatedRoute servisini kullanabilmek için değişken tanımladık.
4. Route operasyonun değerleri asenkron çalışır. Bu nedenle aktive edilmiş route’un parametrelerine subscribe oluyoruz ve params’dan seoUrl’e ulaşıyoruz. Bu seoUrl’i getProduct fonksiyonuna gönderiyoruz. Bu işlem sonunda hata almaya devam edeceğiz. Çünkü, eski product servisimiz parametresiz çalışan bir yapıydı.
...
constructor(private http:Http, @Inject('apiUrl') private apiUrl) { }getProducts(seoUrl:string):Observable<Product[]>{
if (seoUrl) {
return this.http.get(this.apiUrl + "/products/" + seoUrl)
.pipe(map(Response => Response.json()))
} else {
return this.http.get(this.apiUrl + "/products/")
.pipe(map(Response => Response.json()))
}
}
} product.service.ts
Burada parametre olarak seoUrl’i ekledik ve bir koşul tanımladık. Eğer kişi seoUrl’i göndermişse ona göre datayı gösterecek, eğer göndermemiş ise tüm datayı gönderecek.
Uygulamamızda her şey düzgün bir şekilde çalışıyor ancak kullanıcı deneyi açısından bir eksiklik var. Kategori seçimini yaptıktan sonra tüm ürünleri getirmek için adres çubuğundan düzenleme yapmanız gerekiyor. Bunu daha kullanışlı hale getirmek için kategorilerin başına “hepsi” adında bir çalışma yapmalıyız.
<h4 class="page-header">Kategoriler</h4>
<ul class="list-group">
<li class="list-group-item"
routerLinkActive="active"
[routerLinkActiveOptions]={exact:true}
routerLink="products"
(click)="onSelect()">
Hepsi
</li>
<li *ngFor="let category of categories"
...
category.component.html
[routerLinkActiveOptions]={exact:true} routerLink=”products” kullanımı; sizin zorunluluk belirten aslında bir koşulunuzdur. Eğer bu direktifleri yazmazsanız kategoride her hangi bir link’e tıkladığınızda onların url’lerinin de içinde “product” geçtiği için hatalı görüntü elde edeceksinizdir. Burada exact kullanarak “birebir bizim çağırdığımız adres olsun” demiş oluyorsunuz. Tüm datayı getirmek için yapacağımız işlem ise (click)=”onSelect() alanını. Burada onSelect’i boş olarak göndermemizin sebebi, daha önce cattegory.component.ts’de yazdığımız boş gelirse “tüm datayı göster” koşuldan kaynaklı.
Routin’e örnek olarak bir de dropdown dan sepete gitmeyi yapalım.
...
<div class="dropdown-menu dropdown-menu-right">
...
<div class="border-bottom my-2"></div>
<div class="text-center">
<b>
<span routerLink="my-cart">
Sepetim
</span>
</b>
</div>
</div>
</div>
cart-summary.component.html
Bunun için öncelikle dropdow’umuzun içerisinde “sepetim” diye bir router link tanımlıyoruz. Bu link ile my-cart router’ına gitmesini istiyoruz. Yalnız app.modul’unde belirlediğimiz router’larda my-cart’a karşılık gelecek birşey olmadığı için çeşitli eklemeler yapmamız gerekiyor.
...
},
{
path: "my-cart",
component: CartComponent
},
]@NgModule({
declarations: [
AppComponent,
ProductComponent,
...
app.module.ts
Eğer route olarak my-cart gelirse, o zaman cart.component’i açması gerektiğini belirtiyoruz. Artık dropdown’umuzda “sepetim” linkine tıkladığımızda sayfamız açılacaktır.
Şimdi uygulamamızın son etkileşimi olan sepet sayfasında sepete eklediğimiz ürünlerin listelenmesi ve listeden silinmesini yapalım.
import { Component, OnInit } from '@angular/core';// Adım 1
import { CartItem } from './cart-item'
import { Product } from '../product/product'
import { CartService } from "../cart/service/cart.service"@Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css']
})
export class CartComponent implements OnInit {constructor(private cartService: CartService) { } // Adım 2
isProductRemoved: boolean = false; // Adım 3
cartItems: CartItem[] = [];ngOnInit() {
this.cartItems = this.cartService.list(); // Adım 4
}removeFromCart(product: Product) { // Adım 5
if (confirm("Emin misiniz ?")) {
this.cartService.removeFromCart(product);
this.isProductRemoved = true;
}
}}
cart.component.ts
1. Sepet detaylarını göstereceğimiz için sepetle ilgili dosyaları import ettik. Sepetteki ürünler için cartItem, ürünleri listeleyeceğimiz için product ve sepete eklenen ürünlerin servisi için cart servis’i sayfamıza dahil ettik.
2. Kart servisini kullanabilmek için dependency injection ile servisimizi değişkene atadık ve kullanabilir hale getirdik. Kart servisimiz global tanımlı olduğundan burada provider olarak component’imize eklemedik.
3. Ürünün silinip silinmediğinin kontrolü için bir boolen değer tanımlıyoruz. Ayrıca sepetteki ürünleri listelememiz için cartItem’i nesne vasıtası ile oluşturuyoruz.
4. Daha önce hazırladığımız list metodunu servislerin altından çekerek cartItems’ın içini doldurduk.
5. Sepetten ürün çıkartabilmek için bu metodu tanımlıyoruz. Bu metoda product türünde Product geleceğini belirtiyoruz. Silme işlemi gerçekleşeceği zaman koşul kısmında javascriptin confirm özelliği ile kullanıcıya “evet/hayır” sorusu soruyoruz. Cart.service.ts’de önceden hazırladığımız removeFromCart metodunu içerisinde product içe çağırıyoruz. Ayrıca sepetten bir ürün silme işlemi gerçekleşirse “true” olarak tanımlıyoruz. Bu true boolean değeri sayesinde sepetten ürün silinip silinmediğini *ngIf’lerle takip edeceğiz.
<h4 class="page-header">Sepetim</h4>
// Adım 1
<table class="table table-responsive" *ngIf="cartItems.length>0;else cartEmptyContent">
<thead>
<tr>
// Adım 2
<th>Product Name</th>
<th>Description</th>
<th>Unit Price</th>
<th>Units In Stock</th>
<th>Total</th>
<th></th>
</tr>
</thead>
<tbody>
// Adım 3
<tr *ngFor="let cartItem of cartItems">
<td>{{cartItem.product.productName}}</td>
<td>{{cartItem.product.quantityPerUnit}}</td>
<td>{{cartItem.product.unitPrice |currency:'USD':true}}</td>
<td>{{cartItem.quantity}}</td>
<td>{{cartItem.product.unitPrice*cartItem.quantity |currency:'USD':true}}</td> // Adım 4
<td>
// Adım 5
<button class="btn btn-xs btn-danger" (click)="removeFromCart(cartItem.product)">
remove from cart
</button>
</td>
</tr>
</tbody>
</table>
<ng-template #cartEmptyContent>
<div class="alert alert-info">
Sepetinde ürün bulunmamaktadır.
</div>
</ng-template>
cart.component.html
1. İçinde eleman olduğunda tabloyu göstermesi için ngIf tanımlıyoruz. Else kullanımını ayrı olarak aşağıda inceleyeceğiz.
2. Product.ts’nin içinde bulunan nesnelere karşılık gelecek şekilde tablomuzu oluşturuyoruz.
3. Sepette bulunan ürünler tekrar edileceği için ngFor ile döngüye sokuyoruz.
4. Sepete aynı üründen birden fazla eklendiğinde o ürünlerin toplam fiyatını göstermek için hesaplama yapıyoruz.
5. Burada tablodan silme işlemi yapıyoruz. Bunun için cart.component.ts’de tanımladığımız removeFromCart metodunu click event olarak kullanıyoruz. Silme işlemi ngFor’un altında olduğu için cartItem.product ile ekstra bir işlem yapmadan silme işlemini tamamlamış oluyoruz.
Else kullanımı
Yukarıdaki örnekte şuana kadar gördüğümüz ngIf direktiflerinden farklı bir kullanım oluşturuyoruz. Angular 4 ile gelen else özelliği sayesinde oluşturduğunuz koşul sağlanmadığında görünecek content’i else ile işaret edebiliyorsunuz. Örneğin burada else ile sayfanın aşağısında bulunan cartEmptyContent template’ini işaret ediyoruz. Sonuç olarak liste boş olduğunda bize sepetimizin boş olduğunu söyleyen uyarıyı ekrana getiriyoruz.
...
<table class="table table-responsive" *ngIf="cartItems.length>0;else cartEmptyContent">
...
<ng-template #cartEmptyContent>
<div class="alert alert-info">
Sepetinde ürün bulunmamaktadır.
</div>
</ng-template>
cart.component.html
Uygulamayı geliştirmek için daha çok geliştirme yapabiliriz anacak makale yeterince uzun oldu daha fazla uzatmak istemiyorum. React ve Redux eğitimlerindeki gibi tanımlar üzerinden ilerlemek yerine bu makalede bir uygulama üzerinden anlatmayı uygun gördüm.
İşin özeti
- Önce bir servis nesnesi oluşturuyorsunuz,
- Bu servis nesnesini servis dosyanızın içerisine dahil ediyorsunuz,
- Component’inizde servisinizi global veya local olarak çağırıyorsunuz. Global olacaksa root modulüne, local olacaksa component’in içerisindeki provider’a yazıyorsunuz.
- Component’de çektiğiniz observable dataya subscribe olarak ulaşıyorsunuz.
- View(html) alanında çektiğiniz datayı one way binding yada two way binding ile ulaşıyorsunuz. Bu binding işlemleri sırasında ngIf,ngFor..gibi direktiflerle fazla kod kalabalığından kurtulmuş oluyorsunuz.
- Son olarak oluşturduğunuz component’i ismi ile ana root app.component’inize ekliyorsunuz.
işlem bitiyor. İşleyiş üç aşağı beş yukarı bu şekilde. Aslında işlem basit ama pratik gerektiriyor.
Bana soracak olursanız kullanım açısından düzenli görünse de proje içerisinde oradan oraya çok sürükleniyorsunuz. Bu aslında kod yazanın yoğurt yiyişiyle de alakalı ama bana ezbere çok dayalı şey var gibi geldi. Pipe’lar direktifler veya binding işlemleri kullanımı açışından bana biraz uzak kavramlar. React öğrenirken ve proje geliştirirken daha çok keyif almıştım. Tabi seçim sizin…
Uygulama boyunca angular mimarisi, component, injector, CLI kurulumu, kurulum sonrası yüklenen dosyaları tanıma, modül kavramı, provider’ın servisler için önemi, local servis, global servis, http servis kullanımları, rx6 ile gelen yeni özellikler, provider üzerinden parametre ile dependecy injection, third party paket eklemek, event bind, ngDoCheck kullanımı, one way ve two way binding farkları, rooting, ngIf ve ngFor direktifleri, pipe’lar…gibi konulara mümkün oldukça detaylı girmeye çalıştım. Umarım sizin için de yararlı olmuştur. Bir sonraki makalede görüşmek üzere..