İçeriğe geç

Angular(2 & 4)  içinde  AJAX request/ Sunucu iletişimlerimizi yönetebilmek için tıpkı Angular 1 de olduğu gibi dahili bir Http servis gelmekte. Http Service ve Observable yapısına bakacağımız bu yazıda, ilk olarak genel değişikliklerden bahsederek başlayalım.

Angular(2 ve 4 sürümünü kastedeceğiz) içinde Http servisimiz artık promise tabanlı değil Observable tabanlı bir ara katman ile çalışma imkanı sunmakta. Promise yapıyıda hala kullanabilsekde, varsayılan olarak artık Observable türü kullanılmakta.

Genel olarak Javascript ve Web tarayıcılarının sunucu iletişimini gerçekleştirdiği süreç (Ajax mimarisi açısından) Asynchronous / Asenkron bir yapı ile gerçekleşmekte. Bu yapıyı yönetmek için genelde şimdiye dek bir tanesi Javascript içinde dahili bir yapı olan Callbacks diğeri Callback ler ile çalışma zorunluğunu azaltmak ve daha okunaklı bir kod yapısı sağlamak için kullandığımız bir yapı olan Promise modelini olarak iki alternatifimiz mevcuttu.

Promise yapısı calback yapısınının bir çok zorluğunu giderse de yinede bu yapınında bazı eksiklikleri vardır. Bu durumdan dolayı Observable alt yapısıda yeni bir alternatif olarak karşımıza çıkmış oldu.(Observable kavramının kendisi yeni olmasada, bu şekilde front-end mimari de kullanılması açısından yeni bir durum söz konusu)

Kısacacası Asenkron yapı ile ilgili yeni ve yetekenli bir alternatifimiz mevcut “Observables” . Asenkron kod yapısını yönteme şekli olarak bir sıralama yapacak olursak şöyle bir sıralama yapabiliriz.

1. Callbacks 2. Promises 3. Observables

Observables kavramı ilk bakışda biraz karmaşık gözüksede aslında çok karmaşık yada yeni değil. Javascript ve Angular  dünyası açısından bakacak olursak;

Reactive Programing (Tasarım Kalıbı) –>RxJs (Yardımcı Kütüphane ) –> Observable (Yetenekli bir javascript nesnesi/veri türü )

, şeklinde bir hiyerarşi söz konusu diyebilriz.

Reactive Programing kavramı muhtemelen bu günlerde sıkça duyduğunuz kavramlardandır. Javascript dünyasında bu programlama modelini gerçeklemek için yada başka bir değişle Javascript in mevcut yapısında bu programlama modeli için bazı eksiklikleri kapatmak ve daha kolay bir alt yapı sağlamak için RxJs gibi yardımcı kütüphaneleri kullanmaktayız.

Angular özelinde, Reactive Programing kavramından ziyade “Observable” alt yapısı için sıkça başvuracağımız RxJs bize Asynchronous/Asenkron kod yapısı ile çalışmak için yeni ve daha kullanışlı bir imkan sağlamakta. Ve Angular 1 deki promise nesnesini “Observable” ile değiştirmekde. Http isteklerimiz sonrasında artık response/cevap nesnesini, bir Promise olarak değil Observable yapısında bir nesne olarak geri alıyor olacağız.

RxJS ve Observable yapısı, en temel haliyle yine tarayıcının XMLHttpRequest nesnesisini kullanan Http servisimiz, bu katmanın üzerine observable arayüzünü uygulayarak Async/Asenkron kod yapısı için daha yönetilebilir daha esnek bir yapı sunuyor. Başka bir değişle RxJs kütüphanesini kullanarak orjinal, XMLHttpRequest nesnesi ve Callback çalışma mantığını alıp, JQuery nin HTML/DOM yapısı için bize sunduğu zengin ve esnek yapıya benzer bir yapıyı bize asenkron kod yapımız için sağlamış oluyor.

 

Angular  Htpp iletişimi için bu yapıyı zorunlu kılmayarak, istediğimiz başka kütüphaneleride Http isteklerimiz için kullanmamıza imkan vermekte. Örneğin,  RxJs ve Observable yapısını kullanmak istemiyorsanız “tavsiye etmesemde”  Jquery gibi baska bir kutuphanenin  http yapısını yada başka alternatifleri kullanabilmekteyiz.

Angular 2 de ki en önemli iyileştirilmelerden olan zone  kavramı ve alt yapısı sayesinde angular kendi alt yapısı dısğındaki bu değişiklikleri de takip edecek kadar yetenekli.($scope.$apply() yada  $digest() gibi ekstra çağrılara ihtiyaç duymadan , farkli kutuphanelerin http alt yapisinida kullansak, change detection / değişiklik takibi mekanızmasını sorunsuz kullanabilme imkanımızdan bahsediyorum.)

HTTPMODULE VE HTTP SERVICE

Kullanım olarak Angular 1 deki kullanıma yakın olan ve sade bir alt yapı sunan HTTP servisimiz angular ın ana parçlarından olsada, yukarıda bahsettiğimiz “başka kütüphanelerin Http benzeri alt yapılarını kullanabilme imkanı sağlaması” açısından, angular 2 nin core / ana paketiyle birlikte gelmemekte. Bu yüzden uygulamıza ayrıyeten HttpModule nü eklememiz gerekmekte.

Bu modül, bir çok http ihtiyacımız için yardımcı servisler ve metodları barındırmakta. Htpp servislerini kullanabilmemiz için uygulamamıza  HttpModule ü import etmemiz yeterli.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';

@NgModule({
  imports: [BrowserModule, HttpModule],
  declarations: [PonyRacerAppComponent],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}

Bu import ile beraber, artık Http servisimizi istediğimiz yere Inject edebileceğiz. Örneğin;

@Component({
  selector: 'http-service',
  template: `<h1>Http işlemleri</h1>`
})
export class PonyRacerAppComponent {

  constructor(private http: Http) {
  }

}

Yukarıda import ettiğimiz Http; sinifi aslinda tek basina istemci/sunucu ileitişimi açısından az çok bütün ihtiyaçlarımızı karşılamakta.

Fakat, Typescript ile birlikte daha iyi bir tip desteği sağlayabilmek adına Response yada Header, gibi diğer sınıflarıda daha verimli bir tip desteği için bu sürece katabilmekteyiz. HTTP sınfı ile aşağıdaki Http verbs / http methodlarını kullan imkanımız bulunmakta:

  • get
  • post
  • put
  • delete
  • patch
  • head

Yukarıdaki tüm metodlar tıpkı angular 1 olduğu gibi ana request metodunun biraz daha özelleşitirilmiş halleri. Http sınıfının yapısına buradan bakabilirsiniz.  Örneğin, http://sunucu.com/urunler adresine bir GET request göndermek istediğimizde aşağıdaki iki kullanımda aynı şeyi yapmakda. Sadece get() daha açık ve kolay bir kullanım sunmakta.

1- Get yardımıcı ile

http.get(`/urunler`)
  .subscribe(urunler => {
    this.urunler = urunler;
  });

2- base request sınıfı ile

http.request(`/urunler`, { method: RequestMethod.Get })
  .subscribe(urunler => {
    this.urunler = urunler
  });

Bu arada, http.request() metodu gibi diğer tüm kısa yol metodlarımızda(get(), post() vb. ), RequestOptions nesnesini kabul etmekte. Böylece daha düzenli ve yapısal olarak daha saglam http istekleri oluşturmamızı kolaylaştırmaktalar.  Örneğin çoğu zaman, http isteğimize bir Url parametresi eklememiz gerekecektir. Aşğıdaki http isteğine bakalım.

let siralama="sirala="+siralamaKriteri// kullanicidan bir deger aldigimizi dusunun, ornegin "enyeni" gibi
let url = "http://sunucu.com/urunler?"+siralama;
http.get(url)
  .subscribe(response => {
    console.log(response.json());
    // ürün listesi sirali olarak.
  });

Yukaridaki kullanimin yanlis bir tarafi yok sadece Typescript kullandiğimiz için , tip desteğini kullanarak daha düzenli bir şekilde yazabilriz. Örneğin yukarıdaki isteği şu şekilde de yazabiliriz.

let siralama= new URLSearchParams(); 
siralama.set('sirala',siralamaKriteri); 
let options = new RequestOptions({search: siralama});
let url ="http://sunucu.com/urunler" ;
http.get(url, options)
.subscribe(response => { 
    // ürün listesi sirali olarak. 
    console.log(response.json()); 
});

Yukarıdaki yapı daha düzenli bir alt yapı ile http isteğimizi oluşturmamıza imkan veriyor, belki biraz daha uzun ama örneğin URL encoding vb  işlemleri bizim için otomatik olarak yerine getirmiş olacak. Yukarıdaki örnekte, RequestOptions sınıfı kullanarak url parametrelerimizi tnaımladık, ama eğer işlemiz sadece query parametrelerini atamak ise, RequestOptions yerine, kısa yola olan params nesnesini kullanabiliriz.

let url ="http://sunucu.com/urunler" ;
http.get(url, {params:{siralama:'yeni' , sayfa:5}})
.subscribe(response => { 
    // ürün listesi sirali olarak. 
    console.log(response.json()); 
});

Http servislerimizde genelde http header ile de sunucuya bilgi göndermemiz gereken durum çoktur. Örneğin, Authorization token /yetkilendirme tokeni , gibi .

let headers =new Headers();
headers.append('Authorization'`Bearer ${token}`);
let url = "http://sunucu.com/urunler"
http.get(url,new RequestOptions({headers}))
  .subscribe(response => {
    // yetkilendirilmiş bir web isteği için header kullanımı
    console.log(response.json());
  });

Yukarıdaki örneklerde hep Get metodu ile istekde bulunduk, peki sunucumuza body/gövde içinde veri gönderimi yapmamız gereken durumlar ? Örneğin, bir form bilgisini Post metodu ile sunucuya gönderelim;

let yeniUrun ={
 isim:"Tabak",
 fiyat:12.25
}
http.post(url, yeniUrun)
.subscribe(response => {
    console.log(response.json());
  });

Gördüğünüz gibi, post metoduna  (yada put medotuda olabilir) adres bilgimizden sonra ikinci parametre olarak geçtiğimiz  veri,  isteğin body/gövdesine eklenmiş oldu.

OBSERVABLES

Angular 2 icinde  Http servis kullanımı açısından “Observable” kavramına bakarak devam edelim. Http servisi açısından diyorum çünkü, Observable yapısı sadece http isteklerimiz için değil, Angular içinde bir çok fakrlı şekilde kullanma imkanımız var. İlk olarak, bundan önce(Angular 1 ve Jquery dahil) kullandığımız promise yapısını kısaca hatırlayalım,

$http.get("http://sunucu.com/urunler")
success(function(res) {
 console.log(res);
})
.error(function(err) {
    console.log(err);
});

Yukarıdaki http request yapısı ile, http isteğimizi sunucuya gönderiyor ve eğer istek başarılıysa, gelen sonuç ile birlikte çalıştırabileceğimiz callback metodumuzu “success()” çalışmasını sağlıyorduk. Eğer istek başarısız ise diğer callback metodumumuz “error()” çalışıyordu. Diğer bir alternatif olan “Q” benzeri daha gelişmis promise yapılarınıda şu şekilde kullanıyorduk ;

var deferred = $q.defer();
$http.get('http://sunucu.com')
    .then(function (response) {
        deferred.resolve(response.data);
    })
   .catch(funnction{
    deferred.reject(response);
    })
 return deferred.promise;

 

Bu yapı ile biraz daha esneklik kazanmış oluyorduk, örneğin deferred.resolve yada deferred.reject in bize verdiği müdahale imkanı ile,  promise yapısının tam istediğimiz gibi  çalışıp çalışmadığını yada then() ile ardi ardina promiseleri chain/bir birine baglama imkanimiz gibi bazi guzel ozellikler de vardi.

Observable yapısı ile yukarıdaki yapıyı daha yetenekli ve pratikde “daha fazla” müdahaleye açık bir hale getiriyoruz. Promise yapısında ki “İsteği gerçekleştir ve başarılı olursa x işlemini(örneğin; success() ) yap başarısız olursa y işlemini yap(örneğin deferred.reject(response))” şeklindeki akışı, Observable yapısı ile  şuna benzer bir senaryoya dönüştürüyoruz

“Bir akıma abone ol(Stream Subscription) ve bu akımı dinle. Eğer Subsriction sana veri yollarsa tanımladığım işlemleri yap”

Yukarıdaki cümledeki (Stream Subscription) kavramının kullanılması, http isteğinin bir strean/akım olarak gelmesinden değil, Observable yapısının  tasarım şekli itibariyle  “Subscribe to a Stream / Bir akima abone ol” mantiği üzerine inşa edilmiş olmasından. Sadece Http istekleri açısından bakacak olursak, biraz havada kalabilecek bir kavram fakat, Observable yapısı sadece Http işlemleri için değil bir çok farklı işlemde kullanılabilecek bir alt yapı sunmakta(aslına bakarsanız, Asenkron yapının tamamını yönetmek için çok daha yetenekli bir yapı.) Genel olarak baktığımızda gerçekten çok esnek ve yetenekli bir yapı olduğunu ve Promise yapısını ciddi anlamda ileri taşıdığını görebiliriz. 

 Angular 2 içindeki Http istekleri açısından kullanımını inceleyerek devam edelim.

İlk olarak aşağıdaki kod a bakacak olursak, Http isteğimizi ile dönen response/cevap nesnesine subscribe() metodunu çağırarak abone oluyoruz. angular 2 içinde Http servisi varsayılan olarak bir Promise değil “Observable” nesnesi geri döndürmekte. Böylece abone olabilmekteyiz.

let url ="http://sunucu.com/urunler"
http.get(url, options)
  .subscribe(response => { 
    // ürün listesi sirali olarak.
    console.log(response.json());
  });<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

geri dönen, response/cevap içindeki veriye erişmek için, response nesnesi üzerinde response.json() yada response.text() gibi metodları kullanmaktayız. Böylece, sunucudan dönen veri json formatına yada text formatına otomatik olarak dönüştürülmekte. (Angular 1 sürümünde, json dönüşümü için ayrıyeten bir method çağrısına ihtiyacımız yoktu. Bu dönüşüm otomatik olarak yapılmaktaydı.)

Yukarıdaki örnekdeki kullanım şekli aslında eski promise yapısının çok da farklı bir hali değilmiş gibi gözüksede, arka planda çok ciddi değişiklikler var. İlk olarak, response/cevap nesnemiz Observable türünden dedik, yani Observable alt yapısının ve RxJs in bir çok özelliğini kullanabilmekteyiz.  İlk olarak yukarıdaki kodu biraz daha genişleterek devam edelim,

http.get(url, options)
.map(response => response.json()) // veriye abone olmadan once json donusumunu yap
 .subscribe( // veri artik javascript nesnesi, bu haline abone ol
   (urunler) => {console.log(urunler)}, //abone olunan veriyi kullan (onNext callback)
   (err) => {console.log(`Hata : ${err}`)}, // hata kontrolu (onError callback)
   () => {console.log("islem tamam")}// (onCompleted callback)
 );

Öncelikle, artık bir observable nesnemiz olduğu için Observable alt yapısının sağladığı bir çok metodu kullanabilmekteyiz. Yukarıda map() metodunu kullandığımız gibi. Bir çok metod ve yardımcı yapıyı kolayca method chaining/ metod bağlama ile ardı ardına kullanabilmekteyiz. Bu metodlara bu adresten bakabilirsiniz.

Örneğin, http isteğimiz bir sebebten başarısız olursa, basitçe .retry() metodunu da dahil ederek isteğimizin belirli bir sayıya kadar tekrar denenmesini sağlayabilriz ;

http.get(url, options)
.retry(3) // hata durumunda 3 kereye kadar tekrar dene
.map(response => response.json()) 
 .subscribe( 
   (urunler) => {console.log(urunler)},
   (err) => {console.log(`Hata : ${err}`)}, 
   () => {console.log("islem tamam")}
 );

Birden fazla Http isteğini beraber yapmak istediğimiz durumlarda yada bir birine paralel isteklerde de kolay bir çalışma imkani sunuyor Observable yapımız.

 Observable.forkJoin( 
        http.get(url).map((response) => response.json()),
        http.get(url2)
        .retry(2)
        .map((response) => response.json())
        .delay(100)
    ).subscribe(
      data => {
        this.urunler = data[0]
        this.indirimdekiUrunler = data[1]
      },
      err => console.error(err)
    );

Yukarida Observable yapisi icindedeki  forkJoin() metoduna, paralel olarak çalıştırmak istediğimiz, http isteklerini parametre olarak geçiyoruz. Http isteklerimiz Yine Observable olduğu için .map() , .retry() yada .delay() gibi istediğimiz metoduda sürece kolayca dahil edebilmekteyiz.  Örneğin, yukarıda delay() metodu ile , hata durumunda 100 milisecond bekle ve sonra yeninden dene demiş olduk.

Observable yapısı çok kullanışlı ve yetenekli bir alt yapı sunmakta. Alıştıkça daha çok kullanıp, uygulamamız içinde sadece Http istekleri için değil tüm Asenkron yapıyı daha rahat yönettiğinizi fark edeceksiniz. Şimdi gerçek bir örnekle devam edelim. Bir eğitim için hazırladığım bir örnekten aşağıdaki kategori ekleme ekranının bir küçük versiyonu yazalım

 

Angular cli ile ornek bir proje olusturup sayfa1 isminde bir component olusturalim, sayfa1 componentimizin icerigi baslangicta asagidaki gibi:

\src\app\sayfa1\sayfa1.component.html

<div class="col-md-12" style="margin:20px;">

  <h2>Kategoriler</h2>
  <div *ngIf="secilenKategoriler.length > 0 ;  else kategoriYok">

  </div>
  <hr>
  <h3>Kategori Ekle</h3>
  <form>
    <div class="form-group">
      <label> Kategori ara</label>
      <input type="text" class="form-control">
    </div>
  </form>
</div>

<ng-template #kategoriYok>
  <div class="alert alert-warning">
    <h5>Henuz bir kategori secilmedi</h5>
  </div>
</ng-template><span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span><span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span><span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

\src\app\sayfa1\sayfa1.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-sayfa1',
  templateUrl: './sayfa1.component.html',
  styleUrls: ['./sayfa1.component.css']
})
export class Sayfa1Component implements OnInit {
  secilenKategoriler = []
  constructor() { }

  ngOnInit() {
  }

}

bu eklemeler ile uygulamamız aşağıdaki gibi bir görünüe sahip oldu;

İhtiyaçlarımızı kısaca sıralayacak olursak,

  1. Kategoriler başlığı altında , arama sonrası seçtiğimiz kategorileri listeleyeceğiz.
  2. kategori arama işlemimiz için ilgili text box da ki her değişikliği takip edeceğiz
  3. textbox dan aldiğimiz değeri sunucuya göndereceğiz
  4. sunucudan gelecek değerler içinden kullanıcımız secim yapabilecek
  5. Seçilen değerleri tekrar sunucumuza göndereceğiz.

Süreç ilk başta genel olarak çok zor gözükmesede, aslında düşünmemiz gereken bir çok durum var, ilk olarak textbox için sürekli değişimi takip etmemiz lazım dedik ordan başlayalım.

Observable yapısını, sadece http istekleri için değil, hemen hemen tüm asenkron işlemlerimiz için kullanabileceğimiz den bahsetmiştik. Bizim örneğimizde, textbox tarafından fırlatılacak her türlü event / olayı , örneğin click, focus, change vs. bir stream haline getirebilir ve bu stream e abone olabiliriz.

Angular bu yapıyı bize bir üst seviye soyutlama ile sunduğu içinde, sıfırdan kendi Observable nesnemizi oluşturmamıza gerek yok, bir kaç küçük directive ile textbox ı bir reactive stream e yani abone olabielceğimiz bir akışa döndürebilmekteyiz.

İlk yapmamız gereken, textbox ı reactive bir forma dönüştürmek, bunun için ilk olarak html form elementimize ilgili directive leri ekleyerek devam edelim böylece Angular ın bize sunduğu reactive form yapısı ile Obervable nesnelerimizi otamatik olarak oluşturmuş olacağız.

İlk değişikliğimiz html tarafında,

<div class="col-md-12" style="margin:20px;">

  <h2>Kategoriler</h2>
  <div *ngIf="secilenKategoriler.length > 0 ;  else kategoriYok">

  </div>
  <hr>
  <h3>Kategori Ekle</h3>
  <form [formGroup]="aramaFormu">
    <div class="form-group" >
      <label> Kategori ara</label>
      <input formControlName="aramaDegeri" type="text" class="form-control">
    </div>

    <button class="btn btn-sm btn-success">Kaydet</button>
  </form>
</div>

<ng-template #kategoriYok>
  <div class="alert alert-warning">
    <h5>Henuz bir kategori secilmedi</h5>
  </div>
</ng-template>

Form etiketimize ilk olarak [formGroup]="aramaFormu" şeklinde, formGroup directive ini ekledik. Böylece, aramaFormu isminde bir reactive formumuz oldu. Bu forma artık componentimiz içinde erişebileceğiz.

Sonraki adımda ise, arama işlemini yapacağımız textboxa bir formControl directive atadık formControlName="aramaDegeri" böylece, bu textbox ı da reactive bir form control a dönüştürmüş olduk. (Bu yazıda, angular form işlemlerine detaylı değinmeyeceğiz, form üzerinden örneğimize devam etmemizin nedeni, Obervable yapısının farklı kullanım şekillerinide görebilmek)

Şimdi, kod tarafında formumuza ve ilgili değişkene bir observable olarak erişme zamanı. Componentimiz de ilgili değişiklikleri yaparak devam edelim;

import { Component, OnInit } from '@angular/core';
import {FormBuilder, FormControl, FormGroup} from "@angular/forms";

@Component({
  selector: 'app-sayfa1',
  templateUrl: './sayfa1.component.html',
  styleUrls: ['./sayfa1.component.css']
})
export class Sayfa1Component implements OnInit {
  secilenKategoriler = []

  aramaCtrl = new FormControl("")
  aramaFormu:FormGroup

  constructor(private fb:FormBuilder) {
    this.aramaFormu = fb.group({
      'aramaDegeri' : this.aramaCtrl
    })
  }

  ngOnInit() {
  }

}

component içinde, biri form control (textbox a karşılık) , diğeride formGroup(formun tamamı yöntebileceğimiz) iki yeni değişken tanımladık. Angular ile form işlemlerinın bu yazının ana konusu olmadığını tekrar hatırlattıktan sonra, ihtiyaç listemizdeki ilk sorunu çözebiliriz.

Angular , formControl ile, textbox ı artık bir Observable olarak dinlememize hazırladı. Yapmamız gereken tek şey, control üzerindeki değişikliklere abone olmak. Componentimiz sayfaya yazdırılıp, artık her şey hazır hale geldiğinde , textbox üzerinde olacak her türlü değişikliği dinlemeye başlayabiliriz, bunun içinde, en ideal nokta olan OnInit metodunu kullanacağız.

ngOnInit i aşağıdaki gibi düzenleyelim,

  ngOnInit() {
    this.aramaCtrl.valueChanges
      .subscribe(_val => console.log(_val))
  }

 

bu değişiklikten sonra, artık tetbox üzerindeki her değişiklik (Obervable olarak) abone olunabilir durumda. Bunun için, ilgili control ün valueChanges özelliğine(angualr tarafından otomatik her control için oluşturulacak Obervable) .subscribe() çağrısı ile abone olmamız yeterli .

Abone olduktan sonra, gelen her değişikliği şimdilik console ekranına yazdırıyoruz.

Görüntüde, değişiklileri alıp console yazdırdık ama bazı sorunlarımız var örneğin, textbox da bir değer olmasada, boşluk tuşuna bastığımda abonelik devam ediyor. Ve , çok hızlı yazdığımdada abonelik üzerindne akış devam ediyor. gerçek hayatta, ilk oalrak textbox da geçerli bir değer varsa abonelik başlatmamız daha uygun olacaktır.

Akışa abone olmadan önce verinin istediğimiz düzende olduğundan emin olmak için bazı filitreleri ekleyerek devam edelim. örneğin çok hızlı yazmayla ilgili bir sorunumuz, kullanıcı ne kadar hızlı yazarsa yazsın biz işi biraz ağırdan alalım, ve yaklaşık 250-300 mili saniye aralıkla değişiklikleri alalım, sonrasında, boşluk ve bizim için geçersiz olacak “örneğin sayılar” gibi değerleride filitreleyelim

Yeni haliyle aboneliğimiz,

ngOnInit() {
    this.aramaCtrl.valueChanges
      .debounceTime(250)
      .filter(_val => _val != false)
      .filter(_val => isNaN(_val))
      .subscribe(_val => console.log(_val))
  }

eğer sadece boşluk değeri yada harf yerine sayi girmeye çalışırsak, otomatik olarak bu değişiklik bizim için filitre edilecek. Ayrıca, kullanıcı çok hızlı yazsada, belirli bir süre duraklama ekledik.

Bu şekilde dilediğimiz kadar filitre ekleyebilmekteyiz. Şimdi veri artık istediğimiz kıvamda ozaman artık, aldığımız bu değerleri, http servisi ile , sunucuya gönderip, gelen sunucu kullanıcıya seçilmek üzere sunabiliriz.

İlk olarak yeni bir metod yazarak http işlemini gerçekleşitirelim. Bunun için öncelikle, http servisini componentimize Inject etmemiz gerekmekte.

import {Http} from "@angular/http";

http servisimizi import ettikten sonra, constructor icinde de injection kismini yapmak zorundayiz. Böylece, htp servisimizi kullanabileceğiz.

 constructor(private fb:FormBuilder , private http:Http) {
    this.aramaFormu = fb.group({
      'aramaDegeri' : this.aramaCtrl
    })
  }<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

ve metodumuz,

  private aramaYap(searchTerm:string) :Observable<any>{
    let url = `http://localhost:3000/api/categories/search`;
    return this.http.get(url, {params:{search:searchTerm}})
      .map(_res => _res.json())
  }

Metodumuz, parametre olarak aldığı değeri, bir url query parametesine çevirip, sunucumuza yollamakta, gelen cevabıda, json a çevirip geri döndürmekte. Burda önemli bir nokta, değeri değil Observable bir akımı olarak döndürüyor olmamız.

Şimdi elimizdei http requestlerimizi yazpıp bir Observable döndüren metodumuz oldu, artık bu yeni Observable ı bir önceki arama control ü ile birleştirebiliriz. Birleştirmekten kastımız nedir diye soracak olursak, basitçe şöyle diyebiliriz ;

Elimizdeki ilk observable olan, arama kutusu(textbox) ile ikinci obervable olan aramaYap metodunu bir noktada bileştirmek, . Bizim örneğimizde, “textbox ı takip et, değerleri filitrele ve değeri bir sonraki observable a aktar, oda aramasını yapsın ve sonucu bana getir” demiş oluyoruz.

Bunun için , RxJs ile gelen .flatMap() meodunu kullanabiliriz.

 ngOnInit() {
    this.aramaCtrl.valueChanges
      .debounceTime(250)
      .filter(_val => _val != false)
      .filter(_val => isNaN(_val))
      .flatMap(_val => this.aramaYap(_val))
      .subscribe(_val => console.log(_val))
  }

RxJs içinde kullanılabilir olan diğer operatörlere bu adesten bakabilirsiniz.

Bir iki küçük ihtiyacı daha karşılayıp örneğimizi bitirebiliriz, gelen sonuçları ekrana yazdırmak la başlayalım. Öncelikle html taragında küçük bir değişiklik yapalım. Form kısmına küçük bir ekleme yapalım

<div class="col-md-12" style="margin:20px;">

  <h2>Kategoriler</h2>
  <div *ngIf="secilenKategoriler.length > 0 ;  else kategoriYok">
    <label *ngFor="let kat of secilenKategoriler" (click)="kategoriSil(kat)" class="badge badge-pill badge-warning">
      {{kat?.name}}
    </label>
  </div>
  <hr>
  <h3>Kategori Ekle</h3>
  <form [formGroup]="aramaFormu">
    <div class="form-group" >
      <label> Kategori ara</label>
      <input formControlName="aramaDegeri" type="text" class="form-control">
    </div>
    <hr>
    <button *ngIf="gelenKategoriler.length > 0" ngFor="let kategori of gelenKategoriler" (click)="kategoriEkle(kategori)" class="btn btn-sm btn-succes">
      {{kategori?.name}}
    </button>
    <hr>
    <button class="btn btn-sm btn-success">Kaydet</button>
  </form>
</div>

<ng-template #kategoriYok>
  <div class="alert alert-warning">
    <h5>Henuz bir kategori secilmedi</h5>
  </div>
</ng-template>

component tarafında da bir kaç değişikliğe ihityacımız var öncelikle gelen kategorileri tutacak bir dizi ve kategorinin seçilmesi/kaldırılması durumunda yapmamız gereken işlemi tanımlayacağımiz 2 metod ile componentimizin son hali;

import { Component, OnInit } from '@angular/core';
import {FormBuilder, FormControl, FormGroup} from "@angular/forms";

import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/map';


import {Http, RequestOptions} from "@angular/http";
import {Observable} from "rxjs";

@Component({
  selector: 'app-sayfa1',
  templateUrl: './sayfa1.component.html',
  styleUrls: ['./sayfa1.component.css']
})
export class Sayfa1Component implements OnInit {
  secilenKategoriler = []
  gelenKategoriler =[]

  aramaCtrl = new FormControl("")
  aramaFormu:FormGroup

  constructor(private fb:FormBuilder , private http:Http) {
    this.aramaFormu = fb.group({
      'aramaDegeri' : this.aramaCtrl
    })
  }

  ngOnInit() {
    this.aramaCtrl.valueChanges
      .debounceTime(250)
      .filter(_val => _val != false)
      .filter(_val => isNaN(_val))
      .flatMap(_val => this.aramaYap(_val))
      .subscribe(_val => this.gelenKategoriler =_val)
  }

  private aramaYap(searchTerm:string) :Observable<any>{
    let url = `http://localhost:3000/api/categories/search`;

    return this.http.get(url, {params:{search:searchTerm}})
      .map(_res => _res.json().categories)
  }

  private kategoriEkle(kategori){
    this.secilenKategoriler.push(kategori)
    this.aramaCtrl.setValue("")
    this.gelenKategoriler = this.gelenKategoriler.filter(_k => _k._id != kategori._id)
  }
  private kategoriSil(kategori){
    this.secilenKategoriler = this.secilenKategoriler.filter(_k => _k._id != kategori._id)
  }

}

Bu yazımızda, genel olarak, angular http servisini observable yapısını tanımaya çalıştık.

Serinin bir sonraki yazısında görüşmek dileğiyle.