【ハンズオン資料】Monaca + NCMBで日報アプリを作る

MonacaとNCMBで簡単な日報アプリを作ってみるハンズオンの資料です。

---

# Monacaプロジェクトのインポート

https://monaca.mobi/ja/directimport?pid=60055987e788855c2be41eee

よりMonacaプロジェクトをインポートしてください

---

## 仕様について

このアプリの主な機能です。

---

- 認証する
  - ID/パスワード認証を利用します
- 日報を登録する
  - 日報のデータ
    - 日付（作業日） / 作業内容
  - 閲覧は誰でも可、更新や削除は本人のみ
- 日報を一覧する
  - 同じアプリ内で登録されている日報を検索、表示

---

主な利用想定として、同じ部署（または会社）のメンバーが日報を登録します。そして、次の日などに各メンバーの日報を一覧して、困っていることや課題がないか全員で共有します。

---

## 利用した技術

今回はjQueryとOnsen UI、そして日付表示用にdayjsを利用しています。

---

## 画面

日報アプリの画面は全部で3つです。

- ログイン/新規登録画面
- 日報入力画面
- 日報一覧画面

---

### 日報入力画面（form.html）

最初に表示される画面です。ただし認証されていない場合はログイン/新規登録画面へ遷移します。この画面では日付を選択して、その日の作業内容を記述します。

---

### ログイン/新規登録画面（login.html）

ログインと新規登録は処理が似ているので一つの画面にまとめてしまいました。ID/パスワードで認証していますが、追加で表示名も登録できます。

---

### 日報一覧画面（report.html）

登録されている日報を一覧表示する画面です。日付を選択し、その日付を絞り込み条件としています。

---

# 認証を作る

---

## アプリ全体の処理

まず `www/js/app.js` の中で、NCMBを初期化します。これはアプリ全体に関わる処理なので、JavaScriptファイルに記述しています。

---

```javascript
const applicationKey = 'YOUR_APPLICATION_KEY';
const clientKey = 'YOUR_CLIENT_KEY';
const ncmb = new NCMB(applicationKey, clientKey);
```

---

## 認証の判定

まず大事なのは認証の判定です。これは `www/form.html` に実装しています。

---

```javascript
ons.getScriptPage().onInit = async function() {
  // 現在ログインしているユーザを得る
  const user = null; // ここを書き換えます
  if (!user) {
    // 未ログインの場合
    return $('#formNav')[0].pushPage('login.html');
  }
  try {
    // セッションの有効性を確認
    await ncmb.DataStore('Hello').fetch();
  } catch (e) {
    // セッションが無効な場合エラーになるので、その場合はlocalStorageの認証データを削除
    localStorage.removeItem(`NCMB/${ncmb.apikey}/CurrentUser`);
    ncmb.sessionToken = null;
    // ログイン画面に遷移
    return $('#formNav')[0].pushPage('login.html');
  }
}
```

---

現在ログインしているユーザを取得するには `ncmb.User.getCurrentUser();` を実行します。form.htmlの次のコードを書き換えてください。

---

```js
// 変更元
const user = null; // ここを書き換えます
// 変更後
const user = ncmb.User.getCurrentUser();
```
---

さらにもしデータがあったとしてもセッションが有効かどうかはリクエストしてみないと分かりません。そこで適当なクラス（今回はHello）へリクエストしています。ここでエラーが出る場合はセッションが無効になっています。

---

この無効な状態だとログアウト処理 `ncmb.User.logout()` も失敗します。そこでlocalStorageに保存されているデータを直接削除し、さらにセッショントークンも削除してログイン/新規登録画面に遷移します。

---

## ログイン/新規登録画面

ログイン/新規登録画面（login.html）ではユーザ名とパスワード、そして担当者名（displayName）を入力しています。今回は処理を簡略化するため、新規登録とログイン処理を一つにまとめています。

---

### 認証処理

認証を実行する処理は `#login` をクリックした際のイベントで作成していきます。

---

```js
$('#login').on('click', async e => {
  // この中に実装します
});
```

---

まず最初に必要な変数を揃えます。

```javascript
// 記述済み
const userName = $('#userName').val();
const password = $('#password').val();
const displayName = $('#displayName').val();
```

---

次にユーザ登録処理を行います。もしすでに登録されている場合はエラーになりますので、try/catchでエラーが出ないようにします。

---

```javascript
// 記述済み
try {
  await registerUser(displayName, userName, password);
} catch (e) {
}
```

---

```js
// 新規ユーザ登録処理です
async function registerUser(displayName, userName, password) {
  const user = new ncmb.User();
  user
    .set('displayName', displayName)
    .set('userName', userName)
    .set('password', password);
  await user.signUpByAccount();
}
```

---

そしてユーザ登録が済んだら、そのユーザ名とパスワードでログイン処理を実行します。こちらはエラーをハンドリングします。エラーになるとしたら、パスワードの不一致になるでしょう。ユーザ名は表示してもいいように、Aclを誰でも表示可としています。ログイン処理が完了したら日報入力画面 `form.html` に戻ります。

---

```javascript
// 記述済み
try {
  await loginUser(userName, password);
  $('#formNav')[0].resetToPage('form.html');
} catch (e) {
  ons.notification.alert('ログイン失敗しました。ユーザ名、パスワードを確認してください');
  return false;
}
```

---

```js
// ユーザログイン処理です
async function loginUser(userName, password) {
  await ncmb.User.login(userName, password);
  const user = ncmb.User.getCurrentUser();
  const acl = new ncmb.Acl();
  acl
    .setPublicReadAccess(true)
    .setUserWriteAccess(user, true);
  await user.set('acl', acl).update();
}
```

---

## 日報登録処理を作る

日報登録処理は form.html の #send-report をクリックした際のイベント処理に実装していきます。

---

```js
// 日報を登録するボタンを押した時の処理です
$('#send-report').on('click', async e => {
  // 情報の取得
  const date = new Date($('#day').val());
  // 日報の保存処理
  const report = await findOrCreate(date);
});
```

---

### 新規または既存の日報を検索する

すでに同じ日付の日報があれば、それを更新します。もしなければ新規作成とします。そのための処理として `findOrCreate` を呼び出します。

```js
// 日報の保存処理（記述済み）
const report = await findOrCreate(date);
```

---

findOrCreate は次のようになります。日付と入力者の名前で絞り込みます。

---

```js
async function findOrCreate(date) {
  // すでに同じ日付の日報があればそれを返す
  const Report = ncmb.DataStore('Report');
  const user = ncmb.User.getCurrentUser();
  const report = await Report
    .equalTo('userName', user.get('userName'))
    .equalTo('day', date)
    .fetch();
  return report.objectId ? report : new Report();
}
```

---

ACL（アクセス権限）は次のようにします。これは読み込みは誰でも可能、編集は本人のみという条件です。

```js
// ACLを準備
//   読み込みは誰でも可能
//   編集は本人のみ
const acl = new ncmb.Acl();
const user = ncmb.User.getCurrentUser();
acl
  .setPublicReadAccess(true)
  .setUserWriteAccess(user, true);
```

---

次に日報に必要な情報を設定します。

---

```js
// データを設定
report
  .set('day', date)
  .set('report', $('#report').val())
  .set('user', user)
  .set('userName', user.get('userName'))
  .set('acl', acl);
```

---

新規作成の場合は save 、更新の場合は update とメソッドが異なるので、分岐します。ついでに処理完了またはエラー時のメッセージも異なるので、分岐します。

---

```js
// objectIdがない = 新規作成 の場合はsave
// objectIdがある = 更新 の場合はupdate
const method = report.objectId ? 'update' : 'save';
const message = method === 'save' ? '作成' : '更新';
```

---


そして保存します。 try/catchを使います。エラーがあればcatchに移動します。

---

```js
// 新規作成または更新を実行
try {
  await report[method]();
  ons.notification.alert(`日報の${message}に成功しました`);
} catch (e) {
  ons.notification.alert(`日報の${message}に失敗しました`);
}
```

---

### 日報表示時の処理

すでに同じ日付の一方があった場合、その入力内容を表示してあげると親切です。これを `ons.getScriptPage().onShow` で実装します。

---

```js
ons.getScriptPage().onShow = async function() {
  $('#form-title').html('日報登録フォーム');
  const date = new Date(dayjs().format('YYYY-MM-DD'));
  $('#day').val(dayjs(date).format('YYYY-MM-DD'));
  const report = await findOrCreate(date);
  if (report.objectId) {
    // 日報があれば、その内容を表示
    $('#report').val(report.get('report'));
  }
}
```

---

## 日報を表示する

---

次に report.html で日報を検索して表示を行います。 この処理は `#view-report` をクリックした際のイベントで行います。

---

```js
ons.getScriptPage().onInit = async function() {
  // 日報を閲覧するボタンを押した時の処理
  $('#view-report').on('click', async e => {
    // 検索対象の日付を取得
    const day = new Date($('#date').val());
    // 該当日の日報を取得
    const reports = await fetchReports(day);
    // 取得した日報を表示
    viewReports(reports, day);
  })
}
```

---

### 日報を取得する

日報を取得する処理は `fetchReports` になります。これは指定された日付を条件に検索を実行するだけです。

---

```js
// 日報の取得
async function fetchReports(day) {
  const Report = ncmb.DataStore('Report');
  return await Report
    .equalTo('day', day)
    .include('user')
    .fetchAll();
}
```

---

後は viewReports で結果をHTML表示するだけです。

```js
// 日報の表示処理
// 記述済みです
function viewReports(reports, day) {
  const html = [];
  html.push(`<ons-list-header>${dayjs(day).format('YYYY年MM月DD日の日報')}</ons-list-header>`)
  reports.forEach(report => {
    html.push(`
      <ons-list-item expandable>
        ${report.get('user').displayName}
        <div class="expandable-content">
          ${report.get('report').replace(/\n/g, '<br />')}
        </div>
      </ons-list-item>
    `)
  });
  $('#reports').html(html.join(''));
}
```

---

## まとめ

ここまでの処理で認証、日報の登録と閲覧ができるようになりました。利用したmBaaSの機能は次の通りです。

- 会員登録（ID/パスワード）
- 会員認証（ID/パスワード）
- データストア登録/更新
- データストア検索

mBaaSには他にもたくさんの機能があります。ぜひほかのハンズオンで体験してください。