BLOG

R3 Cloud Journey

kintone

Ionic(v5) でスマホから kintone 連携 (中編)

2020-08-20

アールスリーの浅利です!

はじめに謝らないといけないことがあります! 前回の 前編 の最後に「次はスマホネイティブアプリ化までやりたい」と書きましたが、ムリでした!

前回は APIサーバー経由でkintoneアプリからレコードを取得し、一覧表示をさせましたが、 今回は、登録/編集を行いたいと思います。 スマホアプリ化は、次回ぜひやりたいと思います!

Ionic側アプリの変更

( 前回作成した ローカルのAPIサーバーと、 Ionicアプリは起動させておいてください。)

まず、前回作成したIonicアプリに手を加えていきます。

前回 Tab2 には手を加えませんでしたが、これを登録画面にします。

新規登録機能

Tab2 にて、各フィールド用の入力エリアを用意し、登録ボタンを押して kintone に登録できるようにします。

まず、Tab2 の画面に フォームを追加します。
tab2.page.ts 側で以下のようにします。 (前回実装した tab1.page.ts と見比べてみてください )

/src/app/tab2/tab2.page.ts

...
import { AlertController } from '@ionic/angular';         ← 追加
import { KintoneService } from 'src/app/kintone.service';   ← 追加
...
export class Tab2Page {

  loading: boolean;                                          ← 追加
  form: { name: string, latitude: string, longitude: string };  ← 追加

  constructor(private alertController: AlertController,    ← 引数追加
              private kintone: KintoneService) {}     ← 引数追加

  ↓ 以下 追加
  async ionViewWillEnter() {
    try {
      this.loading = true;
      await this.kintone.ready();

      this.form = {
        name: "",
        latitude: "",
        longitude: "",
      };

    } catch (err) {
      console.error(err);
    } finally {
      this.loading = false;
    }
  }
  ...


そして、tab2.page.htmlで、上で追加した form 内の name, latitude, longitude を編集できるようにします。

/src/app/tab2/tab2.page.html

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">編集画面</ion-title>    ← 変更
    </ion-toolbar>
  </ion-header>

  ↓ <app-explore-container...> を削除して、以下を追加
  <ion-spinner *ngIf="loading"></ion-spinner>
  <ion-list *ngIf="form">
    <ion-item>
      <ion-label>名前</ion-label>
      <ion-input type="text" name="name" [(ngModel)]="form.name"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label>緯度</ion-label>
      <ion-input type="text" name="name" [(ngModel)]="form.latitude"></ion-input>
    </ion-item>
    
    <ion-item>
      <ion-label>経度</ion-label>
      <ion-input type="text" name="name" [(ngModel)]="form.longitude"></ion-input>
    </ion-item>
  </ion-list>

</ion-content>


編集画面_ボタン無し

さらに、ボタンを押すと、入力した内容が登録されるようにしたいので、 KintoneService へ API addRecord を実行するメソッドを追加しました。

/src/app/kintone.service.ts

  ...
  async addRecord(record: {name: string, latitude: string, longitude: string}): Promise<void> {
    return this.request('POST', 'addRecord', record);
  }
  ...


Tab2の画面側にボタンを追加し、( register()メソッドが呼ばれるようにしました )

/src/app/tab2/tab2.page.html

    ...
  </ion-list>

  <ion-button expand="block" (click)="register()">登録</ion-button>   ← 追加
</ion-content>

register() メソッドを実装します。 画面への入力内容(this.formの各値)をパラメータとして KintoneService の addRecord() を呼び出し、終わったら「登録完了」とメッセージダイアログを表示させるようにしました。

/src/app/tab2/tab2.page.ts

  ...
  async register() {
    try {
      await this.kintone.addRecord({
        name: this.form.name,
        latitude: this.form.latitude,
        longitude: this.form.longitude,
      });
      
      const alert = await this.alertController.create({
        message: '登録完了',
        buttons: ['OK']
      });
      await alert.present();

    } catch (err) {
      console.error(err);
    }
  }
  ...
追加成功

実際に追加してみてください。登録ボタンを押すと出る「登録完了」ダイアログを閉じてから、一覧画面に戻ると、追加したレコードが表示されます。 kintoneアプリでも確認してみてください。

さらに「登録完了」ボタンを押したらすぐ一覧画面に戻った方がいいんじゃないかということで、「OK」を押すと一覧画面(Tab1)に遷移するようにしてしました。

/src/app/tab2/tab2.page.ts

...
import { AlertController, NavController } from '@ionic/angular';    ← NavController を追加
import { KintoneService } from 'src/app/kintone.service';
...
  constructor(private navCtrl: NavController,    ← 引数追加
              private alertController: AlertController,
              private kintone: KintoneService) {}
  ...
  async register() {
    ...
      const alert = await this.alertController.create({
        message: '登録完了',
        buttons: ['OK']
      });

      ↓ 追加
      alert.onDidDismiss().then((ev) => {
        // ダイアログが閉じられたら、一覧へ遷移
        this.navCtrl.navigateBack("/tabs/tab1");
      });

      await alert.present();
    ...
  }
  ...


alert.onDidDismiss().then(...) というのは、ダイアログが閉じられた場合の動作を設定しているものです。ここで一覧画面(/tabs/tab1) へ遷移させる処理を入れています。

既存レコードの編集機能

追加ができたので、次は既存データの編集機能です。

先ほど Tab2 は編集画面として実装しましたが、 レコードを指定して、この Tab2 を開くと、そのレコードを編集できるようにしたいと思います。

まず、Routing の設定で、URLでレコード番号を渡せるようにします。 タブ周りのルーティングの設定は tabs-routing.module.tsです。

以下の設定により、/tabs/tab2 は今まで通り Tab2 が開きますが、/tabs/tab2/123 のようにレコード番号を渡して Tab2 を開けるようになります。(この場合はレコード番号=123)

/src/app/tabs/tabs-routing.module.ts

      ...
      {
        path: 'tab2',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab2/tab2.module').then(m => m.Tab2PageModule)
          },
          ↓ 以下を追加
          {
            path: ':recordId',
            loadChildren: () =>
              import('../tab2/tab2.module').then(m => m.Tab2PageModule)
          }
        ]
      },
      ...

一覧画面(Tab1)を修正して、クリックすると、上で設定した/tabs/tab2/{レコード番号}に遷移させるようにします。

/src/app/tab1/tab1.page.html

  ...
  <ion-list *ngIf="records">
    <ion-item *ngFor="let r of records" (click)="edit(r)">   ← (click)="~" を追加
      <ion-label>
        ...

/src/app/tab1/tab1.page.ts

import { NavController } from '@ionic/angular';    ← 追加
...
  constructor(private navCtrl: NavController,     ← 引数追加
              private kintone: KintoneService) {}
  ...

  ↓ edit()を追加

  edit(record: object) {
    this.navCtrl.navigateForward("/tabs/tab2/" + record["$id"].value);
  }
  ...

これで、Tab1の一覧から項目をクリックすると、/tabs/tab2/{その項目のレコード番号} へ遷移するようになります。

続いて、Tab2 側で、渡されてきたレコード番号を使えるようにします。

/src/app/tab2/tab2.page.ts

...
import { ActivatedRoute } from '@angular/router';     ← 追加
...
export class Tab2Page {
  ...
  private oldRecord: {} = null;       ← 追加
  ...
  constructor(private navCtrl: NavController,
              private route: ActivatedRoute,    ← 引数追加
              private alertController: AlertController,
              private kintone: KintoneService) {}
  ...
  async ionViewWillEnter() {
    try {
      this.loading = true;
      await this.kintone.ready();

      ↓ 以下のようにする

      // 既存レコード編集時は、recordId が入ってくる。
      const recordId = this.route.snapshot.paramMap.get('recordId');
      if (recordId) {
        // 編集時 (未実装)

      } else {
        // 追加時 (これまでと同じ)
        this.form = {
          name: "",
          latitude: "",
          longitude: "",
        };
      }
    } catch (err) {
      ...


KintoneService へ、getRecord() と updateRecord() を追加しました。

/src/app/kintone.service.ts

  ...
  async getRecord(id: string): Promise<object> {
    const data = await this.request('GET', 'record', { id });
    return data.record;
  }
  ...
  async updateRecord(record: {id: string, revision: any, name: string, latitude: string, longitude: string}): Promise<void> {
    return this.request('POST', 'updateRecord', record);
  }
  ...


Tab2 から この getRecord() と updateRecord() を呼ぶようにします。

/src/app/tab2/tab2.page.ts

  ...
  async ionViewWillEnter() {
    ...
      // 既存レコード編集時は、recordId が入ってくる。
      const recordId = this.route.snapshot.paramMap.get('recordId');
      if (recordId) {
        // 編集時
        ↓ 追加
        const response = await this.kintone.getRecord(recordId);
        this.oldRecord = response["record"];

        this.form = {
          name: this.oldRecord["name"].value,
          latitude: this.oldRecord["latitude"].value,
          longitude: this.oldRecord["longitude"].value
        }
      } else {
        ...

  async register() {
    try {
      ↓ 修正

      if (this.oldRecord) {
        // 編集時
        await this.kintone.updateRecord({
          id: this.oldRecord["$id"].value,
          revision: this.oldRecord["$revision"].value,
          name: this.form.name,
          latitude: this.form.latitude,
          longitude: this.form.longitude,
        });

      } else {
        // 追加時(前のまま)
        await this.kintone.addRecord({
          name: this.form.name,
          latitude: this.form.latitude,
          longitude: this.form.longitude,
        });
      }
    
      const alert = await this.alertController.create({
          ...


これで、一覧から登録済みの項目をクリックして、既存レコードの編集・更新ができるようになりました。

ですがこのままだと、一覧からTab2を開いた場合はいいのですが、その後に タブでTab2を開いたときに、前回 Tab2 を開いた時のレコードが表示されてしまいました。

  • 一覧から編集画面へ行く: URLは /tabs/tab2/123
  • その後で「登録」タブをクリック : /tabs/tab2 に行ってほしいけど、/tabs/tab2/123 に行ってしまう..

どうも、タブは前回のページを覚えているようです。

タブ側(tabs.page.html)で以下を設定すると、「登録」タブをクリックしたときには必ず新規登録(/tabs/tab2)へ遷移するようになりました。

/src/app/tabs/tabs.page.html

    ...
    <ion-tab-button tab="tab2" href="/tabs/tab2">   ← tab="tab2" のところに href="/tabs/tab2" を追加
      ...

レコード編集

現在地情報を取得する

次は、現在地の緯度経度を取得し、編集・登録で使用できるようにします。

そのための Ionic のプラグインがあります。

Ionic の Geolocation プラグイン : 現在の緯度経度を取得するためのプラグイン

Geolocation - Ionic Documentation

This plugin provides information about the device's location, such as latitude and longitude. Common sources of location information include Global Positioning System (GPS) and location inferred from network signals such as IP address, RFID, WiFi and Bluetooth MAC addresses, and GSM/CDMA cell IDs.

$ npx ionic cordova plugin add cordova-plugin-geolocation
$ npm install --save @ionic-native/geolocation

このプラグインを使用できるようにするために、app.module.ts のprovidersに追加します。

/src/app.module.ts

...
import { Geolocation } from "@ionic-native/geolocation/ngx";   ← 追加
...

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
  providers: [
    ...
    Geolocation,      ← 追加
    ...
  ],
  ...


編集画面(Tab2)にボタンを追加し、そのボタンを押すと 現在地の緯度経度が入力されるようにします。 (ボタンの色も変えてみました。)

/src/app/tab2/tab2.page.html

    ...
  </ion-list>
  ↓ 追加
  <ion-button color="secondary" (click)="getLocation()">
    <ion-icon slot="icon-only" name="location-outline"></ion-icon>
  </ion-button>

  <ion-button expand="block" (click)="register()">登録</ion-button>
</ion-content>


/src/app/tab2/tab2.page.ts

...
import { Geolocation } from '@ionic-native/geolocation/ngx';   ← 追加
...
  constructor(private navCtrl: NavController,
              private route: ActivatedRoute,
              private alertController: AlertController,
              private geolocation: Geolocation,   ← 引数追加
              private kintone: KintoneService) {}
  ...
  ↓ getLocation() 追加
  async getLocation() {
    // 現在地を取得
    const resp = await this.geolocation.getCurrentPosition();

    this.form.latitude = String(resp.coords.latitude);
    this.form.longitude = String(resp.coords.longitude);
  }
  ...


現在地

…後編へ続く

中編は以上です。

Webアプリとしては、一覧があって、今回追加と編集ができるようになって、さらに現在地の緯度経度が入力できるようになったのですが、 まだ肝心の、スマホアプリになってません。

次回は、これまでのアプリの「スマホネイティブアプリ化」(Android) をぜひやりたいと思います。

kintone

Webとか.NETとかスマホとかマイコンとかシーケンサーとか、いろいろやってるエンジニアです

R3のご提供サービス
自社のシステム開発・移行などをご依頼したい方
お客様とともに作りながら考える
新しいシステム開発
kintone導入・アプリ開発・カスタマイズにお困りの方
kintoneをもっと使いやすくする
gusukuシリーズ