Ionic, SignalR ve TypeScript ile Mobile Uygulama Geliştirme Bölüm 3

Selamlar,

Bu makalede, Ionic 2. Makalesinden kalınan yerden konular anlatılmaya başlanacaktır. “team-home” sayfası ve “Ions-Tab” ile başlanıp, “Pull Request” konusu ile devam edilecektir. “team-home” sayfası aslında bir çeşit container’dır. Takım  (team) ve Standings(Sıralama) sayfalarının tablerinin ve içeriğinin gösterildiği sayfadır. Daha sonra Lodash kütüphanesi, tarihe göre filitreleme, alert ve toastController kavramları bu makalenin devam konularıdır. Son olarak tournament sayfası ile maklenin sonuna gelinecektir.

Ion-Tabs: Ionic mobile uygulamalarında tabler büyük önem taşımaktadır. Alttaki sayfada bu konuya örnek bir uygulama yazılmıştır. “ionic generate page team-home” komutu ile ilgili sayfa create edilir.

team-home.html: Hatırlarsanız Ana sayfa(my-teams.html) ve Menude(app.component.html) favori takımlar tıklanınca gidilen sayfa, takım anasayfası yani “team-home” page idi. Şimdi gelin bu sayfayı, detaylıcı hep beraber inceleyelim.

  • <ion-navbar color=’primary’>“: Yukarıdaki resimde de görüldüğü gibi, takım adının yazıldığı mavi renkli header kısmıdır.
  • “<ion-title>” : Takım adının yazıldığı “<ion-header>” altındaki yazı kısmıdır.
  • <button> ve <ion-icon name=”home”>” : İlgili button tıklanınca, en baştaki sayfaya yönlenilen “goHome()” methodu çağrılmaktadır.
  • “<ion-tabs>” : team-home sayfasının üstünde 2 farkı daha sayfa bulunmaktadır. İlgili tabler tıklandığında “TeamDetailPage ve “StandingsPage page sayfalarından biri görünür hale gelir.
    • tabTitle: Sayfanın altında görülen ve tabin üstünde yazan başlık kısmıdır. İlki “Team“, ikincisi “Standings“‘dir.
    • [root] : Tıklandığında gösterilecek olan sayfayı belirtir.
    • [rootParams]: Tab’e tıklandığında, ilgili sayfaya gidilecek olan parametre bu property ile tanımlanır. Bu sayfada ilgili parametre, tıklanan takım yani “team“‘dir. Not: Bu da bize, 2 sayfa arasında yönlenme yani navigasyon zamanı, sayfalar arasında parametre geçişinin nasıl olduğunu gösterir.
    • tabIcon : İlgili tab’de görünmesi istenen ikon burada tanımlanır.

team-home.ts: 

  • this.team=this.navParams.data” ile sayfaya önceden parameter olarak gönderilen takım bilgisi aktarılır. NavParamsın sayfada tanımlanabilmesi için, ‘ionic-angular’ kütüphanesinden import edilmesi gerekmektedir.
  • teamDetailTab ve standingsTab değişkenlerine ilgili sayfalar atanır. Ekelenecek “StandingsPage, TeamDetailPage, MyTeamsPage” ==> “../pages” altından sayfaya import edilir.
  • goHome()“: İlgili methodda “this.navCtrl.push(MyTeamsPage)” komutu ile gidilmeye çalışıldığında, ilgili ana sayfada geri ok tuşu görülmektedir. Halbuki ana sayfada geri ok tuşu görünmemesi gereken bir durumdur. Bunun üzerine ilgili navigasyon işlemi ==>”this.navCtrl.popToRoot()” şekilinde yapıldığında, doğrudan ana sayfaya gidilmekte ve geri ok buttonu görülmemektedir.

Şimdi sıra geldi tablere ait sayfaları oluşturmaya. “ionic generate page team-detail” ve “ionic generate page standings” komutları ile ilgili sayfalar yaratılır.

team-detail.html:

  • Pull-Refresh” – “<ion-refresher>“: “<ion-refresher (ionRefresh)=”refreshAll($event)”>” Meşhur :) sayfanın en tepesinde iken, ekranın aşağı doğru çekilerek datanın yenilenmesi işlemidir. Sayfa üzerindeki datanın yenilenmesi için “(ionRefresh)” eventindeki “refreshAll()” methodu çağrılır.

  • “<ion-card>”: Yukarıda görüldüğü gibi 3 boyutlu plate bir yüzeyin oluşmasını sağlayan bir elementtir.
    • “<ion-card-content>”: İlgili içeriğin yazılacağı containerdır. Bu örnekde “*ngIf=”teamStanding!=undifined”” ile “teamStanding” değişkenine birşeyler atanmış ise, score bilgisinin ekrana basılması sağlanmıştır. Böylece hatadan da kurtulunmuş olunur. Aksi takdirde null nesne ekrana basılmaya çalışıldığında, hata alınılması kaçınılmazdır.
    • “<ion-row>” : Aynı Html Table’da olduğu gibi sayfa bir satırın basılacağını bildirir.
    • “<ion-col>”: Sayfaya ilgili satır içinde kolon basılacağını belirtir. “width-50” tanımlaması, toplam satır genişliğini yarısının bu kolon tarafından kaplanacağını belirtir.
    • Record: {{teamStanding.wins}}–{{teamStanding.losses}}“: Takımın toplam kazandığı ve toplam kaybettiği maç sayısı ekrana basılır.
    • Coach: {{team.coach}}” : Takıma ait antrenör bilgisi, ekrana basılmaktadır.
    •  “<button outline (click)=”toggleFollow()” *ngIf=”!isFollowing”>” 2. bir kolon açılıp, yukarıda görüldüğü gibi favorilere ekle buttonu, eğer bu takım favorilere eklenmemiş(!isFollowing) ise gösterilir. Tıklama durumunda “toggleFollow()” methodu çağrılır. Son olarak “Add Favorite” yazısı ekrana basılır.
    • <button (click)=”toggleFollow()” *ngIf=”isFollowing”>” : Eğer takım takip ediliyor ise, o zaman da takibi bırak işlemi yapılacak button sayfaya basılır.
    • IONIC – Filter: Yukarıda görüldüğü gibi Tarihe göre bir filitreleme işlemi yapılmıştır.
      • “<ion-item>” : Sayfaya gurup halinde bir nesne topluluğu konucağı zaman, kapsayıcı containerın ta kendisidir.
      • “<ion-label>” : Text alanın yazılacağı labeldır.
      • “ion-toggle” : Sayfaya konan bir switch buttondur. “useDataFilter” değişkenine çift yönlü bağlanmıştır. (ionChange) eventinde, birazdan inceleyeceğimiz “dataChanged()” methodu çağrılmış ve yapılacak maçların listelendiği  datanın tarihe göre filitrelenmesini veya filitrenin kaldırılmasını sağlanmıştır.
      • “<ion-datetime>” : İlgili tarih elementi belli bir display Formatında yani “Ay/Gün/Yıl” görünümünde “dataFilter”  değişkenine çift yönlü bağlanıp “(ionChange)” eventinde, yani değişme durumunda “dateChanged()” methodu çağrılmıştır. “([disabled])=”!useDateFilter”” tanımlaması ile, hemen yukarısında tanımlanan toggle switch değerine göre, ilgili tarih filitresi aktif veya pasif hale getirilmiştir.

  • <ion-item *ngFor=”let game of games” (click)=”gameClicked($event,game)”>” Çekilen oyun datası gezilerek ekrana basılır. Tıklanma durumunda “Game” sayfasına gidilir. Maç bilgileri yukarıda görüldüğü gibi 3 kolon olarak ekrana basılır.
    • <ion-row> ve <ion-col>” ile ilk satır ilk kolona, maç tarihi “{{game.time | date:’M/d/yy’}}” ve tarihin kısa hali “{{game.time | date:shortTime}}” formatında basılır. İlgili datanın yanına “|” ile konan tanımlama, Angular 4 tarafında pipes yani eski adı ile filter olarak adlandırılmaktadır.
    • <p>{{game.homeAway}} {{game.opponent}}</p>” 2. kolona takım ve rakip takım yazılır. Hemen altına “{{game.location}}” yani maçın oynanacağı lokasyon belirtilir.
    •  “<h4>{{game.scoreDisplay}}</h4>” Son kolona da  maç skoru yazılır.
    • “<ion-badge [color]=”getScoreDisplayBadgeClass(game)”>{{getScoreWorL(game)}}</ion-badge>” Yukarıda yazdırılan skorun yanına, kazanılıp kaybedildiğini gösteren logo, “getScoreWorL()” methodu ile konturol edilip, yukarıda görüldüğü gibi ekrana kırmızı veya mavi logo şeklinde bastırılır.
    • Son olarak tıklanınca “goHome()” methodu ile ana sayfaya dönülen “Go Home” button’u konur.

team-detail.html:

team-detail.ts: Aşağıda görüldüğü gibi takım detay sayfasında maç score’u, yaptığı ve yapacağı maçlar, ilgili takımı favorilere ekleme ve tarihiye göre maçları filitreleme işlemleri yapılmaktadır.

  • allGames[]“: Tüm maçların tutulduğu listedir.
    • dateFilter”: Filitreleme amaçlı seçilen tarih bilgisini tutar.
    • games“: Tarihe göre filitreleme işleminden sonra, tutlan maçların bilgisidir.
    • isFollow” : İlgili takımın takip edilip edilmediğini tutan boolen değişkendir.
    • team” “NavParams” ile filitrleme amaçlı gönderilen oyun datası bu değişkende tutulur.
    • teamStandings” : Kazanılan, kaybedilen maç sayısı ve antrenör bilgilerinin tutulduğu data modeldir.
    • tourneyData” [ ]: Seçilen turnuvaya ait tüm maç bilgisinin tutulduğu dizidir.
    • useDateFilter“: Tarih seçildiği zaman, “true” değerini alani buna göre bir filitreleme olduğu anlamına gelen boolen bir değişkendir.

Şimdi gelelim kullanılan kütüphanelere:

  • NavParams“: Başka bir sayafadan gelinirken parametre almak, ya da başka bir sayfaya yönlenmek ve son olarak bulunan sayfadan başka bir sayfaya parametre gönderebilmek için kullanılır.
  • EliteApi“: Firebase olarak adlandırılan, ilgili Database’den verileri çekmek için kullanılan servisdir.
  • AlertController“: Sayfaya Alert ve Confirm pencereleri çıkartmak için kullanılır.
  • ToastController” Sayfanın altından belli zaman aralığında bildirim göndermek için kullanılır.

ionViewDidLoad()” : Zamanında :

  • this.team = this.navParams.data;” : Parametre olarak gönderilen takım bilgisi alınır.
  • this.tourneyData = this.eliteApi.getCurrentTourney();” İlgili method ile önceden çekilmiş olan bu takımın bulunduğu turnuva maçları, ilgili diziye aktarılır.

  •  Lodash :  Javascript için yazılmış, popüler bir utility kütüphanesidir. Array yani diziler, object ve string değerler için kolayca iterasyon ve manipülasyon işlemlerinin yapılmasını sağlayan, helper classları sayesinde linq şeklinde kod yazmamıza yardımcı olan çok faydalı bir kütüphanedir. “.npm install lodash –save” şeklinde projeye yüklenir. Sayfaya import işlemi ==>”import * as _ from ‘lodash’;” şeklinde yapılır. Peki neden kullanıyoruz? Gelin aşağıdaki örnek ile inceleyelim:
    • this.games =_.chain(this.tourneyData.games)” : Yukarıda görüldüğü gibi belli bir ID’ye göre çekilen Turnuva bilgilerinden games nodu, bir array haline getirilir. Yani herbir games nodu tekbir nod haline gelir.
    • .filter(g => g.team1Id === this.team.id || g.team2Id === this.team.id)” : Method ile aynı Linq’de olduğu gibi 1. veya 2. takım ID değeri, seçilen bu takımın ID’sine eşitse, yani seçilen takıma ait tüm maçlar getirilir.
    • .map(): Observer method ile yenen ve yenilen takımın belirlenmesi için kullanılır:
      • let isTeam1 = (g.team1Id === this.team.id);” seçilen takımın, ilk takım olup olmadığına bakılır.
      • let opponentName = isTeam1 ? g.team2 : g.team1;” : Rakip takım ismi, isTema1 değişkenine göre team1 ve team2’den hangisinin olduğuna karar verilir.
      • let scoreDisplay = this.getScoreDisplay(isTeam1, g.team1Score, g.team2Score);” : İlgili parametrelere göre score bilgisi, ilerde incelenecek olan “getScoreDisplay()” methodu ile ekrana basılır.
      • Yukarıda tanımlanan Games sınıfına ait “id, opponentName, time,  g.location, locationUrl, scoreDisplay, homeAway” alanlarına ilgili değeler atanır. Ve ==> filitrelenen tüm oyunlar, “this.allGames” değişkenine atanır.
      • this.teamStanding = _.find(this.tourneyData.standings, { ‘teamId’: this.team.id });” : Seçilen takıma ait Turnuva bilgilerinden standings bilgisi, seçilen takımın team.id’sine göre filitrelenir. Kısacası ilgili takımın puan sıralaması bulunur.
      • “this.userSettings.isFavoriteTeam(this.team.id).then(value => this.isFollowing = value);” : Seçilen takıma ait ID bilgisi ile, “userSettings.isFavoriteTeam()” methodu kullanılarak, ilgili takımın takip edilip edilmediği bilgisine bakılır ve “isFollowing” değişkenine boolen bir değer atanır.

  • getScoreDisplay(): İlgili method da amaç, oynanmış maçların scorelarını ve seçilen takımın kazanıp kazanamadığını göstermektir.
    • isTeam1” : Seçilen takımın, ilgili maçta ilk mi yoksa ikinci takım mı olduğunun belirlendiği değişkendir.
    • var teamScore= (isTeam1?team1Score:team2Score);” : Takımın score’u, parametre olarak gelen “isTeam1” değişkenine göre atanır.
    • var opponentScore = (isTeam1 ? team2Score : team1Score); ” : Rakip takımın score’u belirlenir.
    • var winIndicator = Number(teamScore) > Number(opponentScore) ? “W: ” : “L: “;” : Seçilen takımın kazanma durumuna göre “W” kaybetme durumuna göre “L” şeklinde sembolünün gözükmesi sağlanır.
    • winIndicator = Number(teamScore) === Number(opponentScore) ? “-: ” : winIndicator;” : Beraberlik durumu da :) bu şekilde “-” tanımlanmıştır.
  • goHome(): Ana Sayfaya yönlenmek için kullanılır. Burada durum biraz farklıdır. Tab yapılarda root’a kadar çıkıp ordan yönlenme işlemi yapılmalıdır. Yanda görülen “.parent” bu işe yaramaktadır=>”this.navCtrl.parent.parent.push(GamePage, sourceGame);” Bunun yerine bu kodu da denemenizi tavsiye ederim==>”this.navCtrl.push(GamePage, sourceGame);” Nasıl yanlış bir durumla karşılaşıldığına şahit olacaksınız.
  • gameClicked(): Bir maça tıklandığında, maça ait sayfaya gitmek için tıklanır.
    • “let sourceGame = this.tourneyData.games.find(g => g.id == game.gameId);” : Tıklanan maça ait data, “ID” değerine göre filitrelenip “sourceGame” değişkenine atanır.
    • “this.navCtrl.parent.parent.push(GamePage, sourceGame);” :  Çekilen maç datası, “GamePage” sayfasına parametre olarak gönderilir.

Ionic Tarih İşlemleri:

Ben bu projede moment.js kütüpahenesini kullandım. Moment kütüphanesi == > “npm install moment –save” komutu ile kurulur.

  • dateChanged() : Filitreleme amaçlı tarih seçildiği zaman (ionChange) event’i tetiklenir ve bu method devreye girer .
    • if(this.useDateFilter)” : İlgli switch’in açık olup olmadığına bakılır.
    • this.games = _.filter(this.allGames, g => moment(g.time).isSame(this.dateFilter, ‘day’));” : Lodash “_” ile filitreleme yapılırken, seçilen turnuvaya ait çekilen maçlar(allGames) içindeki her bir maçın zamanının, “g.time” == “isSame()” methodu ile seçilen tarihteki “this.dateFilter” değer ile gün cisinden “day” aynı olanlar ==>”games” [ ] dizisine atılır.
    • Eğer “this.useDateFilter” switch’i açılmamış ise, yani tarih için bir filitreleme yok ise tüm data “games” [ ] dizisine atanır.
  • getScoreWorL(): Projenin en kolay functionı :) Tek amacı, maça ait bir score var ise “game.scoreDisplay” bilgisini dönmek, yok ise boş ” ” bir string dönmektir.
  • getScoreDisplayBadgeClass() : Amaç takım kaybetmiş ise “danger” yani kırmızı, kazanmış ise “primary” yani mavi ve berabere ise gri yani “light” bir css rengi geriye dönmektir. Yukarıda belirtildiği gibi, maça ait “game.scoredisplay” propertsi içinde “W” veya “L” geçmesine göre kazanılıp kaybedildiği anlaşılmaktadır.

  • Ionic 2 Pull Requests

  • refreshAll() : “this.eliteApi.refreshCurrentTourney()” Seçilen turnuvadaki tüm maç bilgisi, offline yerine servis ile databaseden tekrardan çekilir. Datanın çekildikten sonra “subscribe(() => {” dönen yükleniyor işareti yani refresher kapatılır ==>”refresher.complete()“. Sayfaya ilk gelindiğinde, yükleme işlemi tamamlandığı an çağrılan “ionViewDidLoad()” methodu tekrardan çağrılarak, yeni çekilen datanın ekrana basılması sağlanır.
  • toggleFollow()” : Takım Detay sayfasında ilgili takımı ya favorilere eklemek ya da favorilerden çıkarmak için kullanılır. Bu iki duruma göre çalışacak aksiyonlar farklıdır.
    1. Durum Takibi Bırakma ==>”if (this.isFollowing) {” :

AlertController : Yukarıda görüldüğü gibi, sayfa üzerinde uyarı amaçlı pencerelerin client’a gösterilmesi amacı ile kullanılır. Bu durumda ilgili takım favorilerden çıkarılmak istendiğinde, “Takibi bırakmak istediğinize emin misiniz?” sorusu çıkartılır. 3 property’si vardır.

let confirm = this.alertController.create({” : Yanda görüldüğü gibi “Create()” methodu ile aşağıda görülen propertyler ile oluşturulur.

  • 1-) “title: ‘Unfollow?’,” : Açılan pencerenin başındaki başlık.
  • 2-) “message: ‘Are you sure you want to unfollow?’,” : Ekranda görünecek uyarı mesajı.
  • 3-) “buttons: [” : Ekranda görünecek buttonlar.
    • text:’Yes’” : İlk button text.
    • handler: () => {” : Tıklanma durumunda yapılacak işler. No cevabına basılmış ise:
      • this.isFollowing = false;” : Takip edilme işleminin sonlandığını belirten değişken.
      • this.userSettings.unforiteTeam(this.team);” : Servis tarafında ilgili method ile neler yapıldığı önceki yazılarda anlatılmıştır. 1-) Local Storage’dan ilgili takım çıkarılmıştır. 2-) Pub/Sub ==>Menüdeki favori takımlar arasından da çıkarılması için ilgili “favorites:changed” kanalına, “this.events.publish(‘favorites:changed’);” methodunun tetiklenmesi sağlanmıştır.
      • ToastController : Yukarıda görüldüğü gibi, ekranın altından çıkrılan uyarı mesajları için kullanılırlar.
        • let toast = this.toastController.create({” : Yanda görüldüğü gibi “Create()” methodu ile aşağıda görülen propertyler ile birlikte oluşturulur.
          • message: ‘You have unfollowed ‘ + this.team.name,” : Gözükecek mesaj içeriği.
          • duration: 2000,” : Ekranda kalma süresi.
          • position: ‘bottom’” : Sayfa üzerinde görüneceği yer.
        • toast.present();” : Uyarı mesajını gösteren komuttur.
    • Yes cevabına basılmış ise:
      • this.isFollowing = true;” : Takip et değişkeni true atanır. Böylece takım ismi yanına yıldız şeklinde followe buttonu gözükür.
      • this.userSettings.favoriteTeam()” : Bu method, önceki makalede anlatıldığı gibi, favorilere eklenen takımı “Local Storage“‘a ekler. Ve “Pub/Sub Event” ile menü sayfasındaki listenin de triger edilerek değişmesini sağlar.

team-detail.ts:

Şimdi gelin isterseniz, Ana sayfada tıklanan, yukarıda görülen “Find Your Tournament” buttonu ile gidilen “tournaments” sayfasını kodlayalım.

tournaments.html:  Yukarıda görülen sayfaionic generate page tournamentskomutu ile oluşturulur. Tüm turnuva maçları firebase databaseinden aşağıdaki gibi çekilir. “https://elite-schedule-app-1bbf9.firebaseio.com/tournaments.json

tournaments.html: Servisden çekilen turnuva dataları “tournaments[ ]” dizisine doldurulur ve her bir turnuva bilgisi gezilerek, turnuva ismi ekrana basılır. “(click)” tıklanma eventinde “itemTapped()” methodu ile o turnuvadaki takımların listelendiği “teams.html” sayfasına yönlenilir. Son olarak sayfanın sonuna, geri dönüş buttonu “goBack()” methodunu çağracak şekilde yerleştirilir.

tournaments.ts: 

  • ionViewDidLoad()“: Methodunda en başta bir “loader” create edilir ve “present()” methodu ile gösterilir.
    • this.eliteApi.getTournaments()” : “tournaments [ ]” dizisi servisden dönen json turnuva datası ile doldurulur.
    • loader.dismiss();” : Yükleniyor uyarısı kaldırılır.
  • “goBack()”: Methodunda “this.navCtrl.pop();” komutu ile en baştaki sayfaya gidilir.  Dikkat edilmesi gereken “this.navCtrl.push(TeamsPage);” komutunun kullanılması durumunda, ana sayfada back buttonunun görünmesi durumu oluşacaktır. Bu nedenle tercih edilmemelidir.
  • “itemTapped()”: Methodu ile “this.navCtrl.push(TeamsPage, tourney);” Teams.html sayfasına tıklanma durumunda, turnuva bilgisi de parametre olarak eklenip “TeamsPage” sayfasına yönlenilir.

teams.html: Seçilen turnuvaya ait takımların yukarıda olduğu gibi listesinin görüldüğü ve filitrelendiği sayfadır.

Geldik bir makalenin daha sonuna. Bu makalede Ionic ile nerde ise yapamıyacağımız hiçbirşeyin olmadığını gördük. Bolca hayatımızda kullandığımız “pull-request”‘in hiç de düşünüldüğü kadar zor olmadığını gördük. Filitreleme, guruplama gibi işlemlerde “Lodash” kütüphanesinin işleri nasıl da kolaylaştırdığına şahit olduk. Uyarı mesajları, sayfanın altından çıkan bildirimlerin nasıl yapılacağı, tarihe göre filitreleme ve switch yapısı, “ion-card” ile 3 boyutlu bir paletin çıkarılması gene bu makalede gördüğümüz Ionic kapsamındaki önemli toolardan sadece bir kaçıydı.

Bir sonraki makalede “Team” sayfasında kullanılan ION-SEARCH ve performans artışında büyük önem taşıyan “Standigs” sayfasında kullanılan VIRTUAL SCROLL konuları detaylıca anlatılacaktır. Bir sonraki makalede görüşmek üzere hoşçakalın.

Kaynaklar: Pluralsight Steve Michelotti, https://ionicframework.comhttps://github.com/ionic-team/ionic, Gaurav Saini’nin “Hybrid Mobile Development with Ionic” kitabından faydalandım.

Herkes Görsün:

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.