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

Selamlar,

Bu makalede, Ionic 3. Makalesinde, kalınan yerden konulara devam edilecektir. Öncelikle Ionic mobil bir sayfa üzerinde, arama işleminin nasıl yapılacağı (Ion-Search) incelendikten sonra, yüzlerce kaydın bulunduğu bir sayfada performanslı scroll işlemi(Virtual Scroll) için neler yapılabileceği detaylıca incelenecektir. Daha sonra “Ion-Segment” ve görsel anlamda göze hoş gelen bir score sayfası ile makalenin sonuna gelinecektir.

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

Ionic-Search: 

  • <ion-searchbar placeholder=”Search” [(ngModel)]=”queryText” (ionInput)=”updateTeams()”>” : Listelenen takımların isimine göre arama yapılan, yukarıda sayfanın en üstüne konan bir kutucuktur. “queryText” string değişkenine two way binding olarak bağlanmıştır. “(ionInput)” yeni karşılaştığımız bir event’dir. Searchbar’a, yeni bir karakter girişi olduğu zaman “updateTeams()” methodu çağrılır. Aslında bir çeşit Textbox’ın “(input)” eventi olarak düşünülebilir.

  • *ngFor=”let division of teams”“:  Yukarıda görüldüğü gibi Turnuvaya ait takım bilgileri, “division” kolonuna göre guruplanıp listelenmiştir. Her bir gezilen division ismi  ekrana yazılmıştır.
  • *ngFor=”let team of division.divisionTeams”” : Her bir division ait takımlar gezilerek, isimlere ekrana basılmıştır. “(click)=”itemTapped($event,team)”” Tıklanma durumunda “itemTapped()” methodu ile, önceki makalelerde anlatılan “TeamHomePage” sayfasına yönlenilmektedir.

teams.ts:

  • Linq ve javascript kütüphanelerinde büyük kolaylık sağlıyan Loadash, bir başka sayfaya yönlenme işi yapan “NavController”,  yönlendirme sırasında paramtere almaya yarıyan “NavParams”, sayfa yükleniyor ibaresini çıkaran “LoadingController” , “EliteApi” servisi, sayfa başında import edilmiştir.
  • Tüm takımlar “allTeams [ ]” ve takımların ait oldukları division listesi de “allTeamDivisions [ ]” dizisine atanmıştır.
  • /* Commandli teams [ ] */ bölgesi, sayfada gösterilmek üzere geçici olarak basılan dummy data örneğidir.
  • this.loadingController.create(” : Yükleme sırasında Loading controller yaratılıp “Geting data..” şeklinde ekrana uyarı mesajı basılır.
  • this.eliteApi.getTournamentData(selectedTourney.id).subscribe(data => {” : Servis ile seçilen tournamentID’ye göre ilgili tüm takımların bilgisi çekilir.
    • “this.allTeams = data.teams;” çekilen takım bilgisi bu değişkene atanır.
    • this.allTeamDivisions=_.chain(data.teams)” : Artık ‘_‘=> Lodashin nimetlerini kullanmanın zamanı geldi. “_.chain“==> Linq gibi Guruplama işlemi yapabilmek için öncelikle gelen data içindeki teams data kümesi bir gurup altında toplanır. Aslında işlem yapılacak data kümesinin, sınırları belirlenmiş olunur.
      • “.groupBy(‘division’)” : Bildiğimiz groupBy’dır. Yani aynı “division” ismine sahip takımları bir gurupta toplar.
      • .toPairs()” : Yeni bir dönüş tipi belirlemek amaçlı kullanılan lodash methodu.
      • Önemli:.map(item => _.zipObject([‘divisionName’, ‘divisionTeams’], item))” : Yukarıda görüldüğü gibi dönen json datanın maplenip “divisionName”  ve “divisionTeams” adında 2 kolon şeklinde dönmesi sağlanmıştır.
    • this.teams = this.allTeamDivisions;” Maplenen Json data “teams” değişkenine atanır.
    • loader.dismiss();” : Yükleniyor uyarısı kaldırılır.
  • itemTapped()” : Her bir takım tıklandığında “TeamHomePage.html” sayfasına yönlenecek olan methoddur. Parametre olarak tıklanan takım gönderilmiştir.==> “this.navCtrl.push(TeamHomePage, team);

Ionic-Search ():

  • updateTeams()“:  Search alanına girilen her bir char character için listelenen takım isimlerine göre filitrelenir.
    • let queryTextLower = this.queryText.toLocaleLowerCase();” : Girilen her bir karakter “queryText” değişkenine “[(ngModel)]” two way binding şeklinde bağlanmıştır. Girilen tüm text küçük harfe çevrilip “queryTextLower” değişkenine atanmıştır.
    • “fiteredTeams [ ]”: Filitrleme işleminden sonra dönen result bu diziye atanır.
    • _.forEach(this.allTeamDivisions, td => {” : loads ile gezilen tüm this.allTeamDivisions [ ] dizisi içinde tüm kayıtlar gezilir.
      • let teams = _.filter(td.divisionTeams, t => (<any>t).name.toLowerCase().includes(queryTextLower));” : Gezilen kayıtlar içinde küçük harfler ile ismi “queryTextLower” değişkeni geçenler filitrelenir.
      • fiteredTeams.push({ divisionName: td.divisionName, divisionTeams: teams });” : Filitrelenenler “fiteredTeams [ ]” dizisine aktarılır.
    • “this.teams = fiteredTeams;” : Son olarak filitrelenen tüm data “teams[ ]” dizisine aktarılarak, ekrandaki listenin güncellenmesi sağlanır.

Örnek standigs sayfası:

Virtual Scroll :

Performans amaçlı çok önemli bir konudur. Sayfanın tamamın yerine görülen kısmının yüklenmesini sağlar.

standings.html: Önceki makaleleri hatırlarsanız “standings” sayfası “team-home” sayfasında bulunan standings tab’ine tıklanınca gidilen bir sayfa idi. Esas amaç takımların sıralamasının gösterilmesidir.

“ion-segment” :

  • En üstte filitreleme amaçlı “ion-segment” olarak kullanılan “Division” ve “All” tableri görülmektedir. Division seçilir ise, sadece takımın bağlı olduğu gurubun sıralaması gözükür iken, All seçeneğinde tüm guruplara ait takımların sıralaması gözükmektedir.
  • ion-list [virtualScroll]=”standings” [approxItemHeight]=”‘132px'” [headerFn]=”getHeader”” : Virtual scroll performans için çok önemlidir. Bir çeşit lazy load diyebiliriz. Tüm data en başta yüklenmez. Scroll yapıldıkça yüklenir. Bu yüzden de oldukça performanslıdır. “virtualScroll“==> İlgili listeyi dolduracak data kümesini ifade eder. Bu projede “standings[ ]” içinde gezilecek data kümesi olarak belirlenmiştir. “approxItemHeight“==> Sayfanın rendering anlamında büyük bir performans sağlar. Default’u 40px’dir. Sayfanın oluşumu sırasında, ayrıca bir hesaplama yapılmamasını sağlar. “headerFn” ==> Her bir gurup için konacak olan başlığı temsil eder. Bu projede her bir başlık için “getHeader()” function’ı çağrılmıştır. İlgii function’da herbir başlık için “standings ==> division” field’ı geri dönülmektedir. “color=”secondary”” Arka rengi yeşildir.

  • ion-item-divider color=”secondary” *virtualHeader=”let header”” : Her bir başlık için divison alanına karşılık, header değişkeni atanmıştır. Ve ekrana {{header}} başlığı basılmıştır.
  • ion-item *virtualItem=”let team”” : Tüm data içinde gezilen her bir segment kaydı, team değişkenine atanır.
  • <ion-row>” Her bir satır ve “<ion-col>” her bir kolon olarak ekrana basılır. Tüm kolonlar için ekrana basılan “team.wins, team.losses, team.winningPct, team.pointsFor, team.pointsAgainst, team.pointsDiff” yukarıda görülen standings[ ] listesinden, içinde gezilen sıradaki item’ından dönen kayıttır.
  • Not: Sayfanın sonunda görülen “*ngFor=’let division of allStandings‘” şeklinde gezilerek ekrana data basan yorumlanmış kod satırı, “virtualScroll” yapılmadan, ekran üzerine gösterilen datanın “virtualScroll” ile olanki durumuna göre performans farkını göstermek için yapılmıştır.

standings.html:

standings.ts:  Gelin şimdi yukarıda bahsedilen html sayfa nasıl dolduruluyor, hep beraber inceleyelim.

  • Constructer’da başka bir sayfaya yönlenmek ve parametre göndermek amaçlı NavController, Navparams ve EliteApi service Dependency Injection ile sayfaya eklenmiştir.
  • this.team = this.navParams.data;” : Seçilen takım parametre ile alınmıştır.
  • let tourneyData = this.eliteApi.getCurrentTourney();” :  Seçilen turnuvaya ait tüm data çekilmiştir.
  • “this.standings = tourneyData.standings;” : Seçilen turnuvaya ait “standings”, yani sıralama datası atanmıştır.
  • this.allStandings = _.chain(this.standings)” : Şeklinde “lodash” kullanılarak doldurulan “allStandings” datasına virtualScroll kullanıldığı için gerek kalmamıştır.
  • this.allStandings = tourneyData.standings;” Tüm standings datası “allStandings” değişkenine atanmıştır.
  • this.filterDivision()” : Başta default seçilen takımın bağlı olduğu division’a ait sıralama(Standings) bilgisi, “lodash” filter kullanılarak ==>”_.filter(this.allStandings, s => s.division === this.team.division);” filitrelenmekredir. “All” seçeneği seçilir ise tüm standings bilgisi dönülmektedir.
  • getHeader()” : Belli bir “Division”gurubuna göre başlık basılıp bir çeşit guruplama yapılmıştır. Bu örnekde, gelen data division’a göre belli bir sırada gelmektedir. Yani farklı divison’a ait data farklı sıralada gelmemektedir. Doğal olarak ==> “if (recordIndex === 0 || record.division != records[recordIndex – 1].division) {” eğer ilk kayıt ise veya bir önceki ve bir sonraki kayıt’a ait division datası farklı ise başlık bilgisi basılır. Çünkü artık farklı bir guruba geçilmiş demektir.

Game.html: team-detail.html” sayfasında ilgili maç bilgisi listelenmektedir. Tıklanan maç detayı “Game.html” sayfasında gösterilmektedir. Bu sayfada amaç görsel olarak ionic mobile bir sayfada neler yapılabildiğinin, hep beraber incelenmesidir.  “ion-content” container içinde 3 row bulunmaktadır. Sıra ile bu 3 row’u inceleyelim.

  • En başa konan “<ion-content padding *ngIf=”game!=null”>” tanımlamasında ==> *ngIf=”game!=null tanımı çok önemlidir. Servisden ilgili datanın çekilmesi zaman alacağı için, html render zamanında null hatasını önlemek için, ilgili şartın yazılması gerekmektedir. Aksi takdirde sayfa, asenkron şekilde yüklenmelidir.
  • “<ion-content><ion-row><ion-col width-50 text-center class=”divider-col”>” : İlk row sayfanın en üstünde gözüken satırdır. Yukarıda görüldüğü gibi, sayfanın ortasına “Home” ve “Away” buttonları bulunmaktadır. “width-50” : Buttonun, sayfanın en üstünde ve ilgili kolonun ortasında gözükmesini sağlar.
  • <button (click)=”teamTapped(game.team1Id)”>{{game.team1}}</button>” : “Home” yani, evinde maç yapılan takımın detay sayfasına gidilir. “Away” yani misafir takımın detay sayfasına gidilir.
  • İkinci row “<ion-row center>” ile ilgili satırın sayfanın ortasında gözükmesini sağlar.
  • <ion-col width-50 text-center class=”divider-col” (click)=”teamTapped(game.team1Id)”>” : İlgili kolonun satırın ortasında çıkması ve tıklanma durumunda “teamTapped()” methodunun 1. takımın ID’si ile çağrılması sağlanmıştır. İçlerine 1. ve 2. takımın adları yazılır==>”<h4>{{game.team1}}</h4>
  • Üçüncü satır sayfanın sonuna konumlandırılır. ==>”<ion-row baseline class=”top-bottom-border”>
  • “isWinner()” : Methodu çağrılarak 1. ve 2. takımın maç scoreları kazanma ve kaybetme durumlarına göre farklı renklerde yazılır.
  • Sayfanın en altına ” <ion-card> <ion-item>” ile sayfanın sonuna takvim ikonu konup ==>”<ion-icon name=”calendar” item-left></ion-icon>” maç zamanı tarih ve saat olarak ekrana basılır.==>” {{game.gameTime | date:’M/d/yy’}} {{game.gameTime | date:’shortTime’}}” Tarih format şekli de filter ile “‘|’ date:’shortTime'” şeklinde tanımlanır.
  • Yine aynı “<ion-card>” altına “<ion-row> <ion-col width-60>” ile aynı kolonun %60’lık kısmına ==>”<ion-label>{{game.location}}</ion-label>” oynanan lokasyon ve geri kalan %40’lık kısmına harita ve navigasyon ikonları konulmuştur==>”<ion-icon name=”map”></ion-icon>

game.ts: Seçilen takıma ait maç sonuçlarının gösterildiği sayfaya ait dataların doldurulduğu yerdir.

  • Constructorda yanda görülen kütüphaneler “NavController, NavParams, EliteApi” dependency injection ile sayfaya eklenir.
  • ionViewDidLoad()” : Sayfa yüklenme durumunda parametre olarak gönderilen game datası “this.game = this.navParams.data;” ilgili değişkene aktarılır. “this.game.gameTime = Date.parse(this.game.time);“==> Burada ekrana basılacak maçın oynandığı zaman ilgili değişkene aktarılır.
  • teamTapped(teamId)” : Burada ilgili takıma tıklandığında, ona ait ana sayfaya yönlenilir. Not : Bazen data modeline bağlı olarak, kodlarınız malesef daha angarya bir hal alabilir. Örneğin bu örnekde, direk takım bilgisine ulaşabileceğimiz bir data modeli olmadığından, önce seçilen turnuvaya ait data çekilir==> “let tourneyData = this.eliteApi.getCurrentTourney();” Daha sonra da bu turnavaya ait tıklanan takım ID’sine ait data çekilir ==> “let team = tourneyData.teams.find(t => t.id === teamId); “Gönül isterdi ki turnuva bilgisine gerek olmadan direk ilgili ID’ye göre takım bilgisi çekilsin. Ama bazen Adaptor olarak dışardan alınıp projenize implemente ettiğiniz kaynaklara, uyma zorunluluğu olabilir. Özellikle mobile uygulamalar, web servislerine bağmlı çalıştıklarından dolayı, bu gibi durumlar ile karşılaşma olasılıkları daha yüksektir.” Son olarak çekilen takım bilgisi ile “TeamHome” page sayfasına yönlenilir ==>”this.navCtrl.push(TeamHomePage, team);
  • “goToDirections()”: Bu makalede şimdilik boş bırakılacaktır. Maçın olduğu lokasyona yol tarifi almak amacı ile kullanılacaktır.
  • “goToMap()” : Bir sonraki konu olarak anlatılacak , maçın oynanacağa yerin haritada gösterilmesi için maç bilgisi ile birlikte yönlendirilen “MapPage” sayfasıdır.
  • isWinner()” : 1. takımın kazanıp kazanmadığını boolen olarak geri dönen methoddur ==>”return Number(score1) > Number(score2);”

Geldik bir makalenin daha sonuna. Bu makalede ION-SEARCH’ün nimetlerinden ve “VIRTUAL SCROLL” ile performans artışındaki inanılmaz değişime şahit olduk.  Bir sonraki makalede IONIC-NATIVE üzerine bir çok farklı örneği detaylıca inceleyeceğiz.

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...

2 Cevaplar

  1. Muhammed dedi ki:

    Hocam sizin makalelerinizi görmediğim için kaç gündür saçma sapan şeylerle uğraşıyordum. İngilizcem de zayıf olduğu için aradığım seyleride pek anlamiyordum. Eve gidince ilk iş makalelere göre uygulamami düzenlemek. Stok uygulaması yapıyordum 17bin ürün olunca uygulama takılıyordu. İnfinite scroll ekleyince arama sadece ekranda olanlar arasında yapıyordu. İnşallah sizin anlattıklarınızla sıkıntılar düzelir. Makaleler için çok teşekkürler

    • borsoft dedi ki:

      Selam Muhammed,

      Hadi bakalım. Kolay gelsin. Umarım makaleler işine yarar.
      İyi çalışmalar.

borsoft için bir cevap yazın Cevabı iptal et

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