Angular: Bak Şu Konuşana!
Bir önceki yazımda Angular’da componentler arası bilgi alış verişinde yaygın olarak kullanılan @Input ve @Output değişkenlerinden bahsetmiştim.
Bu yazımda ise ViewChild
ve Service
aracılığı ile bilgili alış verişinin nasıl olduğundan bahsedeceğim. Projenin kaynak kodlarını yazının sonunda bulabilirsiniz. Hadi başlayalım.
1 2 3 4 5 6 7 8 9 10 11 12 |
İçindekiler: Bu Makalede aşağıdaki konuları içerecektir * Proje Senaryosu * Proje hazırlık. * @ViewChild ile PARENT componente bilgi aktarma? * Service aracılığı ile SIBLING componente bilgi aktarma * Sonuç * Proje Kodları (Github Repository) |
Senaryo önüme geldiğinde çok heyecanlandım.
– Senaryoyu okuyunca çok heyecanlandım.
– Çekerken biz çok eğlendik.
– Halkımızın da beğeneceğini umuyoruz.
Makalenin konusunun anlaşılması için “Loto Çekilişi” yapan bir proje yaratacağız. Projemiz 1 tane Parent component, 2 tane de Child componentten oluşacak. Genel yapı aşağıdaki şekilde olacak:
Componentler arası iletişim şu şekilde olacak: Child componentten, Parent componente ViewChild
ile ve Child componentten diğer Child(Sibling) componente Service
ile bilgi aktarımı olacak.
“3, 2, 1 kayıt”
Yönetmen koltuğuna geçip, senaryomuzu hayata geçirelim. Bunun için işe her zaman olduğu gibi Angular CLI kullanarak projemizi oluşturmakla başlıyoruz
Bilgisayarınızda Visual Studio Code ve node.js yüklü olmalı. Eğer yüklü değilse Visual Studio Code için burayı, Node.js için burayı tıklayın
Visual Studio Codeyi açtıktan sonra terminale aşağıdaki kodu yazarak yeni projemizi yaratıyoruz
1 2 3 4 5 |
ng new ng-component-communication |
Projemde bootstrap kullandım. Bootstrap işlemleri için burayı tıklayınız. Ayrıca makalede template sayfalarında kullandığım css işlemleri hakkında bilgi vermeceğim, konumuz @ViewChild ve Service. Ama css kodların tamamı github linkinde olacaktır. Oradan bakıp sizde uygulayabilirsiniz.
Servis ve Componentleri yaratıyoruz:
Aşağıdaki komutları terminal’de sırası ile çalıştırıyoruz.
1 2 3 4 5 6 7 |
ng g c components/cekilis-basla ng g c components/cekilis-sonuc ng g s service/cekilis-sonuc // servimizi yaratıyoruz |
İşlemin sonunda proje yapımız şu şekilde olacaktır.
@ViewChild ile parent componente bilgi gönderme(child → parent).
Parent componentin, doğrudan child component içinde bulunan fonksiyon ve özelliklerine (attribute)erişim yetkisi yoktur. “ViewChild” ile parent componente erişim yetkisi verilerek, child componenteki bilgilerin kullanılması sağlanır. Burada dikkat edilmesi gereken husus; Child component ekranda görüntülenmediği sürece parent componente herhangi bir erişim yetkisi verilemez. Eğer bu durum dikkate alınmazsa uygulama hataya düşebilir. Child componentin bilgilerini alabilmek için “AfterViewInit” yaşam döngüsü kancası(lifecycle hook) uygulamamız gerekmektedir. İlerde ViewChild hakkında detaylı bir makale yazmayı düşünüyorum. Şimdilik sadece Child componentten, parent componente bilgi aktarımını anlatacağım.
“cekilis-basla.component.ts” dosyasını açıp; cekilis
değişkeni oluşturuyoruz. bu değişkende mesaj ve geriSayım nesnelere olacak.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import {Component, OnInit} from '@angular/core'; import {CekilisSonucService} from '../service/cekilis-sonuc.service'; @Component({ selector: 'app-cekilis-basla', templateUrl: './cekilis-basla.component.html', styleUrls: ['./cekilis-basla.component.scss'] }) export class CekilisBaslaComponent implements OnInit { cekilis = { mesaj: 'Çekilişe Başlamak için Başlat Butonuna Basınız', geriSayim: -1 }; constructor(private cekisiSonucService: CekilisSonucService) {} ngOnInit() {} cekilisBaslat() { this.cekilis.mesaj = 'Sonuçların Açıklanmasına Kalan Süre'; this.cekilis.geriSayim = 10; const timerId = setInterval((x) => { this.cekilis.geriSayim -= 1; if (this.cekilis.geriSayim === 0) { clearInterval(timerId); this.cekisiSonucService.cekilisSonuc(); this.cekilis = { mesaj: 'Çekiliş Tamamlandı. Yeni Çekiliş İçin Başlat Butonuna Basınız', geriSayim: -1 }; } }, 1000); } } |
Parent componentimiz olan “app.component.ts” açıyoruz ve “cekilis-basla.component.ts” de bulunan bilgilere erişim için aşağıdaki işlemleri yapıyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import {Component, ViewChild} from '@angular/core'; import {CekilisBaslaComponent} from './cekilis-basla/cekilis-basla.component'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { @ViewChild(CekilisBaslaComponent) child; // Child componenti bir nevi parent componente enject ediyoruz date: string = new Date().toLocaleDateString(); } |
@ViewChild(CekilisBaslaComponent) child;
ile child
değişkenine CekilisBaslaComponent bulunan tüm özellikleri aktarıp, parent componente kullanması için yetki veriyoruz. child
değişkenine aktarmış olduğumuz ve “CeklisBaslaCompononet” bulunan cekilis
nesnesine ve onun içindeki mesaj ve mesajgeriSayım
nesnelerine ulaşım, “child.cekilis”,“child.cekilis.mesaj” ve “child.cekilis.geriSayım” şeklinde olacaktır. child
değişkeninde elde ettiğimiz bilgileri kullanmak için “ app.component.html” dosyasını açıp aşağıdaki işlemleri yapıyoruz. (7, 8 ve 10. satırda ilgili bilgileri kullandık)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<div class="container"> <div class="row"> <!-- item --> <div class="col-md-9 text-center "> <div class="panel panel-danger panel-pricing table-bordered"> <div class="panel-body text-center"> <p><strong>{{child.cekilis.mesaj}}</strong></p> <span *ngIf="child.cekilis.geriSayim >-1" class="fa-stack fa-1x"> <i class="fa fa-square fa-stack-2x"></i> <strong class="fa-stack-1x " style="color: white">{{child.cekilis.geriSayim}}</strong> </span> </div> <ul class="list-group text-center"> <app-cekilis-basla></app-cekilis-basla> <app-cekilis-sonuc></app-cekilis-sonuc> </ul> </div> </div> </div> <div class="row"> <div class="col-md-9 text-center "> <br> <p></p> <h5>{{date}}</h5> </div> </div> </div> </section> <router-outlet></router-outlet> |
Uygulamayı çalıştırdığımızda “Resim-2” de gösterildiği gibi “ Çekilişe Başlamak için Başlat Butonuna Basınız” yazısı ekranda görüntülenecektir. Bu bilgi child componenten, parent componente aktarılmış oldu. Resim-2 deki “Cekilişi Başlat” butonuna basıldığında parent componente yeni mesaj ve 10 dan geriye doğru sayım bilgisi gönderilecek.
Hatırlatma: “Loto Çekilişi” uygulamasında tüm componentler; uygulama açıldığında hemen gösterildiği için “AfterViewInit” life-cycle hook işlemi kullanılmamıştır.
Service ile bilgi paylaşımı
Birbiri ile bağlantısı olmayan componentler arası doğrudan bir bilgi paylaşımı bulunmamaktadır. Bu tarz componentler arası bilgi alış verişini “Service” aracılığı ile yapabiliriz. (child → child, child → parent, child → grandChild, child → subling vb.)
“cekilis-sonuc.service.ts” dosyasını açarak servisimizi yazmaya başlıyoruz. cekilisList
değişkeni sibling component olan “cekilis-sonuc.ts” de sonuçları göstereceğimiz değişkendir. Kullanıcı “ceklis-basla.component.html” dosyasında bulunan “Çekilişi Başlat” butonuna bastıktan 10 saniye sonra cekilisSonuc()
methodu tetiklenecek ve rastgele belirlenen sonuçlar cekilisList
değişkenine aktarılacak. Yeni liste dolduğunda getSonuc()
metodu ile sonuçlar “cekilis-sonuc.component.ts” dosyasına gönderilecek ve ekranda gösterilecektir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import {Injectable} from '@angular/core'; import {Observable, Subject} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CekilisSonucService { cekilisList: Subject<string[]> = new Subject<string[]>(); constructor() { } getSonuc(): Observable<any> { return this.cekilisList.asObservable(); } cekilisSonuc() { this.cekilisList.next(Array.from({length: 6}, () => Math.floor(Math.random() * 10).toString())); } } |
“cekilis-sonuc.component.ts” dosyamızı açıp aşağıdaki işlemleri yapıyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import { Component, OnDestroy, OnInit} from '@angular/core'; import {CekilisSonucService} from '../service/cekilis-sonuc.service'; import {Subscription} from 'rxjs'; @Component({ selector: 'app-cekilis-sonuc', templateUrl: './cekilis-sonuc.component.html', styleUrls: ['./cekilis-sonuc.component.scss'] }) export class CekilisSonucComponent implements OnInit, OnDestroy { listSonuc: string[] = ['?', '?', '?', '?', '?', '?']; subscription: Subscription; constructor(private cekisiSonucService: CekilisSonucService) { this.subscription = this.cekisiSonucService.getSonuc().subscribe(message => { this.listSonuc = message; }); } ngOnInit() {} ngOnDestroy() { this.subscription.unsubscribe(); } } |
14. satırda yazmış olduğumuz servisimizi enject(dependency injection) edip, 15. satırda serviste olacak değişiklikleri gözlemlemek için abone(subscribe) oluyoruz.
Servisimizi hatasız kullanabilmek için “app.module.ts” dosyasını açıp provider kısmına servisimiz tanımlıyoruz
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {AppRoutingModule} from './app-routing.module'; import {AppComponent} from './app.component'; import {CekilisBaslaComponent} from './cekilis-basla/cekilis-basla.component'; import {CekilisSonucComponent} from './cekilis-sonuc/cekilis-sonuc.component'; import {CekilisSonucService} from './service/cekilis-sonuc.service'; @NgModule({ declarations: [ AppComponent, CekilisBaslaComponent, CekilisSonucComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [CekilisSonucService], bootstrap: [AppComponent] }) export class AppModule { } |
Sonuç:
Projemizi çalıştırıyoruz. Terminale aşağıdaki kodu yazıyoruz
1 2 3 4 5 |
ng s --o |
Makalenin kaynak kodları: