MoinMoin Logo
  • Comments
  • Immutable Page
  • Menu
    • Navigation
    • RecentChanges
    • FindPage
    • Local Site Map
    • Help
    • HelpContents
    • HelpOnMoinWikiSyntax
    • Display
    • Attachments
    • Info
    • Raw Text
    • Print View
    • Edit
    • Load
    • Save
  • Login

Navigation

  • Start
  • Sitemap
Revision 23 as of 2020-10-30 10:50:50
  • Ionic

Ionic

  • https://ionicframework.com/

Ionic Framework is an open source mobile UI toolkit for building high quality, cross-platform native and web app experiences.

Run chrome

"/C/Program Files (x86)/Google/Chrome/Application/chrome.exe" --disable-web-security --disable-gpu --user-data-dir="c:\TempChrome" --disable-features=SameSiteByDefaultCookies,SameSiteDefaultChecksMethodRigorously

google-chrome  --disable-web-security --disable-gpu --user-data-dir="/tmp" --disable-features=SameSiteByDefaultCookies,SameSiteDefaultChecksMethodRigorously

Example app

  • Requires Android SDK, node 12.16.2 and npm 6.14.5

Toggle line numbers
   1 cd ~
   2 mkdir IonicTest
   3 npm install @ionic/cli
   4 node_modules/.bin/ionic start IonicTest
   5 #framework angular
   6 #starter template tabs
   7 #integrate capacitor N
   8 #create free ionic account N
   9 cd IonicTest
  10 npm i
  11 npm install @ionic/cli
  12 npm install cordova
  13 npm install cordova-res
  14 node_modules/.bin/ionic cordova plugin add cordova-plugin-advanced-http
  15 npm install @ionic-native/http
  16 node_modules/.bin/ionic cordova platform add android
  17 node_modules/.bin/ionic cordova build android
  18 scp ./platforms/android/app/build/outputs/apk/debug/app-debug.apk userx@example.net:/home/userx/www/ionic-test.apk
  19 node_modules/.bin/ionic cordova platform add browser
  20 node_modules/.bin/ionic cordova build browser
  21 node_modules/.bin/ionic serve --cordova --platform=browser
  22 # http://localhost:8100
  23 # no CORS
  24 google-chrome --disable-web-security --disable-gpu --user-data-dir="/tmp"

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HTTP } from '@ionic-native/http/ngx';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
  providers: [
    HTTP,
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

tab1.page.ts

import { Component } from '@angular/core';
import { HTTP } from '@ionic-native/http/ngx';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})
export class Tab1Page {
  public res;

  constructor(private http: HTTP) {}

  public ionViewDidEnter(){
    console.log("Stuff");
    this.http.get("https://labs.bitarus.allowed.org/xapps/rest/version/", {}, {})
    .then(data => {
      console.log(data.status);
      console.log(data.data); // data received by server
      this.res=data.data;
      console.log(data.headers);
    })
    .catch(error => {
      console.log(error.status);
      console.log(error.error); // error message as string
      console.log(error.headers);
    });
  }
}

tab1.page.html

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Tab 111 {{res}}
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Tab 111</ion-title>
    </ion-toolbar>
  </ion-header>

  <app-explore-container name="Tab 111 page {{res}}"></app-explore-container>
</ion-content>

tab2.page.html

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Sum
    </ion-title>
  </ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Sum</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-item>
    <ion-label>Value 1</ion-label>
    <ion-input [(ngModel)]="in1"></ion-input>
  </ion-item>
  <ion-item>
     <ion-label>Value 2</ion-label>
     <ion-input [(ngModel)]="in2"></ion-input>
  </ion-item>
  <ion-button color="primary" (click)="sumValuesButtonClicked()">Sum values</ion-button>
</ion-content>

tab2.page.ts

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

@Component({
  selector: 'app-tab2',
  templateUrl: 'tab2.page.html',
  styleUrls: ['tab2.page.scss']
})
export class Tab2Page {
  in1:string;
  in2:string;

  constructor(public alertController: AlertController) {}

  sumValuesButtonClicked(){
    let res:Number;
    res = parseInt(this.in1) + parseInt(this.in2);
    this.presentResult(res).then(()=>{
      console.log("Done");
    });
  }

  async presentResult(res:Number) {
    const alert = await this.alertController.create({
      header: 'Sum',
      subHeader: 'Result',
      message: res.toString(),
      buttons: ['OK'],
    });

    await alert.present();
    let result = await alert.onDidDismiss();
    console.log(result); 
  }
}

notifier.service.ts

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

@Injectable({
  providedIn: 'root'
})
export class NotifierService {
  static readonly GOT_PUSH: 'GotPush';

  private actions: string[];
  private callbacks: Function[];

  constructor() {
    this.actions = [];
    this.callbacks = [];
  }

  /**
   * Subscribe a callback to an action
   * @param action 
   * @param callback 
   */
  public subscribe(action: string, callback: Function) {
    this.actions.push(action);
    this.callbacks.push(callback);
  }

  public notify(action: string) {
    let idx: number;
    idx = -1;
    for (idx = 0; idx < this.actions.length; idx++) {
      if (this.actions[idx] == action) {
        break;
      }
    }

    if (idx != -1) {
      this.callbacks[idx]();
    }
  }
}

tab3.page.ts

import { ChangeDetectorRef, Component } from '@angular/core';
import { DbService } from '../db.service';
import { NotifierService } from '../notifier.service';

@Component({
  selector: 'app-tab3',
  templateUrl: 'tab3.page.html',
  styleUrls: ['tab3.page.scss']
})
export class Tab3Page {
  public items: string[];

  constructor(private dbService: DbService, private notifier: NotifierService, private changeDetectionRef: ChangeDetectorRef
  ) {
    this.updateData();
    this.notifier.subscribe(NotifierService.GOT_PUSH, this.gotPush.bind(this));
    //setInterval(this.refresh.bind(this), 5000);
  }

  // private refresh() {
  //   // triggers change detection when called by setInterval
  // }

  public gotPush() {
    try {
      this.updateData();
    } catch (e) {
      alert("Caught error in gotPush");
    }
  }

  public updateData() {

    this.dbService.getNotifications().then((data: string[]) => {
      this.items = data;
      this.changeDetectionRef.detectChanges();
    });
  }

  public ionViewDidEnter() {
    this.updateData();
  }
}

db.service.ts

  • ionic generate service db

import { Injectable } from '@angular/core';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite/ngx';

@Injectable({
  providedIn: 'root'
})
export class DbService {

  constructor(private sqlite: SQLite) {
  }

  private getDb(): Promise<SQLiteObject> {
    let async: Promise<SQLiteObject> = new Promise<SQLiteObject>((resolve, reject) => {
      let promise = this.sqlite.create({
        name: 'data.db',
        location: 'default'
      });

      promise.then((db: SQLiteObject) => {
        db.executeSql('CREATE TABLE IF NOT EXISTS PushNotificationsTable (push text)', [])
          .then(() => {
            resolve(db);
          })
          .catch((e) => { reject('Table not created ' + JSON.stringify(e)); });
      });

      promise.catch(e => reject("Create db error " + JSON.stringify(e)));
    });

    return async;
  }

  public insertPush(body: string) {
    this.getDb().then((db: SQLiteObject) => {
      if (db != null) {
        db.executeSql('INSERT INTO PushNotificationsTable VALUES (?)', [body])
          .then(() => {
          })
          .catch(e => alert("Insert error " + JSON.stringify(e)));
      } else {
        alert('insertPush: this.db is null');
      }
    });
  }

  public getNotifications(): Promise<string[]> {
    let asyncTask: Promise<string[]> = new Promise<string[]>((resolve, reject) => {

      this.getDb().then((db: SQLiteObject) => {
        if (db != null) {
          db.executeSql('SELECT * FROM PushNotificationsTable', []).then((data) => {
            let items: string[];
            items = [];
            for (let i = 0; i < data.rows.length; i++) {
              let item = data.rows.item(i);
              items.push(item.push);
            }
            resolve(items);
          }).catch((e) => {
            reject(e);
          });
        } else {
          alert('getNotifications: this.db is null');
          reject('getNotifications: this.db is null');
        }
      });
    });

    return asyncTask;
  }
}

EchoPlugin

  • www/EchoPlugin.js

Toggle line numbers
   1 window.echo = function(str, callback) {
   2     cordova.exec(callback, function(err) {
   3         callback('Nothing to echo.');
   4     }, "Echo", "echo", [str]);
   5 };
  • echo-plugin/plugin.xml

Toggle line numbers
   1 <?xml version="1.0" encoding="UTF-8"?>
   2 <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
   3     id="org.allowed.bitarus.EchoPlugin"
   4     version="1.0.0">
   5     <name>echo-plugin</name>
   6     <description>echo plugin</description>
   7     <license>null</license>
   8 
   9     <js-module src="www/EchoPlugin.js" name="EchoPlugin">
  10         <clobbers target="EchoPlugin" />
  11     </js-module>
  12 
  13     <platform name="android">
  14         <config-file target="res/xml/config.xml" parent="/*">
  15             <feature name="EchoPlugin" >
  16                 <param name="android-package" value="org.allowed.bitarus.EchoPlugin"/>
  17             </feature>
  18         </config-file>
  19         <config-file target="AndroidManifest.xml" parent="/*">
  20             <uses-permission android:name="android.permission.INTERNET" />
  21         </config-file>
  22         <source-file src="src/main/java/org/allowed/bitarus/EchoPlugin.java" target-dir="src/org/allowed/bitarus" />
  23     </platform>
  24 </plugin>
  • echo-plugin/package.json

Toggle line numbers
   1 {
   2   "name": "org.allowed.bitarus.echoplugin",
   3   "version": "1.0.0",
   4   "description": "Echo plugin",
   5   "cordova": {
   6     "id": "org.allowed.bitarus.echoplugin",
   7     "platforms": [
   8       "android"
   9     ]
  10   },
  11   "keywords": [
  12     "ecosystem:cordova",
  13     "cordova-android"
  14   ],
  15   "author": "Vitor",
  16   "license": "null"
  17 }
  • echo-plugin/src/main/java/org/allowed/bitarus/EchoPlugin.java

Toggle line numbers
   1 package org.allowed.bitarus;
   2 
   3 import org.apache.cordova.CordovaPlugin;
   4 import org.apache.cordova.CallbackContext;
   5 import org.json.JSONArray;
   6 import org.json.JSONException;
   7 import org.json.JSONObject;
   8 import org.apache.cordova.CordovaWebView;
   9 import org.apache.cordova.CordovaInterface;
  10 
  11 public class EchoPlugin extends CordovaPlugin {
  12     @Override
  13     public void initialize(CordovaInterface cordova, CordovaWebView webView) {
  14         super.initialize(cordova, webView);
  15     }
  16 
  17     @Override
  18     public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {        
  19         if (action.equals("echo")) {
  20             String message = "Echo " + args.getString(0) + " !!!";
  21             this.echo(message, callbackContext);
  22             return true;
  23         }
  24 
  25         if (action.equals("getintentdata")) {            
  26             this.getintentdata(callbackContext);
  27             return true;
  28         }
  29 
  30 
  31         return false;
  32     }
  33 
  34     private void echo(String message, CallbackContext callbackContext) {
  35         if (message != null && message.length() > 0) {
  36             callbackContext.success(message);
  37         } else {
  38             callbackContext.error("Expected one non-empty string argument.");
  39         }
  40     }
  41 
  42     private void getintentdata(CallbackContext callbackContext) {
  43         String data = this.cordova.getActivity().getIntent().getDataString() ;      
  44         String action = this.cordova.getActivity().getIntent().getAction();
  45         String result = String.format( "{\"data\":\"%s\" , \"action\":\"%s\"}",data,action );
  46         callbackContext.success(result);
  47     }    
  48 }
  • tab1.page.ts

Toggle line numbers
   1 import { Component } from '@angular/core';
   2 import { HTTP } from '@ionic-native/http/ngx';
   3 import { Platform } from '@ionic/angular';
   4 
   5 declare let cordova: any;
   6 
   7 @Component({
   8   selector: 'app-tab1',
   9   templateUrl: 'tab1.page.html',
  10   styleUrls: ['tab1.page.scss']
  11 })
  12 export class Tab1Page {
  13   public res;
  14   public echo: string;
  15 
  16   constructor(private http: HTTP, private platform: Platform) {
  17 
  18     this.platform.ready().then(() => {
  19       this.init();
  20     }
  21     );
  22 
  23   }
  24 
  25   public init() {
  26     this.getEcho();
  27   }
  28 
  29   getEcho() {
  30     if (this.platform.is('cordova')) {
  31       try {
  32         cordova.exec(
  33           (success) => {
  34             this.echo = success;
  35           },
  36           (error) => { },
  37           //feature name in  /home/vitor/MyIonicProject/echo-plugin/plugin.xml
  38           //feature name in /home/vitor/MyIonicProject/platforms/android/app/src/main/res/xml/config.xml
  39           "EchoPlugin", // class Service name
  40           "echo", // action
  41           ["argxyz"] // arguments
  42         );
  43       } catch (e) {
  44         this.echo = "Got error in get echo " + JSON.stringify(e) + e.message;
  45       }
  46     }
  47   }
  48 
  49   public ionViewDidEnter() {
  50     console.log("Stuff");
  51     this.http.get("https://labs.bitarus.allowed.org/cebapps/r/core/version/", {}, {})
  52       .then(data => {
  53         console.log(data.status);
  54         console.log(data.data); // data received by server
  55         this.res = data.data;
  56         console.log(data.headers);
  57       })
  58       .catch(error => {
  59         console.log(error.status);
  60         console.log(error.error); // error message as string
  61         console.log(error.headers);
  62       });
  63   }
  64 }
  • tab1.page.html

Toggle line numbers
   1 <ion-header [translucent]="true">
   2   <ion-toolbar>
   3     <ion-title>
   4       Tab 111 {{res}}
   5     </ion-title>
   6   </ion-toolbar>
   7 </ion-header>
   8 <ion-content [fullscreen]="true">
   9   <ion-header collapse="condense">
  10     <ion-toolbar>
  11       <ion-title size="large">Tab 111</ion-title>
  12     </ion-toolbar>
  13   </ion-header>
  14   <app-explore-container name="Tab 111 page {{res}} {{echo}}"></app-explore-container>
  15 </ion-content>

SQLite

  • npm install @ionic-native/sqlite
  • node_modules/.bin/ionic cordova plugin add cordova-sqlite-storage
  • https://github.com/storesafe/cordova-sqlite-storage

// app.module.ts
import { SQLite } from '@ionic-native/sqlite/ngx';
providers SQLite

// app.component.ts 
import { SQLite } from '@ionic-native/sqlite/ngx';
constructor private sqlite:SQLite

Create service

  • node_modules/.bin/ionic generate service notifier
  • Add in providers in app.module.ts
  • Inject in constructor of classes that might use the dependency/bean/service

Angular change detection

  • https://www.mokkapps.de/blog/the-last-guide-for-angular-change-detection-you-will-ever-need/

In short, the framework will trigger a change detection if one of the following events occurs:

    any browser event (click, keyup, etc.)
    setInterval() and setTimeout()
    HTTP requests via XMLHttpRequest
  • https://angular.io/api/core/ChangeDetectorRef

constructor(private ref: ChangeDetectorRef) {
//...
this.ref.detectChanges();
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01