Quantcast
Channel: ColdFusion
Viewing all articles
Browse latest Browse all 1091

CFMobile Example – Accessing remote data from mobile application

$
0
0

This post is reproduced from my post at my personal blog ramkulkarni.com.

So far I have posted CFMobile examples that were mostly standalone applications (except a photo application that uploaded image to server). However many mobile applications may need to interact with server, for example to show data from a remote database, to modify data or for many other purposes.

CFMobile features in ColdFusion Splendor make accessing remote CF server very easy. I will demonstrate this using a simple example - I will build a mobile app that displays employee records fetched from a remote CF server. The client side (cfclient) code calls a CFC on the server side which fetches data and returns result to the calling page. You will see that creating and accessing a server side CFC is as easy as it is in a completely server side CFML code - you don't need to worry about writing code to make AJAX calls. cfclient does that for you transparently. I should mention here that this feature to call server CFCs from cfclient is not limited to mobile application, you can even use it for any web application.

Here is a screenshot of the application

 

I will start with building the server side application. We need to create a database and a table on the server. I am going to use Embedded Apache Derby database for this application. Using ColdFusion Administrator, you can create both database and datasource for Embedded Apache Derby. Open up the administrator and go to 'Data & Services'->Data Sources page and add a new data source, called EmployeeDS -

Click Add button, and you will see details page -

Specify database folder and select 'Create Database' option. I have created the database in cfusion/db/EmployeeDS folder. Derby database driver creates the last folder, so make sure EmployeeDS folder does not already exist in cfusion/db, otherwise Derby throws error.

We now need to create employee table. I have created a simple cfml file, db_scripts.cfm with functions to create database, list and delete records.

<!--- Functions to create Employee databsase, display employees 	and delete all employees from the table ---><!--- Comment call to function createTable after table is successfully created ---><cfsetcreateTable()><!---<cfset deleteAll()>---><!---<cfset listEmployees()>---><br>Script Executed.

<cffunctionname="createTable"><cfquerydatasource="employeeDS">
		create table employee (
			id integer not null generated always as identity,
			first_name varchar(20),
			last_name varchar(20),
			address varchar(100))
	</cfquery></cffunction><cffunctionname="deleteAll"><cfquerydatasource="EmployeeDS">
		delete from EMPLOYEE
	</cfquery></cffunction><cffunctionname="listEmployees"><cfquerydatasource="employeeDS"name="rs">
		select * from EMPLOYEE order by FIRST_NAME
	</cfquery><table><tr><th>First Name</th><th>Last Name</th><th>Address</th></tr><cfoutput query="rs"><tr><td>#first_name#</td><td>#last_name#</td><td>#address#</td></tr></cfoutput></table></cffunction>

Run this page and it will create employee table with fields id, first_name, last_name and address.

Next I will create a server side CFC, EmployeeDBManager.cfc with two functions - getEmployees and addEmployee -

<cfcomponent><cfsetthis.ds="employeeDS"><cffunctionname="getEmployees"returntype="Array"access="remote"returnformat="JSON"hint="Fetches employee rows from Employee table and returns array of EmployeeTO components"><cfsetresult=ArrayNew(1)><cfquerydatasource="#this.ds#"name="empRs">
			select * from EMPLOYEE order by FIRST_NAME
		</cfquery><cfloopquery="empRs"><cfsetvarempStruct= {"id":id, "firstName":first_name, 
									"lastName": last_name, "address":address}><cfsetarrayAppend(result,empStruct)></cfloop><cfreturnresult></cffunction><cffunctionname="addEmployee"access="remote"returntype="numeric"hint="Inserts a new employee record in the able"><cfargumentname="emp"type="struct"><cfquerydatasource="#this.ds#"result="newEmpResult">
			insert into EMPLOYEE (FIRST_NAME,LAST_NAME,ADDRESS) values (
				<cfqueryparamcfsqltype="cf_sql_varchar"value="#emp.firstName#">,
				<cfqueryparamcfsqltype="cf_sql_varchar"value="#emp.lastName#">,
				<cfqueryparamcfsqltype="cf_sql_varchar"value="#emp.address#">
			)
		</cfquery><cfreturnnewEmpResult.generatedKey></cffunction></cfcomponent>

Note that both the functions are marked remote (because they will be called from a cfclient block).
getEmployee returns Array in JSON format. It returns array of Struct with fields id, fistName, lastName and address.
addEmployee takes Struct argument with the same fields as above, except id. It returns id of the newly added employee.
We are done with the server side code for this application.

Let's now create the client side application. In index.cfm of the client app, I first create HTML UI and write some JavaScript event handlers.

<DOCTYPE html><script src="jquery-2.0.3.min.js"></script><script >
	$(document).ready(function(){

		$(document).on("click","#addBtn", function(){
			var firstName = $("#fnTxt").val();
			var lastName = $("#lnTxt").val();
			var city = $("#cityTxt").val();

			if (firstName.trim().length ==0)
			{
				alert("First name is required");
				return;
			}

			addEmployee(firstName,lastName,city);
		});
	});
</script><style >th,td {
		text-align:left;
	}	
</style><h2>CFMobile Demo:</h2>
This application calls a CFC on the server side to get all employee records from a database table on the server.<br>
You can add an employee by filling up following details and clicking Submit button.
This again makes call to a server CFC to add employee to the table.

<h3>Add Employee:</h3><form><table><tr><td>First Name:</td><td><inputtype="text"id="fnTxt"></tr><tr><td>Last Name:</td><td><inputtype="text"id="lnTxt"></tr><tr><td>City:</td><td><inputtype="text"id="cityTxt"></tr><tr><tdcolspan="2"><buttontype="button"id="addBtn">Add</button><buttontype="reset">Reset</button></td></tr></table></form><hr><h3>Employees:</h3><tableid="empTable"width="100%"><tr><th>First Name</th><th>Last Name</th><th>City</th></tr></table><cfclient><cfscript></cfscript></cfclient>

In the cfscript block in cfclient, I first instantiate the server side CFC, EmployeeDBManager, and then call getAllEmployees function.

try
{
	empMgr =new EmpServerApp.EmployeeDBManager();
}
catch (any e)
{
	alert("Error : "+ e.message);
	cfabort ();
}

//get all employees from the server and display in the above HTML table
getAllEmployees();

empMgr is a global variable. You can create page level variable also using variables prefix, i.e variables.empMgr.
Notice that you instantiate server CFC from cfclient block just as you would do it in server side code.

In getAllEmployees, I call getEmployees method of the server CFC. But before calling the server CFC, I set callback handler (function) which will be called when server function returns.

//Fetch employees data from server and display in the HTML tablefunction getAllEmployees()
{
	try
	{
		//Set callback function on the server CFC, which will be//called with the result
		empMgr.setCallbackHandler(function(callbackResult){
			var employees = callbackResult.result;
			var empCount = arrayLen(employees);
			for (var i =1; i <= empCount; i++)
			{
				addEmpToHTMLTable(employees[i]);	
			}
		});

		empMgr.getEmployees();
	}
	catch (any e)
	{
		alert("Error : "+ e.message);
		return;
	}

}

If you do not set the callback handler, remote CFC function would be called synchronously, which will block application UI till the CFC function returns. So make sure you set callback handler when invoking remote CFC function from cfclient.

Argument to the callbakc handler is an object with result field. This field actually contains result returned by the remote CFC function. getEmployee function of the CFC returns array (of struct/object), so I iterate over it and call addEmpToHTMLTable function for each employee struct.

<!--- Append employee data in the HTML table ---><cffunctionname="addEmpToHTMLTable"><cfargumentname="emp"><cfoutput ><cfsavecontentvariable="rowHtml"><tr><td>#emp.firstName#</td><td>#emp.lastName#</td><td>#emp.address#</td></tr></cfsavecontent></cfoutput><cfset$("##empTable").append(rowHtml)></cffunction>

There is one more function in the cfscript block of cfclient, to add new employee

//Add a new employee.function addEmployee(firstName, lastName, city)
{
	try
	{
		var newEmp = {"firstName":firstName, "lastName":lastName, "address":city};

		//Set callback function on the server CFC, which will be//called with the result
		empMgr.setCallbackHandler(function(callbackResult) {
			newEmp.id = callbackResult.result;
			addEmpToHTMLTable(newEmp);
		});

		empMgr.addEmployee(newEmp);
	}
	catch (any e)
	{
		alert("Error:"+ e.message);
		return;
	} 
}

addEmployee (in cfclient) takes three arguments -  firstName, lastName and city (address). I create a new struct using these arguments and pass it to addEmployee method of the remote CFC (EmployeeDBManager). Here also I set the callback handler because I don't want call to a server function blocking the application.
addEmployee function on the server side returns id of the new employee. I get this as result field of the argument to callback handler. Then I call addEmpToHTMLTable (see above) to append new employee to the HTML table.

This completes code for the application. You can open index.cfm of the client application in Chrome/Safari and see how the application works. You can also test it on mobile as a standalone application using PhoneGap Shell app that we have built (see links in my earlier post - Simplify Mobile Application Development Using ColdFusion).

However if you package the client application and install it on mobile device, you will find that application does not work - no data is fetched from the server.
When resolving URL of remote CFC in cfclient, the framework tries to resolve it from the URL of the client page. If client page and server CFC are loaded from the same server, which is what happens when you run the application in desktop browser or in the Shell application, cfclient is able to resolve the server CFC. But it cannot resolve it in a packaged application, because the client page is loaded locally from the device and server side CFC does not exist on the device.

To help resolve server CFCs (used in cfclient), you need to provide base URL of the server when packaging the application. You do that by going to project properties (right click the project in the Navigator view of ColdFusion Thunder and select Properties menu option)->ColdFusion Mobile Project-> Miscellaneous page and specifying 'Application Base URL'

When you right click on the project and select 'Generate PhoneGap Build' menu option, the above URL will be used in the generated code to resolve server side CFCs.

Download CFBuilder projects for this application. EmpMobileApp folder in this zip contains client side application and EmpServerApp folder contains server side app. I am not providing Android APK file for this app because my server side CFC will not be accessible from this APK when you install it on you device.

Though I showed you how to access remote database from CFMobile application in this post, the technique is not limited to database access. Server CFC can get data from any source and return to cfclient.

-Ram Kulkarni


Viewing all articles
Browse latest Browse all 1091

Trending Articles