dilluns, 27 de març del 2017

Ionic 2, using AlertController

When we fill data in a form is a good idea show to user what he has written before save the data and in case there is an error give it a chance to modify it. For that in Ionic 2 we can use AlertControllerHere you can find more information about AlertController.

An alert dialog can be used for show information to the user or collect it. I opted for use both options in a same dialog and so streamline the process.

In this case, the alert dialog it will show the data introduced in the form and doing so in a input fields which it allows modify it. For that,  the dialog  will have an input field for each entry and two buttons, a Cancel button to cancel and dismiss the dialog and a Ok button to save the data and dismiss the dialog.

The first step is import the AlertController  from ionic-angular in our controller
   
   import { NavController, AlertController } from 'ionic-angular'

Next we add the AllertController how a parameter in the constructor
   
   constructor(public navCtrl: NavController, 
               public alertCtrl: AlertController, 
               public dbService: DbService) {

   }   

And now we create a function to present the alert to the user

  addMovement() {
    this.data = {date: this.date, expense: this.expenses, 
                 description: this.description, import: this.import};
    let dateToSave = this.date;

    let dataAlert = this.alertCtrl.create ({
      title: 'These data are correct?',
      inputs: [
        {
          name: 'date',
          placeholder: 'Date',
          value: dateToSave.substr(0, 10)          
        },
        {
          name: 'expense',
          placeholder: 'Expense',
          value: this.data.expense          
        },
        {
          name: 'description',
          placeholder: 'Description',
          value: this.data.description
        },
        {
          name: 'import',
          placeholder: 'Import',
          value: this.data.import,
          type: 'number'
       }
      ],
      buttons: [
        {
          text: 'Cancel',
          role: 'cancel',
          handler: () => {
            dataAlert.dismiss();
            return false;
          }          
        },
        {
          text: 'Ok',
          role: 'ok',
          handler: (dialogData: any) => {

            
            this.data.expense = dialogData.expense;
            this.data.description = dialogData.description;
            this.data.import = dialogData.import;


            this.dbService.insertMovement(this.data).then(() => {
              this.expenses = "";
              this.description = "";
              this.import = "";
              dataAlert.dismiss();

              return false;
            });
          }
        }
      ]
    });

    dataAlert.present();
  }
}
 

In the Ok handler as we don't know what field has been modified, we update all data with the alert dialog fields, next insert the data in the database, clean the fields in the screen and dismiss the dialog.

The final result looks like this

dilluns, 20 de març del 2017

Ionic 2 and SQLite

I'm advancing in my new project and it is the moment to decide how save the data. If it's a native project in Android my decision is easy, SQLite. In iOS I have not yet used Core Data so at this time I'm not going  write over that.
At this moment seems there are two options, Local Storage and SQLite. Local Storage has a limitation of 10 MB of data and how I don't know if the users they will use more, I think not, but to ensure that storage limitation is not a problem I will use SQLite.

Here you can encounter the official documentation of ionic native.

First step is install the Apache Cordova SQLite plugin, for that from a terminal and from inside the project directory we run this command

   ionic plugin add cordova-sqlite-storage --save

Now we will create a service to manage all the functionalities of the database, fot that, we use the ionic generator command

   ionic g provider db-service

This command has created the db-service.ts file in the ../src/providers/  directory.

We open the file db-service.ts and we import the SQLite from ionic native, we declare a variable and into the constructor we create an instance to SQLite.

   
   import { Injectable } from '@angular/core';
   import { SQLite } from 'ionic-native';
   import 'rxjs/add/operator/map';


   @Injectable()
   export class DbService {

      db: SQLite = null;

      constructor() {
         this.db = new SQLite();
      }   

   }

Now we create the method openDatabase, which is the responsible of open the database

   
    //Open the database
    openDatabase() {
       return this.db.openDatabase({
         name: 'mydb.db',
         location: 'default'
       })
       .catch(error => console.error('Error opening database', error));
    }

Next, we create the method for create the tables in the database

   
    //Create all tables
    createTables() {
       let myTable = 'CREATE TABLE IF NOT EXIST mytable' + 
                            ' (id INTEGER PRIMARY KEY AUTIOINCREMENT,' +
                            ' title TEXT, data TEXT)';

       return this.db.executeSql(myTable, []).then(() => {

       }, (err) => {
          console.error('Unable to execute sql: ', err);
       }); 
    }

A method for insert data

   
   insertMyTable(title: string, data:string) {
      let sql = 'INSERT INTO mytable(title, data) VALUES(?,?)';

      return this.db.executeSql(sql, [title, data]).then(() => {

      }, (err) => {
         console.error('Unable to execute sql insertion: ', err);
      });
   }

An update method

   
   updateMyTable(list: any) {
      let sql = 'UPDATE mytable SET title=?, data=?  WHERE id=?';

      return this.db.executeSql(sql, [list.title, list.data, 
                                list.id]).then(() => {

      }, (err) => {
         console.error('Unable to execute sql update: ', err);
      });
   }

An delete method

   
   deleteMyTable(list: any) {
      let sql = 'DELETE FROM mytable WHERE id=?';

      return this.db.executeSql(sql, [list.id]).then(() => {

      }, (err) => {
         console.error('Unable to execute sql: ', err);
      });
  }

And finally a query method, in this case obtains all the titles of the table

   
   getAllTitlesFromMyTable() {
      let sql = 'SELECT title FROM mytable';
      let titles: string[] = [];

      return this.db.executeSql(sql, []).then(response = > {
         
         for (var x = 0; x < response.rows.length; x++) {
            titles.push(response.rows.item(x).title);
         }

         return Promise.resolve(titles);
      }, (err) => {

         console.error('Unable to execute sql query: ', err);   
      });
   }

Now, for use it in the file app.module.ts first import the service and  in the providers section we add it
   
   import { NgModule, ErrorHandler } from '@angular/core';
   import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
   import { HomePage } from '../pages/home/home';

   import { DbService } from '../providers/db-service';

   @NgModule({
      declarations: [
         MyApp,
         HomePage
      ],
      imports: [
         IonicModule.forRoot(MyApp),
      ],
      bootstrap: [IonicApp],
      entryComponents: [
         MyApp,
         HomePage
      ],
      providers: [
         {provide: ErrorHandler, useClass: IonicErrorHandler},
         DbService
      ]
   })
   export class AppModule {}

In app.component.ts when the app is ready we call the openDatabase method and create the tables

   
   import { Component } from '@angular/core';
   import { Platform } from 'ionic-angular';
   import { StatusBar, Splashscreen } from 'ionic-native';

   import { HomePage } from '../pages/home/home';
   import { DbService } from '../providers/db-service';


   @Component({
      templateUrl: 'app.html'
   })
   export class MyApp {
      rootPage = HomePage;

      constructor(platform: Platform, public dbService: DbService) {
         platform.ready().then(() => {
            StatusBar.styleDefault();
            Splashscreen.hide();
            dbService.openDatabase()
        .   then(() => this.dbService.createTables());
         });
     }
   }

And we can use it in the controller

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

   import { NavController } from 'ionic-angular';
   import { DbService } from '../../providers/db-service';

   @Component({
      selector: 'page-home',
      templateUrl: 'home.html'
   })

   export class HomePage {
      titles: string[] = [];

      constructor(public navCtrl: NavController, 
                         public dbService: DbService) {
    
      }

      ionViewWillEnter () {
         this.getTitles();
      }

      getTitles() {
         this.dbService.getAllTitlesFromMyTable().then(titles => {
            this.titles = titles as string[];
         }, (err) => {
            console.error('Get title error: ', err);
         });
      }

   }

I use the query method inside the ionViewWillEnter() because it's fired when entering a page, before it's active. Here you can see more info about Navigation Lifecycle Event.

And finally in the template

   
   <ion-content>
      <ion-list inset>
         <ion-item *ngFor="let title of titles">
            {{ title }}
         </ion-item>
      </ion-list>
   </ion-content>

Well, this it's all for now.

dilluns, 13 de març del 2017

More of hybrid apps, now ionic framework

How I did write in other entries, I decided make the game using HTML5 and show it in a webview manually for see how works it, and so see the differences between Android and iOS. This time for my new project I will use again but instead of make it manually I'll use a framework to make things more easy.

In this moment, exist a lot of frameworks to build mobile apps, among them we could name PhoneGapApache CordovaAngular MaterialIonic and we could also include Ubuntu SDK. For this project,  I have decided use Ionic 2, a framework that uses Angular 2 and Apache Cordova to build the app and make it multi platform.

I has to said that although it is multi platform and can works in MS Windows, GNU/Linux and Mac OS for build apps for iOS you need do it from a Mac.

For install all we need the first step is install Node.js as Ionic is distributed as a package npm. For that we go to Node.js download and install it. 





The next step is install Ionic CLI and Apache Cordova for native app development. For that we open a terminal and write


   sudo npm install -g ionic cordova 




Now for run Apache Cordova in Xcode need enable it, from a terminal

   sudo xcode-select --install 


And for launch iOS apps into a device

   sudo npm install -g ios-deploy 

Some people have problems with this command, in my case I solved it adding

   sudo npm install -g --unsafe-perm=true ios-deploy 

Here can encounter more info about that.

Now for create your app have different options  blank for a clean app, tabs for an app with tabs and sidemenu for an app with a side menu.


   ionic start myApp tabs --v2 


And from inside the directory runs the app


   ionic serve 



Well, it's all for now. I will continue working, learning and sharing.


dimarts, 7 de març del 2017

Find the route


This is my first game and it's is a type of puzzle. The purpose of the game is to visit all the nodes connected and return to the start point. Summarizing, find a Hamiltonian cycle.
For this, it's necessary to visit all the nodes and couldn't go back, only go ahead. When it couldn't be continued, the only option is restart the level.

At this moment the game has 120 levels and 10 levels without lines that I have called Phantom levels. I'm writing new levels with more nodes but at the same time, I am also considering change to Eulerian cycles in next levels.
I hope you find it entertaining.

Enjoy it!!






Localization, HTML5 and WebView

I will to release the first version of my game, and this version it only will be in English. Not that there are many lines to translate bu I sent the first build to the App Store (Waiting for review) and now I'll wait to the next version.

The point is that it's the first time I'm going to localize an HTML5 App and I have to investigate how can I do it. It's not possible to use the Android or iOS system to localize apps because the messages appear in dialog boxes into the webview. So I've been looking how I can do it.

If I had created the app with Apache Cordova, there is a module named Globalization for this and information can be found here. But I don't use Cordova so that I can't use it. Another option will could be i18next but when use it in a webview without server appear problems with CORS for what I can not use. More info of i18next here and CORS here.

Finally I opted for write by myself the code necessary for localize the few text who appear in the dialogs. 

For that I write the text strings in JSON format like as


 
var resources = {
    
    "en": {
        "invalidMovement": "This node is not in the route",
        "isVisited": "A node can only be visited once",

        ...........
    },
    
    "es": {
        "invalidMovement": "Este nodo no está en la ruta",
        "isVisited": "Un nodo solo puede ser visitado una vez",
        
        ...........
    },
    
    "ca": {
        "invalidMovement": "Aquest node no es a la ruta",
        "isVisited": "Un node sols pot ser visitat un cop",
      
        ...........
    }
    
}


And in the dialog boxes functions

 
        var lng = navigator.language.split("-");        

        var message;
        var close; 

        if (lng[0] === "ca") {
            message = resources.ca.isVisited;
            close = resources.ca.close;
        }else if (lng[0] === "es") {
            message = resources.es.isVisited;
            close = resources.es.close;
        } else {
            message = resources.en.isVisited;        
            close = resources.en.close;
        }

        document.getElementById('loser').innerHTML = message;
        document.getElementById('closeLoser').innerHTML = close;

First you get the language, next get the string from the resources and finally writes the result.

I don't know if this is the best option, but it is easy to implement and for a little project I think it is sufficient.