NCMBとFramework7を使ったお知らせコンポーネントの紹介と使い方

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

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

今回はFramework7で作ったお知らせ用コンポーネントを紹介します。Monacaアプリでも利用可能です。

## コンポーネントについて

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

1. 必要なSDKの読み込み
1. 必要なキーの取得
1. NCMBの初期化
1. 関数の呼び出し

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

## お知らせ画面

![localhost 016 2022-02-10-17-39-54.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/33e5e082-dfa6-7e88-8876-3457716845d7.jpeg)

お知らせメッセージを通知するモーダル画面が表示されます。一度表示されたモーダルは記録され、次の機会には表示されません。

### お知らせの仕様

- お知らせは配信日時（開始、終了）を指定できます
- 画像を表示できます（オプション）
- お知らせをタップした際には、In App Browser、またはFramework7のルーティングで画面遷移できます

### 必要なライブラリ・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 Notice',
    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);
        }
      },
    },
  });
})();
```

## 関数の設定

app.jsにて、以下の関数を追加します。

```js
// お知らせを表示する関数
const showNotice = async () => {
  // localStorageから配信済みのお知らせ情報を取得
  const noticeString = localStorage.getItem('notices');
  // localStorageには文字列しか保存できないので、JSONでパースする。[1]はダミーデータ
  const notices = noticeString ? JSON.parse(noticeString) : [1];
  // NCMBのデータストアを呼び出し
  let Notice = ncmb.DataStore('Notice');
  // お知らせがあるか検索
  const notice = await Notice
    .greaterThanOrEqualTo('endDate', new Date) // 配信可能な日時になっているデータの条件指定
    .lessThanOrEqualTo('startDate', new Date)
    .notIn('objectId', notices)
    .fetch();
  // お知らせがなければ終了
  if (!notice.objectId) return;
  // 画像の指定があれば取得
  const file = notice.image ? await ncmb.File.download(notice.image, 'blob') : null;
  // 画像を表示するための関数
  const showImage = (file) => {
    if (!file) return '';
    const url = URL.createObjectURL(file);
    return `<img src="${url}" width="100%" />`;
  }
  const showContent = (html) => {
    if (!html) return'';
    return `<p>${html}</p>`;
  }
  const showButton = (label ) => {
    return `<p><a class="button button-fill detail">${label}</a></p>`;
  };

  const openUrl = () => {
    if (notice.url.match(/^https?:\/\//)) {
      window.open(notice.url, '_blank', {
        location: false,
      })
    } else {
      app.views.current.router.navigate(notice.url);
    }
    popup.close();
  }

  // ポップアップを作成
  const popup = app.popup.create({
    content: `
    <div class="popup notice">
      <div class="row no-gap" style="padding-top: 0.5em;">
        <div class="col-90"></div>
        <div class="col-10" style="text-align:right;padding-right: 0.5em">
          <a href="#" class="link popup-close"><i class="f7-icons">xmark_circle</i></a>
        </div>
      </div>
      <div class="block">
      <div class="block-title">${notice.title}</div>
        ${showContent(notice.content)}
        ${showImage(file)}
        ${showButton(notice.label)}
      </div>
    </div>
    `,
    animate: true,
    // Events
    on: {
      opened: function (popup) {
        // 詳細表示用ボタンのトラッキング
        popup.$el.find('.detail').on('click', openUrl)
        // 表示したお知らせのobjectIdを記録する
        notices.push(notice.objectId);
        if (notices.length === 6) {
          notices.splice(1, 5); // 最後の5つを記録
        }
        // localStorageに書き込み
        localStorage.setItem('notices', JSON.stringify(notices));
      },
    }
  });
  // ポップアップを表示
  popup.open('body', true);
}
```

## お知らせの呼び出し

後は任意の画面にて、 `showNotice` 関数を呼び出せば良いだけです。たとえば次のようになります。

```html
<template>
  <div class="page">
    <!-- Top Navbar -->
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="title sliding">メイン画面</div>
      </div>
    </div>
    <div class="page-content">
    </div>
  </div>
</template>
<script>
  export default function (props, {$f7, $on, $onMounted, $update}) {
    $onMounted(() => {
      // お知らせを表示
      showNotice();
    });
    return $render;
  }
</script>
```

## データストアの設定

NCMBのデータストアでは `Notice` クラスを作成します。フィールドは次の通りです。

| フィールド | 型 | 意味 |
|----------------|----------------|----------------|
| title	         | 文字列  | お知らせのタイトルです。 |
| content	   | 文字列  | お知らせの内容。HTMLが使えます。 |
| image	      | 文字列  | 表示する画像です。ファイルストアのファイル名を記述します。 |
| startDate	 | 日時     | お知らせを配信する日時です。 |
| endDate	  | 日時     | お知らせを配信停止する日時です。 |
| url	           | 文字列  | お知らせをタップした際の飛び先URLです。 |
| label         | 文字列  | お知らせをタップする際のボタンのラベルです。 |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2e54fb68-3c11-91e6-1c4b-50444b4c67fd.png)


## まとめ

アプリでのお知らせ機能はよく利用されるかと思います。イチから開発するのは面倒ですが、これを使うことで実装は関数を呼び出すだけになります。ぜひ試してください。

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