NCMBとFramework7を使った設定画面コンポーネントの紹介と使い方

NCMBはモバイルアプリ開発におけるバックエンド機能（認証、データストア、ファイルストア、プッシュ通知など）を提供しています。バックエンドなのでAPIベースで利用するのが基本で、UI（アプリ側）は各自で開発する仕組みになっています。

現在、数多くのアプリが存在し、その中には定番とも言える機能があります。そうした定番機能を各フレームワークごとに実装しておくことで、再利用性高くNCMBが利用できるかと思います。

今回はFramework7で作った設定画面コンポーネントを紹介します。Monacaアプリでも利用可能です。

![FireShot Capture 140 - 20220304131326 Auth - localhost.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/e0713d0b-b873-4957-8ac0-3f309af70158.jpeg)


## UIについて

コンポーネントは1つのHTMLだけで実装されているのが特徴です。そのため、基本的には以下の方法で導入・利用ができます。

1. 必要なSDKの読み込み
1. 必要なキーの取得
1. NCMBの初期化
1. 設定画面の設置

用意されている画面（機能）は次の通りです。

## 設定画面

設定画面では公開情報、非公開情報を設定できます。公開情報としてはたとえば表示名、プロフィール、プロフィール画像などを想定しています。

![FireShot Capture 140 - 20220304131328 Auth - localhost.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/80016702-c7fc-4d93-4632-22ea3d6fbfb3.jpeg)


### 設定の仕様

- 公開情報、非公開情報によって保存するクラスが異なります
- プロフィール画像をアップロードできます
- 利用できるのはテキスト、数字、ラジオボタンになります

### 必要なライブラリ・SDKの読み込み

今回利用しているライブラリ・SDKは次の通りです。

- NCMB JavaScript SDK

これらを `www/index.html` で読み込みます。

```html
<script src="js/ncmb.min.js"></script>
```

### 必要なキー・トークンの取得

NCMBのアプリケーションキーとクライアントキーを取得します。

- [ニフクラ mobile backend](https://mbaas.nifcloud.com/)

### NCMBの初期化

`www/js/app.js` にてNCMBを初期化します。今回は `www/js/config.json` というファイルにキーを記述しているので、以下のように読み込みを行っています。

```js
const $ = Dom7;
(async () => {
  const device = Framework7.getDevice();
	// 設定ファイルの読み込み
  const config = await (await fetch('./js/config.json')).json();
	// NCMBの初期化
  window.ncmb = new NCMB(config.applicationKey, config.clientKey);

  // Framework7の初期化
  window.app = new Framework7({
    name: 'NCMB Setting',
    theme: 'auto',
    el: '#app',
    id: 'com.nifcloud.mbaas.map',
    store: store,
    routes: routes,
    input: {
      scrollIntoViewOnFocus: device.cordova && !device.electron,
      scrollIntoViewCentered: device.cordova && !device.electron,
    },
    statusbar: {
      iosOverlaysWebView: true,
      androidOverlaysWebView: false,
    },
    on: {
      init: function () {
        if (this.device.cordova) {
          cordovaApp.init(this);
        }
      },
    },
  });
})();
```

## ルーティングの設定

設定画面はログインしていないと表示されない画面になります。[Framework7でNCMBの認証状態によってルーティングを振り分ける - Qiita](https://qiita.com/goofmint/items/25c4ee72952a1360b111)にて紹介した方法を使ってルーティングの設定を行います。ログイン、会員登録画面は[認証コンポーネント](https://qiita.com/goofmint/items/6dc28141da611426abc4)を使っています。

```js
const beforeEnter = async ({resolve, reject, router}) => {
	// 現在ログインしているユーザー情報を取得
	const currentUser = ncmb.User.getCurrentUser();
	// 認証していない場合はnullになる
	if (!currentUser) {
		// rejectを実行
		reject();
		// 認証画面に遷移
		router.navigate('/auth');
		// 処理完了
		return;
	}
	try {
		// データストアにダミーアクセス
		await ncmb.DataStore('Hello').fetch();
		// アクセスできればresolveを実行（ /settingsを表示）
		resolve();
	} catch (e) {
		// 駄目だった場合は認証情報を削除
		localStorage.removeItem("NCMB/"+ncmb.apikey+"/currentUser");
		// rejectを実行
		reject();
		// 認証画面に遷移
		router.navigate('/auth');
	}
};

const routes = [
  {
    path: '/',
    url: './index.html',
  },
  {
    path: '/auth',
    componentUrl: './pages/auth.html',
  },
  {
    path: '/settings',
    componentUrl: './pages/settings.html',
    beforeEnter,
  },
  {
    path: '(.*)',
    url: './pages/404.html',
  },
];
```

そして、今回は `www/index.html` にて設定画面を表示するリンクを追加しています。

```html
<div id="app">
	<div class="view view-init safe-areas">
		<div class="page">
			<div class="navbar">
				<div class="navbar-bg"></div>
				<div class="navbar-inner sliding">
					<div class="title">設定</div>
				</div>
			</div>
			<div class="page-content" style="margin-top: 2em; text-align: center;">
				<a href="/settings"><i class="f7-icons">gear_alt_fill</i></a>
			</div>
		</div>
	</div>
</div>
```

## 設定画面の追加

[settings.html](https://raw.githubusercontent.com/NCMBMania/framework7_setting_component/main/www/pages/settings.html)をダウンロードして、 `www/pages/settings.html` として配置します。

### 設定画面のカスタマイズ

設定画面のHTMLは次のようになっています。

```html
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner sliding">
        <div class="title">設定</div>
      </div>
    </div>
    <div class="page-content">
      <form class="profile">
        <div class="block-title">公開情報</div>
        <div class="list">
          <ul>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label"></div>
                <div class="item-input-wrap">
                  <input type="text" name="displayName" placeholder="ユーザー名" value="${profile.displayName}" data-public="1" />
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label"></div>
                <div class="item-input-wrap">
                  <textarea class="resizable" name="profile" data-public="1">${profile.profile}</textarea>
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">写真</div>
                <div class="item-input-wrap">
                  ${ image ?
                    $h`<img class="profileImage" src=${image} @click=${openFilePicker} />`
                    :
                    $h`<i class="f7-icons size-150" @click=${openFilePicker}>person_alt_circle</i>`
                  }
                  <span style="display: none;">
                    <input type="file" name="photo" data-public="1" accept="image/*" @change=${selectPhoto} />
                  </span>
                </div>
              </div>
            </li>
          </ul>
          <div class="block-title">非公開情報</div>
          <ul>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">誕生日</div>
                <div class="item-input-wrap">
                  <input type="date" name="birthDate" placeholder="2013-09-01" value="${currentUser.birthDate}" />
                </div>
              </div>
            </li>
            <li>
              <div class="block-title">メールマガジン受信</div>
              <label class="item-radio item-radio-icon-start item-content">
                  ${currentUser.mailMagagine ?
                    $h`<input type="radio" name="mailMagagine" value="true" checked="checked" />`
                    :
                    $h`<input type="radio" name="mailMagagine" value="true" />`
                  }
                <i class="icon icon-radio"></i>
                <div class="item-inner">
                  <div class="item-title">する</div>
                </div>
              </label>
              <label class="item-radio item-radio-icon-start item-content">
                ${currentUser.mailMagagine ?
                  $h`<input type="radio" name="mailMagagine" value="false" />`
                    :
                  $h`<input type="radio" name="mailMagagine" value="false" checked="checked" />`
                }
                <i class="icon icon-radio"></i>
                <div class="item-inner">
                  <div class="item-title">しない</div>
                </div>
              </label>
            </li>
            <li>
              <a href="#" class="button button-large" @click=${save}>保存する</a>
            </li>
          </ul>
        </div>
      </form>
    </div>
  </div>
</template>
```

設定は次のようになります。

| 要素 | 内容 |
|-------|-------|
| data-public | "1" を設定するとProfileクラス（公開情報）、ない場合はUserクラス（非公開情報）として保存されます |
| name | データストアのフィールド名 |

利用できる入力形式は以下になります。

| タイプ | NCMBの型 |
|--------|--------|
| text | テキスト型 |
| number | 数値型 |
| date | 日付型 |
| textarea | テキスト型 |
| radio | 真偽値型 |
| file | ファイルストアに保存（画像のみ） |

入力内容はカスタマイズしてください。

![FireShot Capture 140 - 20220304131326 Auth - localhost.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/76521928-a561-b26a-0715-9b32f5f5f792.jpeg)


## まとめ

Profileクラスは誰でも読み込み可、関連したユーザーがだけが編集できる権限で保存されます。ユーザークラスは本人のみ読み書き可能です。メールアドレスなどの情報は公開されないので安全に利用できます。

認証が伴うアプリでは設定画面がはよく利用されるかと思います。イチから開発するのは面倒ですが、これを使うことで実装は簡単になります。ぜひ試してください。

[mBaaSでサーバー開発不要！ | ニフクラ mobile backend](https://mbaas.nifcloud.com/)