build-your-first-mobile-app(第一个 PhoneGap cordova Coldfusion App)
摘自:http://www.adobe.com/devnet/coldfusion/articles/build-your-first-mobile-app.html
Introduction
Use of mobile devices and mobile applications are ever increasing. Mobile applications are becoming important for businesses. If you have a customer facing web application or employee facing intranet application, chances are that you might want to provide some of the functionalities on mobile devices.
ColdFusion 11 and ColdFusion Builder 3 makes HTML5 mobile application development easy by supporting the complete workflow. You can write mobile applications using CFML and use all the tools in ColdFusion 11 and ColdFusion Builder 3 to debug, test, and package your applications.
This article focuses on development part of the workflow. You will learn how to develop your first mobile application using ColdFusion and run it on mobile devices.
This section describes the tools required for building HTML5-based mobile applications using ColdFusion. In the context of this article, mobile application means HTML5 mobile application and not native mobile application.
Installing ColdFusion Builder 3
For the purpose of this article, you need to install only ColdFusion Builder 3 (CFB). It is now bundled with ColdFusion server and automatically configured in the CFB after installation. Make sure you select the option to install server in the installation wizard of ColdFusion Builder.
Account on build.phonegap.com
ColdFusion 11 uses PhoneGap APIs to provide access to certain device functionalities. ColdFusion Builder uses PhoneGap build to package mobile applications. Therefore you will need to create an account on build.phonegap.com, if you haven’t done it already.
Setting up mobile testing environment
The article first describes how to create a very simple mobile application, which you can run in your desktop browsers (only Chrome or Safari). It then goes on to describe how to improve the application with additional features, better code structure, and user interface. You will need a mobile device (iOS or Android) to test the application. If you do not have a physical device, you can use the iOS Simulator or the Android Emulator.
iOS Simulator is packaged with Xcode (IDE for developing applications for Mac and iOS). Download Xcode from iOS Dev Center.
Android emulator is a part of Android SDK.
PhoneGap Shell Applications
The application we are going to build in this article use Camera APIs. If you use Device APIs like Camera, Contacts etc, you will not be able to run the application in normal Web browsers (either desktop or mobile browsers). The reason is that such applications ,developed using ColdFusion, use PhoneGap APIs which normal browsers do not support. Therefore ColdFusion Team has developed mobile applications (for iOS and Adnroid), we call them PhoneGap Shell Applications, that embed web browser and at the same time support PhoneGap APIs in them. So if you want to quickly test your application on mobile/simulator/emulator without packaging and installing it, then download and install PhoneGap Shell application. We distribute this application as a APK file, which you can directly install on Android.
We cannot distribute this application for iOS (IPA file) because of licensing restrictions. So we have made the Xcode project avilable for the iOS version of this application.
Let us start building a simple database-based mobile application. We will then add some of the device APIs and see how to package and run it on mobile devices.
The application we are going to build is called ‘Expense Tracker’. Initially, we will build a very simple HTML UI to enter expense information and retrieve list of expenses. We will then enhance the application to associate image (image of the bill) with expense items.
To keep the code simple, we will implement limited options. However, you can easily enhance the application to modify/delete individual expense items.
Figure 1 is the first application we will build. It is a very simple expense tracer.
Figure 2 and Figure 3 are screen shots of improved Expense Tracker with the additional feature of attaching image captured from device camera to an expense item.
Building Simple Expense Tracker
In this section, we will create the ‘Simple Expense Tracker’ with very basic user interface. We will implement the following functionalities:
- Adding a new expense item
- Fetching expense items from the database and displaying them
Configuring server in ColdFusion Builder 3
If you had selected the option to install ColdFusion server with ColdFusion Builder, then you will see the 'defaultLocal server' configured in ColdFusion Builder. Initially, the server status will be ‘stopped’.
Select the server and click ‘Start Server’ (play) button in the toolbar. Or you can right click the server and select ‘Start Server’ option from the pop-up menu. After the server is started successfully, you should see status of the server changed to ‘Running’. It is important that the server status should be ‘Running’ for creating this mobile application.
If you have not installed the buit-in server of ColdFusion Builder 3, then you will need to configure the server in ‘CF Servers’ view. You can either use ‘Import Local Server’ option (first button in the view-toolbar from left) or ‘Add Server’ option (second button in the view-toolbar). You can access these options from pop-up menu too, after right clicking in the view.
Creating CF Mobile Project
In ColdFusion Builder 3, select menu File->New->ColdFusion Mobile Project. You should see mobile template page of the new mobile project wizard. Make sure that the BLANK template is selected and click Next. You shoud see ‘Project Information’ page of the wizard.
Enter the project name ‘SimpleExpenseTracker. Keep the other default options and click Next. You should see the ‘Server Details’ page of the wizard with ‘defaultLocal’ server selected.
Click Next. You should see ‘Link Folders’ page of the wizard.
Click Finish. You should see the project created in the Navigator View. The project files are created in CFB workspace folder and a folder is created in wwwroot of the server with the project name. This folder is linked in the new project. The wizard also creates the index.cfm file and opens it in the editor.
Writing the application code
This application has a simple HTML form at the top where you can enter the expense information and click on the Add button to save it.
Copy the code from SimpleExpenseTracker/index.cfm (available in the attachement) file to index.cfm in your project. The index.cfm file starts with the HTML code to create the input fields for expense date, amount, and description. It then creates the HTML table (id expList) that displays the expense items.
In the JavaScript block it registers click event handler for the Add button.
The cfclient code starts after the JavaScript block. It first creates the expense database table if it is not already created, using cfquery. Unlike in the server side CFML code, you do not need to pre-configure datasource in cfclient. The datasource will be created if it is not present.
<!--- on client side you do not need to pre-configure datasource ---> <cfset variables.dsn = "expense_db">
<!--- create database if not already created ---> <cfquery datasource="#variables.dsn#"> create table if not exists expense ( id integer primary key, expense_date integer, amount real, desc text ) </cfquery>
Note that the code in this application is inside the cfclient tag and is executed on the client side (on mobile device). Though there is CFML code inside the cfclient tag, when the application is installed on a mobile device, CF server does not come into play. ColdFusion converts all the CFML code to corresponding JavaScript code at the time of packaging the application. Even the database created in the application is on the client side. Code generatd by ColdFusion for client side query uses Web SQL APIs of WebKit browser. So expense table will be created in the browser.
<!--- Get expense records from the table ---> <cfquery datasource="#variables.dsn#" name="expenses"> select * from expense order by expense_date desc </cfquery>
<!--- Loop over expenses query object and display ---> <cfloop query="rs"> <cfset var tmpDate = new Date(expense_date)> <cfset addExpenseRow(expense_date,amount,desc)> </cfloop>
The application queries the expense table and gets all the records using the client side cfquery tag. It loops over the result and displays the records in an HTML table by calling the addExpenseRow function. The addExpenseRow function uses the cfsavecontent tag to build HTML text for adding row to the HTML table and finally uses the document.getElementById function and innerHTML property to append row to the table.
The addExpense method is called in response to the click on the Add button (called from JavaScript event handler in the script block). The application uses cfquery to execute insert statement with values passed to the function. Then it calls the addExpenseRow function to append the newly added expense item to the HTML table.
Most of the code in the cfclient block should look familiar to you, because it is very similar to the code you would have written on the server side for accessing database.
Running the application
This application does not really use any device specific API, so you can run this application in any WebKit-based browser (Chrome or Safari). The client-side database APIs used in this application are supported by the WebKit browsers on desktop as well as on mobile devices.
The application creates datasource and tables in the browser. I normally test such applications on desktop by opening the browser in incognito mode – that way data source is not permanent and I can make any changes to the database table without dropping the table first. So open Chrome in incognito mode (menu File->New Incognito Window). Open developer tools using menu option View->Developer->Developer Tools. Click on Resources tab and expand Web SQL node in the left pane. You will see that there are no Web SQL databases.
Now, open the expense tracker application in the browser window by typing the application URL, e.g.http://localhost:8600/SimpleExpenseTracker/
. After the page is loaded, check the Web SQL node in the Resources tab. You should see the expense_db database and expense table created under it. Add one expense item by entering valid values in the input fields and clicking the Add button. Observe that the record is inserted in the expense table in the Resources tab.
You can package and run this application on mobile now. But we will wait till we restructre the code and add some more features to this application.
Restructuring the application and using device APIs
Code in the application we developed above is not very well structured. There is no clear separation of user interface and data access code. In this section we will restructure the application, improve user interface a bit and add a feature to attach image (using device camera) of bill/receipt to an expense item.
Download the entire project for this application (available in the attached sample file) and unzip in wwwroot of the server. CFMobileExpenseTracker will be created in wwwroot folder. You can esaily import the project in ColdFusion Builder.
Select menu option File->Import.
From the wizard, select ColdFusion->Import existing Projects node and click Next.
Click browse button and select CFMobileExpenseTracker from wwwroot (the folder created after you unzip the project in wwwroot). Click Finish. You should see the project CFMobileExpenseTracker in the Navigator view
The source code contains separate folders for css, JS and CFCs.
All data access code is in cfc/ExpenseManager.cfc. The CFC is maked as client side CFC by attribute client=”true”. The constructor of the CFC creates ‘expense’ table on the client side, if it is not already created. See the createTable function in ExpenseManager.cfc:
function createTable() { try { queryExecute("drop table expense",[],{"datasource":this.dsn}); } catch (any e){ //ignore } queryExecute( "create table if not exists expense (id integer primary key, expense_date integer, amount real, desc text, receipt_path text)", [], {"datasource":this.dsn}); }
In the previous application, we had used the cfquery tag to execute SQL statements. There is a new function, queryExecute, avilable in ColdFusion 11 that can be used in cfscript code.
This function is avilable both on the server as well as on the client side. The first argument to the function is SQL statement, second argument is parameter values (you specify the parameter in SQL statement as ‘?’) and the third argument is a Struct with attribute name-value pairs.
Note that we have crated additional column, receipt_path, in the expense table.
See addExpense function in ExpenseManager.cfc to understand how to pass values for parameters in queryExecute function:
function addExpense (expVO) { queryExecute( "insert into expense (expense_date,amount,desc,receipt_path) values(?,?,?,?)", [expVO.expenseDate,expVO.amount,expVO.description,expVO.receiptPath], {"datasource":this.dsn} ); //get auto generated id queryExecute( "select max(id) maxId from expense", [], {"datasource":this.dsn, "name":"rs"} ); expVO.id = rs.maxId[1]; }
The addExpense function takes component of type ExpenseVO (this component is in cfc folder) as argument and uses its fields to insert a new expense row in the table. The first queryExecute inserts a new row and the second queryExecute fetches the auto generated ID.
The index.cfm file (found in the project folder) mostly contains the HTML code to create the user interface. It uses JQuery and JQuery Mobile libraries. The fist CF tag in index.cfm has:
<cfclientsettings enabledeviceapi="true" >
This statement tells ColdFusion to enable device APIs for this application and include required libraries. This statment must preceed the first cfclient block in the application.
To separate the cfclient code from the main UI code, we can just include a file in the cfclient block:
<cfclient> <cfinclude template="index_include.cfm" > </cfclient>
Note that this is a client-side include and when the application is compiled, the included file is translated to JavaScript file.
The index_include.cfm file starts with the cfclient block. It creates an instance of the ExpenseManager CFC and calls the getExpenses function on it. This function returns an array of ExpenseVO components.
<cftry> <cfset expMgr = new cfc.ExpenseManager()> <cfset expenses = expMgr.getExpenses()> <cfcatch type="any" name="e"> <cfset alert(e.message)> </cfcatch> </cftry>
The index_include.cfm file then calls the cliient side custom tag (expenseList.cfm) to display a list of expenses.
<cf_expenseList parentDiv="expenseListDiv" expenses=#expenses# action="displayAll">
It passes id of the div in which to display expenses, array of expenses (ExpenseVO) and action type. It also contains UDFs for saving expenses (saveExpense), deleteing all expenses (deleteAll), ataching receipt with an expense item (attachReceipt) and a few helper functions. saveExpense function gets values from input fields (created in index.cfm) and calls addExpense function in ExpenseManager.cfc to actually insert row in the table.
The addReceipt function launches the camera application of the device and gets the image path after the picture is captured.
function attachReceipt() { if (!createApplicationFolder()) return; var imageUrl = cfclient.camera.getPicture(); if (isDefined("imageUrl")) { var imagePath = copyFileFromTempToPersistentFileSystem(imageUrl); if (isDefined("imagePath") && imagePath != "") { _tmpImagePath = imagePath; console.log("image saved to " + imagePath); loadImage(imagePath,"receiptImg"); } } }
The function first creates a folder named ‘CFMobileExpenseTracker’ in the persistent file system by calling the UDF, createApplicationFolder.
The cfclient.camera.getPicture() function launches the mobile camera application and returns the path of the image file. The image is stored in the temporary file system and could be deleted when the application cache is cleared or when the application is restarted. So, we need to move the image file to the persistent file system. We do that in the copyFileFromTempToPersistentFileSystem function. This function uses the file APIs to copy the image to the persistent location and remove the image from the temporary location. It returns the new path of the image file. This is the path that we store in the database.
For more details, see index_include.cfm file. See copyFileFromTempToPersistentFileSystem function in index_include.cfm for complete code.
Functions saveExpense, deleteAll, and attachReceipt are called in repsonse to the JavaScript events (button clicks). The JavaScript event handlers are declared in app.js in js folder and is included in index.cfm. You will observe in app.js that you can call methods declared in cfclient from JavaScript:
$("#dlgOKBtn1").on("vclick", function(){ saveExpense(); }); You can also call JavaScript functions from cflcient code, for example index_include.cfm has following code to get values from text fields – <cfscript> var dateStr = trim($("##dateTxt").val()); var amtStr = trim($("##amtTxt").val()); ... </cfscript>
Client-side custom tag, expenseList.cfm, contains all its code in the cfclient block. It is recommended that all code in the client side included file and custom tag be enclosed in cfclient block.
This custom tag supports three actions – displayAll, append, and removeAll. When displayAll action is called, this tag replaces all the content of the parent div with the table contianing the expense items. When the append action is called, it appends a single expense item to the existing expense table – this action is called when the user adds a new expense item. The removeAll action replaces the content of the parent div with the message ‘No expenses found’.
As you can see from the above description, we have separated HTML, cfclient, and data access code in ths application. Most of the HTML static code is in index.cfm. cfclient code to interface between HTML UI and data access code is in the included file (index_include.cfm). Custom tag (expenseList.cfm) creates dymanic HTML content using JQuery. And all data access code is in ExpenseManager.cfc.
Testing application in Shell App
Before you package the application, you may want to see how it looks on a mobile device and make necessary changes. You will not be able to run this application in standard desktop or mobile browser, because the application now uses device APIs that are not supported in the standard web browsers. As mentioned in the beginning of this article, you can use the Shell application that we have devloped to quickly test your applications before packaging them.
We have created the Android APK file for the Shell application avilable here, which you can directly install on any Android device. Copy the APK file on to device storage and open it to start the installation process.
Installing the shell app on iOS is not as easy. You will have to sign the iOS Shell application with your own certificate (developer or distribution certificate obtained from Apple). I have made Xcode project avilable for iOS shell application which you can open in Xcode and install the application in Simulator or on a physical device. Description of these steps is outside the scope of this article.
To test the application using Shell application on a device, it should be on the same network as the CF server. In other words, CF server should be accessible from the device.
Following are screen shots of Shell appliction on Android and iOS:
Type URL of the application (e.g. http://<ip_of_cf_server>:8600/CFMobileExpenseTracker
) in the text box and click Go button. This will launch the application. The application will have access to all device APIs in the Shell app. If you make any changes to the application, simply refresh it (in Android Shell applicatoin, refresh option is avilable in the application menu).
When testing application in the Shell app, it is important that the application includes
<cfinclude template="/CFIDE/cfclient/useragent.cfm" >
This is a server side include. Application.cfm file in CFMobileExpenseTracker project includes this file. Application.cfm file should not be selected when packaging the application. We will see this in a next section.
Configuring PhoneGap Build server settings
Before we package the application, we need to configure a few parameters in ColdFusion Builder related to PhoneGap Build server. If you do not have an account with PhoneGap Build, you need to create one now. Go to build.phonegap.com to create an account.
Open menu Window->Preference in ColdFusion Builder and go to ColdFusion->PhoneGap node in the left pane.
Enter the email id and password that you regestered with the PhoneGap Build. If you want to generate the iOS build, enter the iOS key details. Visit Apple Developer Support Center for information about code signing certificates.
Android key details are required only if you are planning to distirbute your application on Google Play Store. Otherwise, you can leave these fields blank. The PhoneGap Build server will generate the build even without the Android key details.
Click OK to save and close preferences.
Selecting files to package
You need to select the files in the project that you want to include in the mobile application. Right click the project in the Navigator view and select Properties. Select ‘ColdFusion Mobile Project’ from the left pane and make sure ‘Resource Selection’ tab is open. Select only epxenseList.cfm, index.cfm and index_include.cfm from the project folder. Select all the files from other folders.
Selecting Device APIs for packaged application
You need to notify the PhoneGap build server which device APIs to enable in the application. To do that, open ‘ColdFusion Mobile Project’ properties again (as desribed above) and select the PhoneGap tab. Click the New button and select ‘Feature’ in the left pane. This application use Camera and File APIs. So, select those two check boxes.
Click OK. You should see values added for Camera and File under the ‘feature’ node. Click OK to save changes and close project properties.
Packaging the application
Right click the project name in the Navigator view and select the ‘Generate PhoneGap Build’ option from the pop-up menu. ColdFusion Builder will start the process of packaging the app and will add a new application entry in the PhoneGap Status view, if it is not already there. Along with the ColdFusion Server, CFB compiles the application, creates a bundle to upload to PhoneGap Build Server, uploads the bundle and waits for the response from the PhoneGap Build server. When the build is ready, you will see the ‘Download’ buttons for the application. Click on the appropriate Dowload button to dowload the IPA (for iOS) or APK (for Android) file.
You can install the APK file on a device by copying it to the device storage and opening the file. Use iTunes to install the IPA file on an iOS device.
Where to go from here
ColdFusion 11 and ColdFusion Builder 3 provides a complete workflow for mobile application development – code, test, debug, and package. In this article, we saw how to build and package a mobile application. We have left out some of the features in the application, which you can easily add, for example, deleting individual expense items or modifying expense items.
The ColdFusion team blog contains many articles on how to use device APIs with fully functional application code. Also refer to ColdFusion 11 and ColdFusion Builder 3 documentation which explains mobile development work flow and has the API reference section.