Monacaのサンプルアプリである「フォトシェアアプリデザインテンプレート」にバックエンド機能を実装する連載の第2回目。今回はプロフィール編集機能を作ります。

その1:認証編

ライブラリのインストール

元々のフォトシェアアプリでは document.getElementById などを使っていましたが、今後も多用することを踏まえてjQueryに移行しました。次のように変更していきます。

document.querySelector
// ↓
$

document.getElementById('id');
// ↓
$('#id');

document.getElementById('id').classList.contains('class');
// ↓
$('#id').hasClass('class');

document.getElementById('id').classList.remove("class1","class2");
// ↓
$('#id').removeClass('class1 class2');

document.getElementById('id').classList.add('ion-ios-heart', 'like');
// ↓
$('#id').addClass('class1 class2');

document.getElementById('id').style.opacity = 1;
// ↓
$('#id').css('opacity', 1);

最近ではVirtualDOM系のライブラリが人気でjQuery需要が減っていますが、素のDOMを扱うのに比べるとやはり便利です。

ログインチェック

ニフクラ mobile backendのセッションはデフォルトで24時間となっています(延長は可能です)。また、認証情報は都度確認するのではなく、localStorageにあるデータを使っています。そのため、認証情報の有効性については実際にクラウドにリクエストを投げてみないと分かりません。そこで、アプリを立ち上げた際にダミーのリクエスト(なんでも構いません。今回はユーザー情報のアップデート処理を行っています)を送ってセッションが有効かどうかをチェックしています。

const noProfileImage = 'img/user.png';
const noProfileName  = 'No name';
document.addEventListener('init', function(event) {
  var page = event.target;
  const user = ncmb.User.getCurrentUser();
  if (user) {
    user
      // ダミーのリクエスト
      .set('sessionTest', !user.sessionTest)
      .set('authData', {}) // 執筆時点ではこれがないと更新に失敗します
      .update()
      .then(() => {
        $('.userName').html(user.userName);
        $('.realName').html(user.realName || noProfileName);
        $('.profileImage').attr('src', user.profileImage || noProfileImage);
      })
      .catch((err) => {
        // 失敗する場合はログイン画面を表示
        $('#nav')[0].pushPage('register.html', {animation: 'fade'});
      });
  }
  // 省略
});

編集ボタンを押した時のイベント

プロフィール画面で編集ボタンを押した時のイベントを追加します。これはHTMLにonclickで指定します。

<ons-button class="profile_info_button" id="edit_button" onclick="editProfile()" modifier="large">
  Edit Your Profile
</ons-button>

そして、この editProfile() を app.js 内に定義します。ここで行っている処理は、元々名前が表示されていた部分にOnsen UIのテキストボックスを表示することです。Onsen UIは ons- ではじまる独自タグなので、ons.createElement を使ってHTMLタグに変換し、それを #editRealName 要素内に追加します。編集が終わってフォーカスが外れた際には updateRealName() が呼ばれるようにしています。

const editProfile = () => {
  $('#editRealName').html(ons.createElement(`
    <ons-input modifier="underbar" onblur="updateRealName(event)" style="width:50%" />
  `));
};

#editRealName 要素は以下のように定義しています。class属性として realName を指定しています。

<div>
  <p class="profile_extra_wrapper realName" id="editRealName"></p>
</div>

編集が完了した際のイベント

名前の編集が終わった時に呼ばれるのが updateRealName() です。ここではユーザ情報をアップデートする処理を行います。名前の登録と一緒に、ACL(アクセス権限)も更新しておきます。権限は誰からでも閲覧可能 setPublicReadAccess(true) とし、更新や削除は自分だけ .setUserWriteAccess(user, true) としておきます。

const updateRealName = (e) => {
  const user = ncmb.User.getCurrentUser();
  const realName = e.target.value;
  const acl = new ncmb.Acl;
  acl
    .setPublicReadAccess(true)
    .setUserWriteAccess(user, true);
  user
    .set('authData', {}) // 執筆時点ではこれがないと更新に失敗します
    .set('acl', acl)
    .set('realName', realName)
    .update()
    .then(() => {
      // 処理成功した場合は何も出力しない
      $('.realName').html(realName);
    })
    .catch((err) => {
      ons.notification.alert(err.message);
    })
}

処理が完了すると then() が呼ばれますので、テキストボックスになっていた .realName を更新された文字列に置き換えて完了です。

プロフィール写真のアップロード

次にプロフィール写真をアップロードする処理について紹介します。プロフィール画面にプラスのアイコン画像がありますので、これをタップした際にファイル選択できるようにします。
そのために、まずは隠しオブジェクトとしてファイルインプット(<input type="file" />)を追加します。下記HTMLの .profile-image-file-area の部分です。

<div class="profile_image_wrapper">
  <ons-icon size="24px" icon="ion-ios-plus" id="profileImageUpload">
  </ons-icon>
  <img class="profileImage" src="img/profile-image-08.png">
  <div class="profile-image-file-area">
    <input type="file" id="profileImageFile" />
  </div>
</div>
<!-- 省略 -->
<style>
  .profile-image-file-area {
    opacity: 0;
    position: absolute;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
  }
</style>

そして、プラスアイコンタップ時にイベントを設定します。このイベントは隠してあるファイルインプット(<input type="file" />)をクリックするものです。iOSのWebViewではなぜかファイル選択ダイアログが開かないため、繰り返しclickイベントを呼び出す必要があります。

document.addEventListener('show', function(event) {
  var page = event.target;
  // 省略
  if (page.id == "profile-page") {
    page.querySelector('#profileImageUpload').addEventListener('click', (e) => {
      if (ons.platform.isIOS()) {
        $(e.target).click();
      }
      page.querySelector('#profileImageFile').click();
    });
  }
});

プロフィール写真を選んだ後の処理

今回、プロフィール写真については正方形であるという前提になっており、選択された写真をそのままニフクラ mobile backendのファイルストアへアップロードしています。本格的なアプリ開発を行う場合は、写真のリサイズ処理を行った方が良いでしょう。
ファイルストアへアップロードされた写真にはアクセス権限を付与でき、またHTTPS経由でもアクセスできるようになっています。まず、そのための設定としてニフクラ mobile backendの管理画面でHTTPSアクセスを有効にしておきます。

次にプロフィール写真を選んだ時の処理ですが、これは <input type="file" />change イベントを使います。
fileUpload() (処理内容は後述)にてファイルアップロード処理を行っています。一つ目の引数がファイル名、二つ目の引数がファイルオブジェクトになります。
プロフィール写真の名前は他のユーザと被らない名前になっていなければなりません。そこでユーザのID objectId をファイルのプリフィックスとしています。

$(document).on('change', '#profileImageFile', (e) => {
  const file = e.target.files[0];
  const user = ncmb.User.getCurrentUser();
  fileUpload(`${user.objectId}-${file.name}`, file)
    .then((fileUrl) => {
      $('.profileImage').attr('src', fileUrl);
      // 自分の設定を更新
      return user
        .set('authData', {}) // 執筆時点ではこれがないと更新に失敗します
        .set('profileImage', fileUrl)
        .update()
    })
    .then(() => {

    })
    .catch((err) => {
      ons.notification.alert(JSON.stringify(err));
    })
});

アップロードが完了するとその写真のURLが返ってきますので、自分のプロフィール画像を表示している要素を更新します。と、同時にユーザクラスの profileImage フィールドにもURLを保存しておきます。

ファイルアップロード処理

では実際にファイルアップロードを行う fileUpload() の中身を解説します。アップロードは非同期処理になりますので、全体をPromiseで囲みます。
アップロード処理は ncmb.File.upload() で行います。引数としては順番にファイル名、ファイル内容、アクセス権限となっています。アクセス権限は全体に対しての閲覧許可 setPublicReadAccess(true) と、自分だけの書き込み許可 setUserWriteAccess(user, true) を指定しています。
ファイルアップロード処理はプロフィール以外でも使いますので関数化しておくと便利です。

const fileUpload = (fileName, file) => {
  return new Promise((res, rej) => {
    const user = ncmb.User.getCurrentUser();
    // アクセス権限の設定
    const acl = new ncmb.Acl();
    acl
      .setPublicReadAccess(true)
      .setUserWriteAccess(user, true);
    ncmb.File
      .upload(fileName, file, acl)
      .then((f) => {
        res(filePath(f.fileName));
      })
      .catch((err) => {
        rej(err);
      })
  });
};

以上で、プロフィール写真のアップロード処理が完了です。


クラウドに写真をアップロードする場合、その受け口になるサーバを立てたり、バイナリファイルを保存したり面倒な印象があります。そういった面倒さがニフクラ mobile backendを使うことで大幅に軽減されるはずです。今回の記事ではユーザ名とプロフィール画像の変更機能が実装できました。次回は写真投稿機能を作っていきます。

ここまでのソースコードはNCMBMania/photoshare at v0.3にアップロードしてあります(次回の写真アップロード機能も含まれています)。実装時の参考にしてください。