Monaca(Onsen UI)× mobile backendでささっとクラウド連携アプリを作ってみた

# 自己紹介
こんにちは🌞
初めて[FUJITSU Advent Calendar](https://qiita.com/advent-calendar/2022/fujitsu)に参加させていただきます😊
今回は、富士通クラウドテクノロジーズで提供しているクラウドサービス「[ニフクラ mobile backend](https://mbaas.nifcloud.com/)」を使って簡単なアプリを作っていきたいと思います！

この記事を読んで、「アプリってこんな簡単に作れるんだ☺」と思っていただければ嬉しいです🥰
コードも公開しているのでご自由に活用してくださいね！
少し長めの記事ですが、プログラミングをやったことがある方は**20分程度**で完成する内容になっています🧑‍💻


# 今回開発するアプリ
* Onsen UIを使ってネイティブライクでリッチな見た目にしつつ、クラウド連携もしているアプリをささっと作ってみようと思います
* 今回は[Onsen UIをはじめよう！](https://ja.onsen.io/v2/guide/)にサンプルコードがあるシンプルなアプリをベースにバックエンド機能をつけていきます
* ↓これ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/512a7e42-a8a8-52cf-0499-9b10e4f82627.png)


# 利用するサービス

* [ニフクラ mobile backend（NCMB）](https://mbaas.nifcloud.com/)
    * スマートフォンアプリでよく利用される汎用的な機能（プッシュ通知やDBなど）をクラウドから提供するサービスです。サーバー開発・運用不要でよりリッチなバックエンド機能をアプリに実装することができます。
* [Monaca](https://ja.monaca.io/)
    * Webの標準言語でアプリのフロントエンドを開発できます。また、iOS・Android向けのアプリを同時開発できるので開発コスト高騰や技術者不足などアプリ開発に関わる課題が解決できます。
* [Onsen UI](https://ja.onsen.io/)
    * モバイルアプリに特化した、ネイティブのようなパフォーマンスに優れたUIコンポーネントが特徴です。開発環境のセットアップも必要なくMonacaに組み込むことができます。

# 準備
## ニフクラ mobile backend（NCMB）

* [新規会員登録](https://mbaas.nifcloud.com/signup.htm)（無料から登録可能）
* アプリを作成
    * `WeCanDevelopFaster`とアプリ名を付けます（どうせならかっこいい名前にしようぜ）
 <img width="500px" alt="アプリ名作成イメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/53d53932-73c5-74ea-af30-0631f2b00f9d.png">

* APIキーが発行されますが`OK`で一旦閉じてダッシュボードに遷移したら完了🎉
 <img alt="ダッシュボードイメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/68408427-358a-8b3d-519f-ae8ec21f94f3.png">

## Monaca
* 新規会員登録（無料から登録可能）
* プロジェクトを作成
    * こちらも`NewApp`とプロジェクト名を付けます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/0e20ae48-bf06-4332-6bf4-e8c8c885837d.png)
* エディター画面に遷移したら完了🎉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/e14a0d92-5520-f9b7-ad41-7f73dc7d6b72.png)

# Onsen UIを使ってみる
* https://ja.onsen.io/v2/guide/ に記載されている通りにやってみましょう
    * 以下を`headタグ`に追加
```HTML
<link rel="stylesheet" href="https://unpkg.com/onsenui/css/onsenui.css">
<link rel="stylesheet" href="https://unpkg.com/onsenui/css/onsen-css-components.min.css">
<script src="https://unpkg.com/onsenui/js/onsenui.min.js"></script>
```

* [Webアプリ、ハイブリッドアプリをネイティブに近づける](https://ja.onsen.io/v2/guide/#webapurihaiburiddoapuriwoneitibunidzukeru)のサンプルコードをMonacaにコピペ

<details><summary>コードはこちらにも記載します</summary>

* bodyタグ内
```html
<ons-navigator swipeable id="myNavigator" page="page1.html"></ons-navigator>

<template id="page1.html">
  <ons-page id="page1">
    <ons-toolbar>
      <div class="center">Page 1</div>
    </ons-toolbar>

    <p>This is the first page.</p>

    <ons-button id="push-button">Push page</ons-button>
  </ons-page>
</template>

<template id="page2.html">
  <ons-page id="page2">
    <ons-toolbar>
      <div class="left"><ons-back-button>Page 1</ons-back-button></div>
      <div class="center"></div>
    </ons-toolbar>

    <p>This is the second page.</p>
  </ons-page>
</template>
```
* scriptタグ内
```javascript
document.addEventListener('init', function(event) {
  var page = event.target;

  if (page.id === 'page1') {
    page.querySelector('#push-button').onclick = function() {
      document.querySelector('#myNavigator').pushPage('page2.html', {data: {title: 'Page 2'}});
    };
  } else if (page.id === 'page2') {
    page.querySelector('ons-toolbar .center').innerHTML = page.data.title;
  }
});
```
</details>

# 動作確認（まだクラウド未連携）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/635c3087-900b-fe03-198d-658410abafbb.png)

:::note warn
注意
 `onsenui.min.js:2 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'remove')`というエラーが出ることがあります。その場合は再度Monacaでプロジェクトを作成し直して見てください
:::

<b>画面遷移できましたね🎉</b>


# Monacaとニフクラ mobile backendを連携（クラウド連携）

いよいよクラウド連携していきます！（慣れると3分ほどでできるくらい簡単です😁）
## SDKを導入
* Monaca上部のメニューバーから`設定`＞`JS/CSSコンポーネントの追加と削除`を選択
* `ncmb`と入力し検索
* コンポーネントが出てきたら`追加`を選択し、インストール
    * チェックボックスは必ずチェックを入れて保存してください✅
## APIキーを連携
* scriptタグの中に以下を入力

:::note warn
<b>Webアプリ、ハイブリッドアプリをネイティブに近づける</b>のサンプルコードの<b>上</b>に記載
:::
```js
const applicationKey = "YOUR_APPLICATION_KEY"; //YOUR_APPLICATION_KEYには自身のアプリケーションキーを入れてください
const clientKey = "YOUR_CLIENT_KEY"; //YOUR_CLIENT_KEYには自身のクライアントキーを入れてください
const ncmb = new NCMB(applicationKey, clientKey);
```
:::note info
アプリケーションキー、クライアントキーはNCMBのアプリ設定から確認可能
:::
<details><summary>現時点でのコード全文</summary>

```html
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: content: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
    <script src="components/loader.js"></script>
    <script src="https://unpkg.com/onsenui/js/onsenui.min.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <link rel="stylesheet" href="css/style.css">
    <link rel="stylesheet" href="https://unpkg.com/onsenui/css/onsenui.css">
    <link rel="stylesheet" href="https://unpkg.com/onsenui/css/onsen-css-components.min.css">
    <script>
        //APIキーの設定
        const applicationKey = "YOUR_APPLICATION_KEY";    //★★ここに追加
        const clientKey = "YOUR_CLIENT_KEY";              //★★ここに追加
        const ncmb = new NCMB(applicationKey, clientKey); //★★ここに追加
        
        //Webアプリ、ハイブリッドアプリをネイティブに近づけるのサンプルコード
        document.addEventListener('init', function(event) {
            var page = event.target;
            if (page.id === 'page1') {
                page.querySelector('#push-button').onclick = function() {
                document.querySelector('#myNavigator').pushPage('page2.html', {data: {title: 'Page 2'}});
                };
            } else if (page.id === 'page2') {
                page.querySelector('ons-toolbar .center').innerHTML = page.data.title;
        }
    });
    </script>
</head>
<body>
	<ons-navigator swipeable id="myNavigator" page="page1.html"></ons-navigator>

    <template id="page1.html">
      <ons-page id="page1">
        <ons-toolbar>
          <div class="center">Page 1</div>
        </ons-toolbar> 
        <p>This is the first page.</p>
        <ons-button id="push-button">Push page</ons-button>
      </ons-page>
    </template>
    
    <template id="page2.html">
      <ons-page id="page2">
        <ons-toolbar>
          <div class="left"><ons-back-button>Page 1</ons-back-button></div>
          <div class="center"></div>
        </ons-toolbar>   
        <p>This is the second page.</p>
      </ons-page>
    </template>
</body>
</html>
```
</details>

<b>これでMonaca PJ`NewApp`とニフクラ mobile backendクラス`WeCanDevelopFaster`が連携されました🎉</b>

# DBへデータを保存する
* Page2が寂しいので、ボタンを押したら`ボタンが押された！`というデータをクラウド上に保存するように機能を追加してみましょう

### フロントエンド（bodyタグ）
* 以下のコードを追加する
```html
<ons-button onclick="addData()">データ送信</ons-button>
```

<details><summary>追加後のコード全文（bodyタグ内）</summary>

```html
<template id="page2.html">
  <ons-page id="page2">
    <ons-toolbar>
      <div class="left"><ons-back-button>Page 1</ons-back-button></div>
      <div class="center"></div>
    </ons-toolbar>
    <p>This is the second page.</p>
    <ons-button id="addData()">データ送信</ons-button> //★★ここに追加
  </ons-page>
</template>
```
</details>

* ボタンができました🎉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/48e00e58-30bf-8aac-d2b4-b13f9b1254af.png)

### バックエンド（scriptタグ）
* ボタンが押されたらDB（NCMBではデータストアといいます）にデータを保存します
    * DBにデータを入れる場合はデータを入れる入れ物`クラス`を作成します
        * 今回はクラス名を`DataClass`とします
        * `scriptタグ`内の最後に追記します
```js
function addData(){
    //クラスを生成
    const DataClass = ncmb.DataStore("DataClass");
    //値を設定するクラスインスタンスを生成
    let dataClass = new DataClass();
    //データストアに保存するデータを定義
    dataClass.set("Data", "ボタンが押された！")
             .save()
}
```

# DBに保存されているか見てみる
ニフクラ mobile backendのデータストアを見てみましょう🤩
* `DataClass`が作成されている
* DataClass内に`Data`フィールドが作成され、「ボタンが押された！」というデータが入っていたら成功です🎉

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/39d773f4-5764-620f-a461-b7b53ae62cd0.png)


# おまけ

## 保存ボタンを押したときに保存成功・失敗のアラートを表示したい

`scriptタグ`の最後 `.save()`につなげて以下を追記してみてください
```javascript
.then(function(){
      alert("保存成功")
})
.catch(function(err){
      alert("保存失敗" +JSON.strindify(err))
})
```
<details><summary>現時点でのコード全文(function addData()内のみ)</summary>

```javascript
function addData(){
    //クラスを生成
    const DataClass = ncmb.DataStore("DataClass");
    //値を設定するクラスインスタンスを生成
    const dataClass = new DataClass();
    //データストアに保存するデータを定義
    dataClass.set("Data", "ボタンが押された！")
             .save()
             .then(function(){                           //★★ここに追加
                 alert("保存成功")                       //★★ここに追加
             })                                         //★★ここに追加
             .catch(function(err){                      //★★ここに追加
                 alert("保存失敗" + JSON.stringify(err))//★★ここに追加
            )                                          //★★ここに追加
}
```
</details>

#### 動作確認
<img width="400px" alt="アラートイメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/235bda48-cac9-364e-9e8c-12150a0ecc69.png">

:::note warn
アラートがわかりやすいように[Monacaデバッガー](https://ja.monaca.io/debugger.html)で動作確認しています<br>プレビューで動作確認をした場合はプレビュー画面外（画面の真ん中あたり）に表示されます

:::

**アラートが表示できましたね🎉**

## 任意の文字を保存したい

* `bodyタグ`にテキスト入力用の「テキストエリア」を追加します
    * 参考：[textarea](https://onsen.io/v2/api/css.html#textarea-category)
```html
<textarea id="memo" class="textarea" rows="3" placeholder="Textarea"></textarea>
```
* `scriptタグ`のaddData内に以下のコードを以下のように追加・変更します
    * 以下のコード内「//★★ここに追加」「//★★変更」部分のみ更新しています
        * テキストエリアで入力された文字を`ID=memo`とし、そのIDを取得して保存しています
    * `function addData()`の中身を以下のように変更します
```javascript
function addData(){
    //クラスを生成
    const DataClass = ncmb.DataStore("DataClass");
    //値を設定するクラスインスタンスを生成
    const dataClass = new DataClass();
    //ID=memoを取得
    const memo = document.getElementById("memo").value; //★★ここに追加
    //データストアに保存するデータを定義
    dataClass.set("memo", memo) //★★変更
             .save()
}
```

#### 動作確認
* アプリ側
<img width="400px" alt="保存イメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/a438a79d-5d93-c62a-a5b4-493a12c8ff33.png">

* NCMBのデータストア
<img width="800px" alt="dbイメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492105/ead171c5-bdd8-e97d-7eb7-50ecd0b354db.png">

**データが保存できましたね🎉**

# 最後に
今回はMonca（Onsen UI）を利用してNCMBでアプリを開発してみましたが、他にもNCMBは`iOS(Swift) / iOS(Obj-C) / Android(Kotlin)/ Android(Java) / JavaScript / Unity `で利用可能です👌
Monaca, Onsen UI, NCMBを使うと一瞬でアプリができるので
短期間で本格的なアプリを開発してみたい📱という方は是非一度お試しくださいね

### 企業でNCMBを利用される方は相談会を随時開催中です
https://inquiry.nifcloud.com/webeq/pub/mbaas/consulting

### 富士通グループの皆様はこちらをご覧ください
* FUJITSU Hybrid IT Service クラウドサービスの「mobile backend」としても販売しております
    * https://www.fujitsu.com/jp/services/hybrid-it-service/digital-application-platform/
        * サービスサイト
            * https://mbaas.nifcloud.com/dap/

##### mobile backend を最大で2カ月間無料でご利用いただけるトライアルを実施中です
https://clouddirect.jp.fujitsu.com/service/campaign-mobile-backend
* FJcloud-Vの無料トライアルの内容に含まれるため、お申込み時の商品名は「【無料トライアル】FJcloud-V」となります。
    * トライアルのお申込みはこちら
        * 法人向け
            * https://clouddirect.jp.fujitsu.com/products/detail/f9ba7cdd-0022-11ec-ab30-a4da592dd6a9
        * 個人向け
            * https://personal.clouddirect.jp.fujitsu.com/products/detail/9c96bee6-5976-11ec-83f6-00a128e40050