Saturday, March 9, 2019

Learning Ionic 4 - Part 2

After long time I'm busy with the projects in my company, now I have a time to write this Part 2.
In Part 1, we finished the login & sign-up page. In this part, we will focus on how to create pages with main menu (side menu), how to deal with local storage (use local storage as DB with tables).

The main menu has 2 submenu Games menu & History menu. Because we will Home page for Games menu, so we jus generate new page called History for History menu:
ionic g page History
Next, we add a side menu. Open file src/app/app.component.ts and add menuPages:
export class AppComponent {
  public menuPages = [
    {
      title: 'Games',
      url: '/home',
      icon: 'logo-usd'
    },
    {
      title: 'History',
      url:'/history',
      icon: 'list'
    }
  ];
  constructor(
...
Then open file src/app/app.component.html and change as below:
<ion-app>
  <ion-split-pane>
    <ion-menu>
      <ion-header>
        <ion-toolbar>
          <ion-title>Menu</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content padding>
        <ion-list>
          <ion-menu-toggle *ngFor="let p of menuPages">
            <ion-item [routerLink]="p.url" [routerDirection]="'root'">
              <ion-icon slot="start" [name]="p.icon"></ion-icon>
              <ion-label>{{p.title}}</ion-label>
            </ion-item>
          </ion-menu-toggle>
        </ion-list>
      </ion-content>
    </ion-menu>
    <ion-router-outlet main></ion-router-outlet>
  </ion-split-pane>
</ion-app>
This change will add ion-split-pane into ion-app, and it will hold ion-menu (for menu content) and ion-router-outlet (for routing).
To display menu button on any page, let add ion-menu-button into its html. We need to add it into Home page & History page, for example src/app/home/home.page.html of Home page:
<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>Games</ion-title>
  </ion-toolbar>
</ion-header>
OK, now we had the menu.
In Login page, we use a remote DB for authenticating, however we will use local storage to store the content of Games & History. In the real app, what is needed to store in centralized remote DB and what is needed to save on local storage depending on your strategy for your app. Here I want to show you a way to deal with the local storage like we work with tables. For beginning, let generate a MyTablesService service:
ionic g service services/MyTables
Open src/app/services/my-tables.service.ts and add the codes like the following:
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';

export class Game {
  public id: number;
  public name: string;
  public numbers: number;
  public min_number: number;
  public max_number: number;
  public created_time: Date;
  public deleted: boolean;
  public deleted_time: Date;
}

export class History {
  public id: number;
  public game_id: number;
  public play_time: Date;
  public result: string;
  public rating: number;
}

@Injectable({
  providedIn: 'root'
})
export class MyTablesService {
  table = 'table_';
  seedkey = 'seed_table';
  items: any = [];
  seed = 0;

  constructor(private storage: Storage) { }
  private getKeyFormat(id) {
    return this.table + id;
  }

  private _addItem(item) {
    item.id = this.seed;
    this.storage.set(this.getKeyFormat(item.id), JSON.stringify(item));
    this.seed++;
    this.storage.set(this.seedkey, this.seed);
  }

  private setTable(table_name: string) {
    this.table = table_name + '_';
    this.seedkey = 'seed_' + table_name;
  }

  getItems(table_name: string) {
    this.setTable(table_name);
    this.items = [];
    var promise = new Promise((resolve, reject) => {
      this.storage.forEach((v, k, i) => {
        let a = this.table;
        if (k.indexOf(a) > -1) {
          this.items.push(JSON.parse(v));
        }
      }).then(() => {
        resolve(this.items);
      });
    });
    return promise;
  }

  addItem(table_name: string, item: any) {
    this.setTable(table_name);
    this.storage.get(this.seedkey).then((value) => {
      if (value) this.seed = value;
      else this.seed = 1;
      this._addItem(item);
  }, (error) => {
      this.seed = 1;
      this._addItem(item);
    });
  }

  getItem(table_name: string, id: any) {
    this.setTable(table_name);
    var promise = new Promise((resolve, reject) => {
      this.storage.get(this.getKeyFormat(id)).then((value) => {
        resolve (JSON.parse(value));
      }, (error) => {
        reject (false);
      });
    });
    return promise;
  }

  getItemByField(data: any, field: string, value: string) {
    return data.filter(i => i[field] == value);
  }

  sortAscByField(field: string) {
    return function(a,b){
      if( a[field] > b[field]){
          return 1;
      }else if( a[field] < b[field] ){
          return -1;
      }
      return 0;
    }
  }

  sortDescByField(field: string) {
    return function(a,b){
      if( a[field] > b[field]){
          return -1;
      }else if( a[field] < b[field] ){
          return 1;
      }
      return 0;
    }
  }
}


Basically the local storage will store data in form of key & value, the MyTablesService service above will help to work with local storage as tables. In which, Game class and History class present for the structure of games table & history tables. We will see on later code.

Next, we will generate "New Game" page for adding new game:
ionic g page "New Game"
Here are codes for src/app/new-game/new-game.page.ts:
import { Component, OnInit } from '@angular/core';
import { Game,  MyTablesService } from '../services/my-tables.service';
import { NavController } from '@ionic/angular';

@Component({
  selector: 'app-new-game',
  templateUrl: './new-game.page.html',
  styleUrls: ['./new-game.page.scss'],
})
export class NewGamePage implements OnInit {
  game: Game;

  constructor(private gameService: GamesService, private navCtrl: NavController) {
    this.game = new Game();
    this.game.numbers = 5; //default value
  }

  ngOnInit() {
  }

  save() {
    this.game.created_time = new Date();
    this.gameService.addItem('games', this.game);
    this.navCtrl.goRoot('/home');
  }
}
And codes for src/app/new-game/new-game.page.html:
<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>New Game</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content padding>
  <ion-list>
    <ion-item>
      <ion-label>Name:</ion-label>
      <ion-input [(ngModel)]="game.name" type="text"></ion-input>
    </ion-item>
    <ion-item>
      <ion-label>Quantity of numbers (1-10):</ion-label>
      <ion-select [(ngModel)]="game.numbers" value="5" okText="Ok" cancelText="Cancel">
          <ion-select-option value="1">1</ion-select-option>
          <ion-select-option value="2">2</ion-select-option>
          <ion-select-option value="3">3</ion-select-option>
          <ion-select-option value="4">4</ion-select-option>
          <ion-select-option value="5">5</ion-select-option>
          <ion-select-option value="6">6</ion-select-option>
          <ion-select-option value="7">7</ion-select-option>
          <ion-select-option value="8">8</ion-select-option>
          <ion-select-option value="9">9</ion-select-option>
          <ion-select-option value="10">10</ion-select-option>
      </ion-select>
    </ion-item>
    <ion-item>
      <ion-label>Random # between:</ion-label>
      <ion-input [(ngModel)]="game.min_number" type="number"></ion-input>
      <ion-label>and</ion-label>
      <ion-input [(ngModel)]="game.max_number" type="number"></ion-input>
    </ion-item>
  </ion-list>
  <ion-button expand="full" shape="round" color="secondary" (click)="save()">Save</ion-button>
</ion-content>
To invoke this New Game page, let add a FAB (floating action button) into the html of Home page:
...
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
    <ion-fab-button color="danger" (click)="newGame()">
      <ion-icon name="add"></ion-icon>
    </ion-fab-button>
</ion-fab>
...
Add function newGame() into src/app/home/home.page.ts:
newGame() {
    this.navCtrl.goForward('/new-game');
}
Below are new screenshots of our app until now after login:


Through this part, we have known how to add a menu, work with local storage and create a new game. In next Part 3, we will code remain things & finish the app. See you!

1 comment:

Subscribe to RSS Feed Follow me on Twitter!