Mar
31

Calling a CFC from a Flash Form without refreshing Part I

17 comments Posted by: Laura

Everybody 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 yours
Paul
1. Paul wrote on June 07, 2005 at 1:57 PM
I'm trying to use a slightly modified version of this code to perform some data validation. Since LoadVariables doesn't wait for a response, I wasn't successful trying to use it in onSubmit=&quot;#validateMyForm#&quot;.

Instead, I'm very close to having it work through a button onclick using SubmitForm(). Here is my code:

&lt;cfsavecontent variable=&quot;validateMyForm&quot;&gt;
   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 = &quot;validateBuyerRegistration&quot;; //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 &quot;' + dataholder.LoginID + '&quot; 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);
&lt;/cfsavecontent&gt;

Every line of code above works as desired except for the final SubmitForm() when I get successful validation. Any ideas?
Nahuel
Try:
_root.submitForm();
Jason
3. Jason wrote on September 25, 2005 at 2:36 PM
var dataholder = this.createEmptyMovieClip('dataholder',4587);

just curious, what the above line for?
I'm new to AS, thanks!
Nahuel
Hi Jason,
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.
Jeremy
5. Jeremy wrote on February 11, 2006 at 6:45 PM
I have modified this code to work with a db that I am working on. I am trying to return to the interface two vars. One called fileNum and one called propStreet. It will return the first var, but not the second. And idea why it would only return one?

Here is my code.

CFC:
&lt;cfcomponent&gt;
   &lt;cffunction name=&quot;getInfo&quot; access=&quot;remote&quot; returntype=&quot;void&quot;&gt;
      &lt;cfargument name=&quot;search&quot; required=&quot;no&quot; type=&quot;string&quot; /&gt;
      &lt;cfquery name=&quot;mySearch&quot; datasource=&quot;#application.dsn#&quot;&gt;
      select * from tbl_smartPanel_propInfo_List
      where fld_fileNum = '#arguments.search#'
      &lt;/cfquery&gt;      
      &lt;cfoutput&gt;&amp;eof=true&amp;propStreet=#mySearch.fld_propStreet#&amp;fileNum=#mySearch.fld_fileNum#&amp;loaded=true&lt;/cfoutput&gt;      
   &lt;/cffunction&gt;
&lt;/cfcomponent&gt;


Index.cfm:

&lt;cfsavecontent variable=&quot;callingCFC&quot;&gt;
   var dataholder = this.createEmptyMovieClip('dataholder',4587);
   dataholder.method = &quot;getInfo&quot;;//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);
&lt;/cfsavecontent&gt;

&lt;cfform name=&quot;myform&quot; height=&quot;200&quot; width=&quot;400&quot; format=&quot;Flash&quot; timeout=&quot;0&quot; &gt;
   &lt;cfformitem name=&quot;mesg&quot; type=&quot;html&quot;&gt;&lt;p&gt;Enter a fileNum &lt;/p&gt;&lt;p&gt;Example L061234&lt;/p&gt;&lt;/cfformitem&gt;
   &lt;cfformgroup type=&quot;hbox&quot;&gt;
       &lt;cfformgroup type=&quot;vbox&quot; id=&quot;displayHolder&quot;&gt;
         &lt;cftextarea type=&quot;text&quot; name=&quot;textEntered&quot; height=&quot;50&quot; label=&quot;Search&quot;&gt;Enter Text&lt;/cftextarea&gt;
         &lt;cfinput type=&quot;text&quot; name=&quot;display&quot; disabled=&quot;true&quot; style=&quot;borderStyle:none; fontWeight:bold; disabledColor:##000000&quot; label=&quot;Result&quot; /&gt;
         &lt;cfinput type=&quot;text&quot; name=&quot;display2&quot; disabled=&quot;true&quot; style=&quot;borderStyle:none; fontWeight:bold; disabledColor:##000000&quot; label=&quot;Result2&quot; /&gt;
      &lt;/cfformgroup&gt;
       &lt;cfformgroup type=&quot;vbox&quot; width=&quot;120&quot;&gt;
          &lt;cfinput type=&quot;Button&quot; name=&quot;setFunction&quot; width=&quot;104&quot; value = &quot;Get Data&quot; onclick=&quot;#callingCFC#&quot; style=&quot;cornerRadius:0; borderThickness:1;&quot;&gt;          
      &lt;/cfformgroup&gt;
&lt;/cfformgroup&gt;
&lt;/cfform&gt;
Pedro
I can't get this to work, I keep getting this error: "Illegal use of actionscript ...". Any ideas?
Laura
Pedro,
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
Michael White
I am using the onRequestStart methods in my application.cfc so I don't think remoting will work. I only need to return a numeric integer (record count of a query) from my CFC. my form doesn't seem to do anything when I click the button with onclick attribute. I get no errors and no alert box.

<!--- 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>
Michael White
I put in a regular cf call to the same method to test that it returned the right value and it does (after I put single quotes on the '#Arguments.UserLogin#' in the cfc query). I also added a field to see what LoginBadID was returned and it said "undefined". I re-read the article and it says something about outputting variables in loadvars format... whatever that means.
michael white
here is the streamlined version, same result, undefined back to the calling actionscript...
<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.
Laura
Michael,
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
michael white
I'm trying to do the same thing as Paul from comment #1 (in fact he gave me the CFC function) but I'm assuming from what you are saying that he must be using flash remoting in order for that component to work. Can I do this with remoting when I also have onRequestStart in the application.cfc? I am using Coldfusion 7.02. I also have the Full Flex 2.0. I'm open to rethinking this but the client wants a pop-up (alert) when they try to enter duplicate login names. and the whole application is Flash-forms
Paul
13. Paul wrote on September 10, 2006 at 11:37 AM
Yes, that is correct. I am using Flash Remoting of which there are plenty of examples available on how to it from Flash CFFORMs.

Paul
Laura
Michael,
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
michael white
I think I got this working using flash remoting (my first foray into remoting) with your help and Paul's. I think the problem was that I had virtual websites because when i removed all connectors (using the remove_all_connectors.bat) and then reconnected (using IIS_connector.bat) and then played around with the two lines with paths in them it all started working. Now to integrate the code into live pages!
philip g
Hi, major noobie here; incredible site. 20 minutes and time mask example got me going rapidly to exactly what I needed. Thank you very very much.
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.
philip g
Hi, major noobie here; incredible site. 20 minutes and time mask example got me going rapidly to exactly what I needed. Thank you very very much.
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.

Leave your comment

Comment etiquette: As a gesture to those subscribed to this post, please keep your comments relevant to the post.

Your email address will never be displayed.
Email is gravatar enabled.Gravatar are the pictures you see next to the comments. If you like to have one, visit gravatar



Allowed tags:

<code>
All other tags will be shown as such, when in doubt, use the preview.




Preview:

Refresh Preview
1. You wrote on