MonacaとNCMBで簡単なアプリを作ってみるハンズオンの資料です。今回はカメラメモアプリを作ってみます。カメラで写真を撮影し、そこにメモ書きを追加して保存しておくというアプリです。この記事ではカメラの処理とメモ書き、そして一覧画面周りを解説します。なおコードはNCMBMania/camera_appにて公開しています。
カメラの処理について
カメラの起動、および写真の選択はcamera.htmlで行います。この処理はNCMBを使っていませんので説明することは多くありません。全体を下記に掲載しますので、コメントを参考にしてください。
<ons-page> <ons-navigator id="nav"> <ons-toolbar> <div class="center" id="toolbar-title">カメラ</div> </ons-toolbar> <div style="text-align: center; margin-top: 150px;"> <ons-card style="text-align: center;"> <ons-icon icon="fa-camera" size="180px" id="camera"></ons-icon> </ons-card> <div style="display: none;"> <input type="file" id="photo" accept="image/*"> </div> </div> </ons-navigator> <script> // 初期化時に実行される関数 // イベント処理を追加します ons.getScriptPage().onInit = async function() { // カメラアイコンをクリックした時のイベント this.querySelector('#camera').onclick = showDialog.bind(this); // 写真が選択された時のイベント this.querySelector('#photo').onchange = showMemo.bind(this); } // カメラアイコンをクリックした時のイベント // 表示を隠してあるinput type="file"をクリックします async function showDialog() { this.querySelector('#photo').click(); } // 写真を選択した時のイベント // 次の画面(photo.html)に選択されている写真を送信します async function showMemo(e) { document.querySelector('#nav').pushPage('photo.html', {data: { photo: e.target.files[0]} }); } </script> </ons-page>
写真の表示とメモ書き
では次に写真が選択された後の画面(photo.html)について紹介します。まず画面は次のようなHTMLになっています。選択された写真とテキストエリアを表示します。
<ons-page> <ons-toolbar> <div class="left"><ons-back-button>カメラ</ons-back-button></div> <div class="center" id="toolbar-title">メモを書く</div> </ons-toolbar> <div style="text-align: center; margin-top: 20px;"> <ons-card style="text-align: center;"> <img src="http://placehold.jp/300x400.png" id="photo" width="300px" height="400px" /> </ons-card> <p> <textarea class="textarea" rows="5" cols="45" placeholder="メモを書いてください" id="memo"></textarea> </p> <ons-icon size="30px" id="spin" spin icon="md-spinner"></ons-icon> <ons-button modifier="large" id="save">保存する</ons-button> </div> </ons-page>
初期化時のイベント設定
画面の初期化時には #save
を押した際のイベントを設定します。
ons.getScriptPage().onInit = async function() { this.querySelector('#save').onclick = saveMemo.bind(this); }
画面表示時のイベント設定
画面を表示した際には選択されている写真をimgタグに表示する処理を行います。
ons.getScriptPage().onShow = async function() { showPhoto.bind(this)(this.data.photo); }
写真の読み込み
showPhotoの解説です。ここではFileオブジェクトをFileReaderで読み込みます。そして、それをimgタグに反映します。
async function showPhoto(photo) { const data = await loadPhoto(photo); this.querySelector('#photo').src = data; }
loadPhoto関数は、この後別な画面でも使うので www/js/app.js に定義してあります。指定されたFileオブジェクトの内容をDataURL形式で返却します。
async function loadPhoto(photo) { return new Promise(res => { const reader = new FileReader(); reader.onload = function(e) { res(e.target.result); } reader.readAsDataURL(photo); }) }
保存ボタンを押した時の処理
メモ書きを追加して保存ボタンを押したら、NCMBへの保存処理を実行します。
async function saveMemo() { // この中に記述します }
まずボタンを多重で押されないようにします。
this.querySelector('#save').style.display = 'none'; this.querySelector('#spin').style.display = 'block';
次に必要な変数を準備します。ファイル名はほかのユーザと被らないように、ユーザ名とユニークなID(マイクロ秒)を付けています。
const body = this.querySelector('#memo').value; const { photo } = this.data; const user = ncmb.User.getCurrentUser(); const fileName = `${user.get('userName')}-${(new Date).getTime()}-${photo.name}`;
そしてまず写真を保存します。同じACLをデータストアでも使うので getAcl として関数にしています。ACL(アクセス制限)はアップロードしたユーザに限りデータの閲覧、更新を可能としています。
try { await uploadPhoto(fileName, photo); // 略 } // ファイルアップロード処理 async function uploadPhoto(fileName, photo) { await ncmb.File.upload(fileName, photo, getAcl()); } // ACLを返すのみ function getAcl() { const user = ncmb.User.getCurrentUser(); const acl = new ncmb.Acl(); acl .setUserReadAccess(user, true) .setUserWriteAccess(user, true); return acl; }
次にデータストアに保存します。ここではメモ書きとファイル名を保存します。
await addMemo(fileName, body); // 略 async function addMemo(fileName, body) { const Memo = ncmb.DataStore('Memo'); const memo = new Memo(); await memo .set('photo', fileName) .set('body', body) .set('acl', getAcl()) .save(); }
処理がうまくいったら、タブバーを一覧表示に切り替えます。さらにボタンを再表示します。
document.querySelector('#tabbar').setActiveTab(1); ons.notification.alert('アップロード完了しました'); this.querySelector('#save').style.display = 'block'; this.querySelector('#spin').style.display = 'none';
写真の一覧表示
写真が保存できたら、一覧画面(list.html)に遷移します。ここでは、まず写真を取得します。
<ons-page> <ons-navigator id="photoNav" page="list-page.html"> </ons-navigator> <template id="list-page.html"> <ons-page> <ons-toolbar> <div class="center" id="toolbar-title">写真</div> </ons-toolbar> <div style="text-align: center; padding-top: 50px;"> <ons-row id="photos"> </ons-row> </div> </ons-page> </template> </ons-page>
JavaScriptは画面表示時のイベントで実行します。
ons.getScriptPage().onShow = async function() { showPhotos.bind(this)(); }
showPhotosでは、メモ一覧を取得して、その内容を表示します。
async function showPhotos() { const photos = this.querySelector('#photos’); // 現在の内容をクリア photos.innerHTML = ''; // メモ一覧を取得 const memos = await loadMemos(); // メモを表示 showMemo(memos, photos); }
メモ一覧の表示はデータストアの機能を使います。作成日の逆順として、最新のものが上に出るようにしています。
async function loadMemos() { const Memo = ncmb.DataStore('Memo'); return await Memo .order('createDate', true) .limit(100) .fetchAll(); }
メモ一覧を受け取ったら、後はHTMLに書き出すだけです。ここでは <ons-card />
で囲んで画像を表示しています。
function showMemo(memos, photos) { memos.forEach(memo => { const dom = document.createElement('ons-col'); dom.innerHTML = ` <ons-card> <img class="photo" src="http://placehold.jp/150x150.png" width="150px" /> </ons-card> `; photos.appendChild(dom); ncmb.File.download(memo.get('photo'), 'blob') .then(async (photo) => { toView.bind(this)(dom, memo, photo); }); }); }
カードをクリックした際のイベントは toView となっています。ここは写真とメモを表示するために viewer.html へ遷移しています。
async function toView(dom, memo, photo) { const img = dom.querySelector('img'); img.src = await loadPhoto(photo); dom.onclick = () => { this.querySelector('#photoNav').pushPage('viewer.html', { data: { memo, photo } }); } }
写真とメモの詳細表示
viewer.html は次のように写真とメモを表示するHTMLとなっています。
<ons-toolbar> <div class="left"><ons-back-button>一覧</ons-back-button></div> <div class="center" id="toolbar-title">メモ</div> </ons-toolbar> <div style="text-align: center; padding-top: 50px;"> <ons-card style="text-align: center;"> <img src="http://placehold.jp/350x350.png" id="photo" width="350px" height="350px" /> </ons-card> <p id="body"> </p> </div>
ここでは画面表示の際に前画面から受け取った情報を表示します。
ons.getScriptPage().onShow = async function() { showPhoto.bind(this)(); } async function showPhoto() { const img = this.querySelector('#photo'); img.src = await loadPhoto(this.data.photo); this.querySelector('#body').innerText = this.data.memo.get('body'); }
これで画像とメモのアップロード、そして一覧表示、詳細表示までの処理ができあがりました。
まとめ
今回はNCMBの以下の機能を利用しました。
- 会員管理
- ID/パスワードによる新規登録とログイン
- データストア
- メモとファイル名の保存
- ファイルストア
- 写真のアップロード
- 写真のダウンロード
mBaaSには他にもたくさんの機能があります。ぜひほかのハンズオンで体験してください。