Size: 16073
Comment:
|
Size: 16657
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 191: | Line 191: |
=== tab3.page.html === {{{#!highlight html <ion-header [translucent]="true"> <ion-toolbar> <ion-title> Push notifications list </ion-title> </ion-toolbar> </ion-header> <ion-content [fullscreen]="true"> <ion-header collapse="condense"> <ion-toolbar> <ion-title size="large">Push notifications list</ion-title> </ion-toolbar> </ion-header> <ul> <li *ngFor="let pushNotification of items"> {{pushNotification}} </li> </ul> </ion-content> }}} |
|
Line 193: | Line 216: |
{{{ | {{{#!highlight javascript |
Line 237: | Line 260: |
{{{ | {{{#!highlight javascript |
Line 285: | Line 308: |
{{{ | {{{#!highlight javascript |
Ionic
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
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); } }
tab3.page.html
1 <ion-header [translucent]="true">
2 <ion-toolbar>
3 <ion-title>
4 Push notifications list
5 </ion-title>
6 </ion-toolbar>
7 </ion-header>
8
9 <ion-content [fullscreen]="true">
10 <ion-header collapse="condense">
11 <ion-toolbar>
12 <ion-title size="large">Push notifications list</ion-title>
13 </ion-toolbar>
14 </ion-header>
15 <ul>
16 <li *ngFor="let pushNotification of items">
17 {{pushNotification}}
18 </li>
19 </ul>
20 </ion-content>
notifier.service.ts
1 import { Injectable } from '@angular/core';
2
3 @Injectable({
4 providedIn: 'root'
5 })
6 export class NotifierService {
7 static readonly GOT_PUSH: 'GotPush';
8
9 private actions: string[];
10 private callbacks: Function[];
11
12 constructor() {
13 this.actions = [];
14 this.callbacks = [];
15 }
16
17 /**
18 * Subscribe a callback to an action
19 * @param action
20 * @param callback
21 */
22 public subscribe(action: string, callback: Function) {
23 this.actions.push(action);
24 this.callbacks.push(callback);
25 }
26
27 public notify(action: string) {
28 let idx: number;
29 idx = -1;
30 for (idx = 0; idx < this.actions.length; idx++) {
31 if (this.actions[idx] == action) {
32 break;
33 }
34 }
35
36 if (idx != -1) {
37 this.callbacks[idx]();
38 }
39 }
40 }
tab3.page.ts
1 import { ChangeDetectorRef, Component } from '@angular/core';
2 import { DbService } from '../db.service';
3 import { NotifierService } from '../notifier.service';
4
5 @Component({
6 selector: 'app-tab3',
7 templateUrl: 'tab3.page.html',
8 styleUrls: ['tab3.page.scss']
9 })
10 export class Tab3Page {
11 public items: string[];
12
13 constructor(private dbService: DbService, private notifier: NotifierService, private changeDetectionRef: ChangeDetectorRef
14 ) {
15 this.updateData();
16 this.notifier.subscribe(NotifierService.GOT_PUSH, this.gotPush.bind(this));
17 //setInterval(this.refresh.bind(this), 5000);
18 }
19
20 // private refresh() {
21 // // triggers change detection when called by setInterval
22 // }
23
24 public gotPush() {
25 try {
26 this.updateData();
27 } catch (e) {
28 alert("Caught error in gotPush");
29 }
30 }
31
32 public updateData() {
33
34 this.dbService.getNotifications().then((data: string[]) => {
35 this.items = data;
36 this.changeDetectionRef.detectChanges();
37 });
38 }
39
40 public ionViewDidEnter() {
41 this.updateData();
42 }
43 }
db.service.ts
- ionic generate service db
1 import { Injectable } from '@angular/core';
2 import { SQLite, SQLiteObject } from '@ionic-native/sqlite/ngx';
3
4 @Injectable({
5 providedIn: 'root'
6 })
7 export class DbService {
8
9 constructor(private sqlite: SQLite) {
10 }
11
12 private getDb(): Promise<SQLiteObject> {
13 let async: Promise<SQLiteObject> = new Promise<SQLiteObject>((resolve, reject) => {
14 let promise = this.sqlite.create({
15 name: 'data.db',
16 location: 'default'
17 });
18
19 promise.then((db: SQLiteObject) => {
20 db.executeSql('CREATE TABLE IF NOT EXISTS PushNotificationsTable (push text)', [])
21 .then(() => {
22 resolve(db);
23 })
24 .catch((e) => { reject('Table not created ' + JSON.stringify(e)); });
25 });
26
27 promise.catch(e => reject("Create db error " + JSON.stringify(e)));
28 });
29
30 return async;
31 }
32
33 public insertPush(body: string) {
34 this.getDb().then((db: SQLiteObject) => {
35 if (db != null) {
36 db.executeSql('INSERT INTO PushNotificationsTable VALUES (?)', [body])
37 .then(() => {
38 })
39 .catch(e => alert("Insert error " + JSON.stringify(e)));
40 } else {
41 alert('insertPush: this.db is null');
42 }
43 });
44 }
45
46 public getNotifications(): Promise<string[]> {
47 let asyncTask: Promise<string[]> = new Promise<string[]>((resolve, reject) => {
48
49 this.getDb().then((db: SQLiteObject) => {
50 if (db != null) {
51 db.executeSql('SELECT * FROM PushNotificationsTable', []).then((data) => {
52 let items: string[];
53 items = [];
54 for (let i = 0; i < data.rows.length; i++) {
55 let item = data.rows.item(i);
56 items.push(item.push);
57 }
58 resolve(items);
59 }).catch((e) => {
60 reject(e);
61 });
62 } else {
63 alert('getNotifications: this.db is null');
64 reject('getNotifications: this.db is null');
65 }
66 });
67 });
68
69 return asyncTask;
70 }
71 }
EchoPlugin
- www/EchoPlugin.js
- echo-plugin/plugin.xml
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
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
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
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
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
// 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
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
constructor(private ref: ChangeDetectorRef) { //... this.ref.detectChanges();