Calling a CFC from a Flash Form without refreshing Part I
17 comments Posted by: LauraEverybody knows that it’s not possible to use Flash Remoting to call a cfc from a flash form. Furthermore, one of the limitations that the flash forms have is that they do not allow writing the keyword "new" (except in cfcalendar :) ), so we cannot use it to create a new remoting connection.
But we can use a different approach. Instead of using Remoting, we can use the loadVariables function. Although this method is not as powerful as Flash Remoting, it works great for small calls. Note is that this is not a solution for sending recordsets back and forward to the server. For complex and robust applications, I recommend taking a look at Flex (that allows to do this and much more). With loadVariables we can send and receive variables but not structures, queries, Arrays, or Objects (at least not without a parser).
The example that follows will make a simple call to a cfc and receive a variable back. It calls a cfc with one parameter (mask) that tells the CFC the mask to format the current date and shows the results received from the component.
<cfsavecontent variable="callingCFC">
var dataholder = this.createEmptyMovieClip('dataholder',4587);
dataholder.method = "getDate";//the cfc method
dataholder.mask = textEntered.text; //the parameter mask the component expects
dataholder.loadVariables ('dateResponder.cfc', 'POST');//note the component name with full path
var display = display;
var obj = {};
//we must check whether the data has already loaded
var checkData = function(obj)
{
if(dataholder.time != undefined)
{
clearInterval(obj.id);
display.text = dataholder.time;
dataholder.time = undefined;
}
}
obj.id = setInterval(checkData, 100, obj);
</cfsavecontent>
<cfform name="myform" height="200" width="400" format="Flash" timeout="0" >
<cfformgroup type="hbox">
<cfformgroup type="vbox" id="displayHolder">
<cftextarea type="text" name="textEntered" height="50" label="Mask">mmmm dd of yyyy
hh:mm:ss</cftextarea>
<cfinput type="text" name="display" disabled="true" style="borderStyle:none; fontWeight:bold; disabledColor:##000000" label="Result" />
</cfformgroup>
<cfformgroup type="vbox" width="120">
<cfinput type="Button" name="setFunction" width="100" value = "Get Date" onclick="#callingCFC#">
</cfformgroup>
</cfformgroup>
</cfform>
The component should output any variable in the loadvars compatible format. It should also have access="remote" and return type void.
<cfcomponent name="dateResponder" access="public" description="Responds to Flash load vars requests">
<cffunction name="getDate" output="true" description="Returns the current time in a flash load vars format" access="remote" returntype="void">
<cfargument name="mask" required="false" type="string" default=""/>
<!--- get the current time --->
<cfset var time = now() />
<cfif len(arguments.mask)>
<!--- format the date according to mask --->
<cfset time = dateformat(time,arguments.mask) />
</cfif>
<!--- make a string that flash can read --->
<cfoutput>&time=#time#&eof=true</cfoutput>
</cffunction>
</cfcomponent>
A live example
Download the source
Update: We found that it is possible to use Flash Remoting, so look at this post to see how to use it.
Category: CFForm | ColdFusion |
17 Comments so far
Write yoursInstead, I'm very close to having it work through a button onclick using SubmitForm(). Here is my code:
<cfsavecontent variable="validateMyForm">
if ( (password.text == '') || (reenterpassword.text =='') || (LoginID.text =='') || (AgentCode.text =='') ) {
submitForm();
return;
}
if ( password.text != reenterpassword.text ) {
reenterpassword.setFocus();
alert('Passwords do not match!');
return;
}
var dataholder = this.createEmptyMovieClip('dataholder',4587);
dataholder.method = "validateBuyerRegistration"; //the cfc method
dataholder.LoginID = LoginID.text;
dataholder.AgentCode = AgentCode.text;
dataholder.loadVariables('/besthomepro/controller.cfc', 'POST');
var obj = {};
var checkData = function(obj) {
if(dataholder.LoginIDGood != undefined) {
clearInterval(obj.id);
if (dataholder.LoginIDGood == 0) {
LoginID.setFocus();
alert('LoginID "' + dataholder.LoginID + '" is already taken! Please select an alternate LoginID.');
dataholder.LoginIDGood = undefined;
dataholder.AgentCodeGood = undefined;
return;
} else {
if (dataholder.AgentCodeGood != 1) {
AgentCode.setFocus();
alert('Unidentified Agent Code: ' + dataholder.AgentCode + '\n\nPlease check the code provided by your agent.');
dataholder.LoginIDGood = undefined;
dataholder.AgentCodeGood = undefined;
return;
} else {
dataholder.LoginIDGood = undefined;
dataholder.AgentCodeGood = undefined;
submitForm(); //Doesn't submit!!! Aargh
return false;
}
}
}
}
obj.id = setInterval(checkData, 100, obj);
</cfsavecontent>
Every line of code above works as desired except for the final SubmitForm() when I get successful validation. Any ideas?
_root.submitForm();
just curious, what the above line for?
I'm new to AS, thanks!
We create a Movie Clip to store some variables and send them to the server, you can store in the _root, but you will send all the data to the server no just a few variables. The number 4587 is just the level where it was created, could be any number.
Here is my code.
CFC:
<cfcomponent>
<cffunction name="getInfo" access="remote" returntype="void">
<cfargument name="search" required="no" type="string" />
<cfquery name="mySearch" datasource="#application.dsn#">
select * from tbl_smartPanel_propInfo_List
where fld_fileNum = '#arguments.search#'
</cfquery>
<cfoutput>&eof=true&propStreet=#mySearch.fld_propStreet#&fileNum=#mySearch.fld_fileNum#&loaded=true</cfoutput>
</cffunction>
</cfcomponent>
Index.cfm:
<cfsavecontent variable="callingCFC">
var dataholder = this.createEmptyMovieClip('dataholder',4587);
dataholder.method = "getInfo";//the cfc method
dataholder.search = textEntered.text; //the parameter mask the component expects
dataholder.loadVariables ('cfc/myCFC.cfc', 'POST');//note the component name with full path
var display = display;
var obj = {};
var checkData = function(obj)
{
if(dataholder.fileNum != undefined)
{
clearInterval(obj.id);
display.text = dataholder.fileNum;
display2.text = dataholder.propStreet;
}
}
obj.id = setInterval(checkData, 100, obj);
</cfsavecontent>
<cfform name="myform" height="200" width="400" format="Flash" timeout="0" >
<cfformitem name="mesg" type="html"><p>Enter a fileNum </p><p>Example L061234</p></cfformitem>
<cfformgroup type="hbox">
<cfformgroup type="vbox" id="displayHolder">
<cftextarea type="text" name="textEntered" height="50" label="Search">Enter Text</cftextarea>
<cfinput type="text" name="display" disabled="true" style="borderStyle:none; fontWeight:bold; disabledColor:##000000" label="Result" />
<cfinput type="text" name="display2" disabled="true" style="borderStyle:none; fontWeight:bold; disabledColor:##000000" label="Result2" />
</cfformgroup>
<cfformgroup type="vbox" width="120">
<cfinput type="Button" name="setFunction" width="104" value = "Get Data" onclick="#callingCFC#" style="cornerRadius:0; borderThickness:1;">
</cfformgroup>
</cfformgroup>
</cfform>
Did you change anything?
By the way, it is better to use Flash Remoting instead of this. See this post: http://www.asfusion.com/blog/entry/remoting-for-coldfusion-flash-forms
<!--- here is the actionscript in the form --->
<cfsavecontent variable="validateLogin">
var dataholder = this.createEmptyMovieClip('dataholder',4000);
dataholder.method = "checkUserLogin";
dataholder.UserLogin = UserLogin.text;
dataholder.loadVariables('lookup.cfc', 'POST');
var obj = {};
var checkData = function(obj) {
if(dataholder.LoginIDBad != undefined) {
clearInterval(obj.id);
if (dataholder.LoginIDBad > 0) {
UserLogin.setFocus();
alert('UserLogin ' + dataholder.UserLogin + ' is already taken! Please select an alternate UserLogin.');
dataholder.LoginIDBad = undefined;
return;
} else {
dataholder.LoginIDBad = undefined;
_root.submitForm();
return;
}
}
}
obj.id = setInterval(checkData, 100, obj);
</cfsavecontent>
<!--- Here is the cfc method --->
<cffunction
access="remote"
hint="returns a number of User Login matches"
name="checkUserLogin"
output="false"
returntype = "numeric">
<cfargument
name="UserLogin"
type="string"
required="true">
<cfset var LoginIDBad = "">
<cfquery name="qryUserLogins" datasource="#Application.Datasource#">
SELECT
Contacts.UserLogin
FROM
Contacts
WHERE
Contacts.UserLogin = #Arguments.UserLogin#
</cfquery>
<cfset LoginIDBad = qryUserLogins.RecordCount>
<cfreturn LoginIDBad>
</cffunction>
<!--- Here is the form --->
<cfform
method="post"
name="frmContactAssoc"
action="/Admin/ContactProcess.cfm"
preloader="no"
format="flash"
timeout="300"
height="650"
width="920">
<!--- edit form --->
<cfformgroup
type="panel"
label="CONTACT FORM"
height="400"
width="540"
style="panelBorderStyle:'roundCorners'; marginLeft:10; marginRight:10; marginTop:12; color:##F5F5F5; themeColor:##A6BBCA; headerColors:##538AB0,##91CAF2; backgroundColor:##103B66; fontFamily:'Trebuchet MS'; fontSize:16;">
<!--- Start of Tab Navigator --->
<cfformgroup
type="tabnavigator"
height="300"
style="themeColor:##467599;color:##103B66; fontSize:14; horizontalAlign:left; backgroundColor:##F5F5F5;selectedFillColors:##467588,##F5F5F5; headerColors:##F0F8FF, ##538AB0;">
<!--- Start of Admin Page --->
<cfformgroup type="page" label="Admin">
<cfinput
id="UserLogin"
label="Login"
name="UserLogin"
required="true"
style="cornerRadius:5; borderStyle:solid; dropShadow:yes; shadowDirection:right; shadowDistance:3;"
type="text"
width="250" />
<cfinput
id="UserPassword"
label="Password"
name="UserPassword"
required="true"
style="cornerRadius:5; borderStyle:solid; dropShadow:yes; shadowDirection:right; shadowDistance:3;"
type="password"
width="250" />
<!--- End of Admin Page --->
</cfformgroup>
<!---End of Tab Navigator --->
</cfformgroup>
<cfformgroup type="horizontal">
<cfinput
id="addContact"
name="addContact"
type="button"
onClick="resetForm();"
value="Clear Form"
style="color:##E1F5FA; fillColors:##334C66,##4C7399; textRollOverColor:##FFFFFF; themeColor:##667B8A;" />
<cfinput
id="submit"
name="submit"
type="button"
onclick="#validateLogin#"
value="Just a Button"
style="color:##E1F5FA; fillColors:##334C66,##4C7399; textRollOverColor:##FFFFFF; themeColor:##667B8A;" />
</cfformgroup>
</cfformgroup>
</cfform>
<cfsavecontent variable="validateLogin">
var dataholder = this.createEmptyMovieClip('dataholder',4587);
dataholder.method = "checkUserLogin";
dataholder.UserLogin = UserLogin.text;
dataholder.loadVariables('/Affinity/components/lookup.cfc', 'POST');
retResult.text = dataholder.LoginIDGood;
if(dataholder.LoginIDGood != undefined) {
if (dataholder.LoginIDGood == 0) {
UserLogin.setFocus();
alert('UserLogin ' + dataholder.UserLogin + ' is already taken! Please select an alternate UserLogin.');
dataholder.LoginIDGood = undefined;
return;
} else {
dataholder.LoginIDGood = undefined;
_root.submitForm();
return;
}
}
</cfsavecontent>
<cfform
name="frmContactAssoc"
action="/testing/formdump.cfm"
format="flash"
timeout="0"
height="500"
width="600">
<!--- edit form --->
<cfformgroup
type="panel"
label="CONTACT FORM"
height="300"
width="540"
style="themeColor:haloSilver;marginTop:50;marginLeft:40;">
<cfinput
id="UserLogin"
label="User Login Name"
name="UserLogin"
required="true"
type="text"
value="mwhite"
width="250" />
<cfinput
id="UserPassword"
label="Password"
name="UserPassword"
required="true"
type="password"
width="250" />
<cfinput
id="retResult"
label="result"
name="retResult"
required="false"
type="text"
width="250" />
<cfinput
id="checkLogin"
name="checkLogin"
type="button"
onclick="#validateLogin#"
value="Check Login" />
</cfformgroup>
</cfform>
<!--- and here is the function in the lookup.cfc --->
<cffunction
access="remote"
hint="returns a boolean whether User Login already in use"
name="checkUserLogin"
output="false"
returntype = "boolean">
<cfargument
name="UserLogin"
default=""
type="string"
required="true">
<cfset var LoginIDGood = false>
<cfquery name="qryUserLogins" datasource="#Application.Datasource#">
SELECT
UserLogin
FROM
Contacts
WHERE
UserLogin = <cfqueryparam cfsqltype="cf_sql_varchar" value="#Arguments.UserLogin#">
</cfquery>
<cfif qryUserLogins.recordcount EQ 0><cfset LoginIDGood = true></cfif>
<cfreturn LoginIDGood>
</cffunction>
it's hard to imagine anything simpler but it's not working for me, no matter the variations I've tried.
To be honest, I have not completely looked at your code, but... you are returning from your components as if it was flash remoting. This is different... the component *must* output the result and therefore must have output="true" (and probably returntype="void" also). Also, see what I do at the end of the cffunction to "return" the values in loadvars format:
<cfoutput>&time=#time#&eof=true</cfoutput>You will still need to make the component as described here, but this custom tag should make your life simpler: http://www.asfusion.com/blog/entry/calling-a-cfc-from-flash-form-part2
Paul
The only changes you need to make are those I described in my previous response (make output="true" and output the response within the cffunction instead of using cfreturn).
In any case, there is no problem using onRequestStart and Flash remoting. I use it in many of the apps you can find in this blog. The problem with Remoting comes with onRequest as described in http://livedocs.macromedia.com/coldfusion/7/htmldocs/00000698.htm
Question: The cfc (identical in principle to time mask example dateresponder.cfc; queries UPS) does not return a result when called from https://www.mysite.com/verifyAddress.cfm? ...
Any ideas? Thanks in advance.
Question: The cfc (identical in principle to time mask example dateresponder.cfc; queries UPS) does not return a result when called from https://www.mysite.com/verifyAddress.cfm? ...
Any ideas? Thanks in advance.