ColdFusion Builder 3 with AngularJSを使ってMobileネイティブアプリを体験してみる
はじめに
Adobe ColdFusion 11が2014年4月30日に正式リリースされ、同時にAdobe ColdFusion Bulider 3もパワーアップしてリリースされたようです。目玉はモバイルアプリ開発がサポートされたこととHTMLからPDFへの変換がパワーアップしたようです。そこで今まで手をつけていなかったネイティブアプリの領域を体験してみたので、その体験してみたことをまとめてみました。やったことは基本的にColdFusion Developer Centerに掲載されています。。。
環境の準備
- MacOS X 10.8.5
- ColdFusion 11 デベロッパー版(トライアル版はこちらから)
- ColdFusion Builder 3(トライアル版はこちらから)
- jquery
- jquery.mobile
- AngularJS
※ColdFusion Builder 3をインストールする際にColdFusion 11も同時にインストールすることができるので、そのオプションで入れた方はColdFusion 11 デベロッパー版は不要だと思われますが当方では確認してません。
署名キーの準備
ColdFusion Builder 3を使ってビルドする際に必要となるAndroid,iOS用の署名キーを用意します。Androidの場合は自己証明書を用意してそれを使ってアプリに署名すればいいだけなのでいいのですが、iOSの場合はiOS Developer Programに登録しないといけないので、プロビジョニングファイルの作成方法等についてはここでは割愛します。
cd /usr/bin keytool -genkey -v -keystore [appname].keystore -alias [alias] -keyalg rsa -keysize 2048 -validity 10000
[appname]と[alias]の部分は適宜変更してください。
上記のコマンドを実行すると質問が表示されますので、指示に従って入力します。日本語が化けてしまう場合はターミナルの環境設定を一時的に変更すれば解決します。
文字エンコーディングのところをUnicode(UTF-8)から日本語(Mac OS)に変えます
keystoreの作成が完了したら文字エンコーディングのところをUnicode(UTF-8)に戻しておきます。
ColdFusion Builder 3の設定
まだユーザ登録してない場合はhttps://build.phonegap.comでユーザ登録できます
※無料だとPrivateアプリ1つ、Publicアプリ無制限で利用できます。Buildする際に必要になるので先に登録をすませておきます。
次にモバイルプロジェクトをインポートします。
利用させて頂いた元ソース(Original)はCreating CFMobile Application using AngularJSにあります。
ダウンロードしたzipファイルを展開したフォルダを指定します。(一応webroot配下に置きました)
元ソースでは日付と金額と備考及び画像添付を入れて登録するとDivタグにレコードを追加して表示されるものになっていますがこのデモでは日付と金額と出発駅と到着駅及び画像添付を入れるようにちょっと変更してみました。(下記はindex.cfmファイルです)
<!DOCTYPE html> <html lang="ja" ng-app="CFAngularMobileSample"> <head> <META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta charset="utf-8"> <link rel="stylesheet" href="css/jquery.mobile-1.4.2.min.css" ></link> <script src="js/jquery-2.1.0.min.js" ></script> <script src="js/index_app.js"></script> <script src="js/jquery.mobile-1.4.2.min.js" ></script> <script src="js/angular.min.js" ></script> <script src="js/angular_app.js"></script> <style > table th { text-align:left; } </style> </head> <body > <!--- メインページ ---> <div data-role="page" id="mainPage"> <div data-role="header" data-position="fixed" > <h2 id="toc-3">電車代備忘録</h2> <div style="padding-left:10px"> <button class="ui-btn ui-btn-inline" id="addBtn">追加</button> <button class="ui-btn ui-btn-inline" id="deleteAllBtn">全件消去</button> </div> </div> <div class="ui-content"> <div ng-controller="ListItemsCtrl" id="angularDiv1"> <table width="100%" ng-if="lists.length > 0"> <tr> <th>日付</th> <th>金額</th> <th>出発駅</th> <th>到着駅</th> <th></th> </tr> <tr ng-repeat="list in lists" > <td>{{dateToStr(list.expenseDate)}}</td> <td>{{list.amount | number}}</td> <td>{{list.stStation}}</td> <td>{{list.edStation}}</td> <td> <span ng-if="list.receiptPath.length > 0"> <a href="{{list.receiptPath}}" class="imageReceipt">receipt</a> </span> <span ng-if="list.receiptPath.length == 0"></span> </td> </tr> </table> <div ng-if="lists.length == 0"> レコードがありません </div> </div> </div> <div data-role="footer" data-position="fixed" > <h5>Created with CFBuilder 3 and AngularJS</h5> </div> </div> <!--- アイテム追加のダイアログボックス表示 ---> <div data-role="page" id="addDlg" > <div data-role="header"> <h2 id="toc-4">レコード追加</h2> </div> <div class="ui-content" ng-controller="addExpDlgCtrl" id="addDlgContent"> <table width="100%"> <tr> <td>日付:</td> <td><input type="date" id="dateTxt" ng-model="date"></td> </tr> <tr> <td>金額:</td> <td><input type="text" id="amtTxt" ng-model="amount"></td> </tr> <tr> <td>出発駅:</td> <td><input type="text" id="stTxt" ng-model="stStation"></td> </tr> <tr> <td>到着駅:</td> <td><input type="text" id="edTxt" ng-model="edStation"></td> </tr> <tr> <td colspan="2"><button class="ui-btn ui-btn-inline" id="attachRcptBtn">レシート添付</button></td> </tr> <tr> <td colspan="2"><img id="receiptImg" width="50" ng-src="{{imagePath}}" ng-show="imagePath.length > 0"></img></td> </tr> </table> </div> <div data-role="footer"> <button class="ui-btn ui-btn-inline" id="dlgOKBtn1">登録</button> <button class="ui-btn ui-btn-inline" id="dlgCancel1">キャンセル</button> </div> </div> <!--- レシートのリンククリック時に画像を表示させる ---> <div data-role="dialog" id="receiptDlg" style="overflow:scroll"> <div data-role="header"> <h2 id="toc-5">レシート</h2> <div> <button class="ui-btn ui-btn-inline" id="receiptFitBtn">フィットさせて表示</button> <button class="ui-btn ui-btn-inline" id="receiptFullBtn">フルサイズで表示</button> </div> </div> <div class="ui-content" style="overflow:scroll" ng-controller="displayReceiptCtrl"> <img id="receiptImgLarge" ng-src="{{imagePath}}"> </div> <div data-role="footer"> </div> </div> </body> </html> <!--- デバイスAPIの利用宣言 ---> <cfclientsettings enabledeviceapi="true" > <cfclient> <cfinclude template="index_include.cfm" > </cfclient>
次にAngularJSのコントローラを定義します。(下記はangular_app.jsファイルです)
CFAngularMobileSampleApp = angular.module("CFAngularMobileSample",[]); CFAngularMobileSampleApp.controller( "ListItemsCtrl", [ "$scope", function ($scope) { $scope.lists = []; $scope.addList = function (list) { $scope.lists.push(list); } $scope.dateToStr = function (dateNum) { var tmpDate = new Date(dateNum); return dateFormat(tmpDate,"yyyy/mm/dd"); //this is cfclient function } } ] ).controller ( "addExpDlgCtrl", [ "$scope", function ($scope) { $scope.resetExpense = function () { $scope.amount = 0; $scope.stStation = ""; $scope.edStation = ""; $scope.date = new Date(); //デフォルト表示 $scope.imagePath = ""; } $scope.resetExpense(); } ] ).controller ( "displayReceiptCtrl", [ "$scope", function ($scope) { $scope.resetData = function() { $scope.imagePath = ""; } $scope.resetData(); } ] )
あとはボタンクリック時の処理をindex_app.jsに記述して、saveList()などのfunctionを呼び出し、index_include.cfm内に<cffunction name="saveList">〜〜〜</cffunction>として処理を記述しておきます。あとはcffunction内からcfcを呼び出してデータベースの更新処理を行います。下記がindex_include.cfmを一部抜粋したソースコードです
<cfclient> <!--- レシートの画像を保存するフォルダ名 ---> <cfset variables.appFolderName = "CFMobileSample"> <cftry> <!--- Create an instance of ListManager.cfc and get all expenses from it ---> <cfset expMgr = new cfc.ListManager()> <cfset lists = expMgr.getExpenses()> <cfscript> angular.element("##angularDiv1").scope().$apply(function($scope){ $scope.lists = lists; }); </cfscript> <cfcatch type="any" name="e"> <cfset alert(e.message)> </cfcatch> </cftry> <cffunction name="saveList"> <cfscript> var scope = angular.element("##addDlgContent").scope(); var dateStr = scope.date; var amtStr = trim(scope.amount); if (dateStr == "" || amtStr == "") { alert("Date and amount are required"); return; } if (!isNumeric(amtStr)) { alert("Invalid amount"); return; } var amt = Number(amtStr); var tmpDate = new Date(dateStr); var stStation = trim(scope.stStation); var edStation = trim(scope.edStation); var receiptPath = ""; if (isDefined("_tmpImagePath")) receiptPath = _tmpImagePath; var expVO = new cfc.ListVO(tmpDate.getTime(),amt,stStation,edStation,receiptPath); var expAdded = false; try { expMgr.addList(expVO); expAdded = true; } catch (any e) { alert("Error : " + e.message); return; } </cfscript> <cfset $("##addDlg").dialog("close") > <cfif expAdded eq true> <cfscript> angular.element("##angularDiv1").scope().$apply(function($scope){ $scope.addList(expVO); }); </cfscript> </cfif> </cffunction> </cfclient>
DB更新している部分を一部抜粋(下記はListManager.cfcファイルです。)
component client="true" { this.dsn = "sample_db"; function addList (expVO) { queryExecute( "insert into trainfare (expense_date,amount,stStation,edStation,receipt_path) values(?,?,?,?,?)", [expVO.expenseDate,expVO.amount,expVO.stStation,expVO.edStation,expVO.receiptPath], {"datasource":this.dsn} ); //get auto generated id queryExecute( "select max(id) maxId from trainfare", [], {"datasource":this.dsn, "name":"rs"} ); expVO.id = rs.maxId[1]; } }
ビルドしてみる
ソースの変更が完了したので、実際にビルドをしてみます。
ウィンドウ/設定で設定ウィンドウを開きます。
- PhoneGap Build サービスへのログイン情報を入力します
- iOSキーを登録します。(設定しない場合はipaファイルが作成されないだけで特に問題ありません)
- Androidキーを登録します。(こちらは署名キーの準備のところで作った自己証明書を指定します)
次にプロジェクトのプロパティを設定します。
ColdFusion サーバーの設定でサーバーを指定します。
※ColdFusion Builder 3と一緒にCF11を入れた場合はdefaultLocalになっているかと思います。
ColdFusion モバイルプロジェクトを設定します。パッケージビルドする際にはApplication.cfmのチェックは外しましょう。
最後にリソースの選択の右隣にあるPhoneGapタブをクリックしてアプリケーションで利用するデバイスネイティブ機能にチェックを付けます。これに最初気づかないで何回がビルドしてアプリがエラーになりました。。。
このアプリではFileとCameraにチェックを付けます。
PhoneGap Buildサービスにログインした直後の画面
それではプロジェクトのところで右クリックしてPhoneGap Buildを生成を選択します。
ビルドを実行するとビルドステータスのところがPendingに変わり、暫くするとダウンロードに変わります
再度PhoneGap Buildサービスの画面を見てみると下記のようにアプリケーションが出来上がってることが確認できます。
デフォルトではPrivateアプリケーションとなっていますので、一般公開したい場合にはPublicにする必要があります。私は無料版ユーザなのでプライベートアプリは1つしかもてません。この状態で2つ目のアプリケーションをビルドしてもプライベートアプリが既に1つあるので何も変わらないです。複数作成する場合は先に一般公開にするかアプリそのものを削除しましょう。
ダウンロード&インストールして実行してみる
ようやくパッケージ化ができたので、実際にダウンロードして実機で動作させてみました。
Android4.xで動作させたスクリーンショット
Android版立ち上げ直後 | Android版データ登録後 |
---|---|
iOS版立ち上げ直後 | iOS版データ登録後 |
---|---|
どちらも無事動作しました。
ただ、、、iOS版の場合で動作確認する際にはPhoneGap Build サービス経由でインストールしないとインストールできませんでしたので、試される方はその点ご注意下さい。
※PhoneGap Build サービス経由からインストールする為にPrivateアプリになっているのを一旦Publicアプリにしました。下の画像はプライベートアプリの状態です。
設定のところをクリックして Public Application にチェックをつけて保存すると再ビルドが走ります。
下記の様に再ビルド時にエラーが出た場合はキーのロックを一度解除してあげます。(パスワードはkeystore作成時のものです)解除後に再ビルドをクリックするとapkファイルが出来上がってます。
最終的には下記の様になればOKです。
まとめ
cfclientタグ内のCFMLコードは全てJavascript,HTMLに置き換えられて各モバイル端末向けにパッケージ化されます。この辺りはすべてビルドとやるだけでColdFusion Builder 3が全部自動でやってくれるので非常に楽でした。
またColdFusionサーバはこのサンプルアプリをJavascript、HTMLに変換するだけのエンジンとなっています。当然バックエンドとしてColdFusionを使うこともできますが、まずはMobileアプリを体験するという位置づけの検証なので、機会があったらサーバと通信するあたりも検証してみたいと思います。
試してみようという方々のご参考になればと思います。