Login System For A Flex Application With ColdFusion Backend Part 3 - Custom Login Form Component

Introduction To Part 3 - Custom Login Form Component

When I finished part 2 of this project, I had successfully connected my Flex application to the ColdFusion backend. Now I could finally get to work on designing the actual login form of my Flex application. My initial design question was how should I build the login form? I could include all the mxml and action script in the same application that will be used for the newsletter or I could design a custom login form component. I decided to design a custom login form component since I could then reuse the login form in other applications. Also, while I'm designing the custom login form component, other developers could be working on the main newsletter application.

References

  1. Adobe Flex Documentation, Creating and Extending Flex 2 Components
  2. Bruce Phillips Blog, For Flex 2.0 Beginners - Use the mx:States and mx:ViewStack Tags To Change The Application's View

If you're not familiar with how to create simple custom components in Flex or how to use the mx:ViewStack tag then consult the above references. You may also want to review parts 1 and 2 of this project (see related entries below).

Initial Design

To keep this initial design simple and to get a working prototype, I recalled my first use case stated in part 1:

A. User visits the application and the login screen loads. User is already a member. User enters user name and password correctly. User information is validated by comparing user information to information stored in a database. User supplied information matches what is found in the database. User is shown the main application.

So in building my custom Login form I needed to have the basic functionality to meet the above use case. You can view a demo of the custom login form in a working Flex application; right click on the application to view all the source files. Enter phil1021 for the username and seven for the password to login.

Custom Flex Component Notes

My custom Flex component, called LoginForm, extends the Form tag. Inside this custom component is all the code to create a simple login form, connect to the backend CFC, and process the results returned by the CFC.

I wanted to make my custom component somewhat independent (loosely coupled) from the actual application. So my custom component would need to have a couple of advanced features: dispatching a custom event and providing access to a variable. These features of custom components are very well explained in reference 1, pages 91-112.

Declaring a custom event is simple. Here's the code:


<mx:Metadata>

[Event(name="loginSuccess", type="flash.events.Event")]
        
</mx:Metadata>

The above code defines a new custom event named loginSucess. When the CFC getMemberByUserNameAndPassword method returns a Member object--thus indicating the provided username and password were correct--I have the custom LoginForm component dispatch this event.



    dispatchEvent(new Event("loginSuccess"));

In the main GeekNews mxml file, I specify this event when adding the LoginForm to my application.



<myComp:LoginForm id="login" loginSuccess="handleLoginSucess(event);" />

So when the custom LoginForm component dispatches the loginSucess event, the GeekNews application calls the handleLoginSucess function. If you examine the source code (see link above), you'll note that in the handleLoginSucess function I change the ViewStack's selected child to the main application view.

Now I also want the main GeekNews application to have a reference to the Member object that is returned by the CFC when the userName and password are found in the table. Since this Member object will be returned to the custom LoginForm component, I need a way to get the Member object when inside my main GeekNews application. Here is the code from the custom LoginForm component that sets up a Member object variable and provides a public get method that will allow outside components to access it.


//Define private variable to hold the Member
//object returned by the CFC method
private var __member:Member;


// Define a getter method for the private variable.
public function get member():Member {
    
    return __member;

}//end get member

To get the above __member object from within the main GeekNews application I just need to use the value of the id attribute of the custom LoginForm component (in my code I used login for the id value). The following code is in the function handleLoginSuccess which is called when the custom LoginForm component dispatches the loginSucess event. Right now the main GeekNews application is just using the Member object's firstName field (see the welcome message after a successful login).



//get the Member object from
//the LoginForm component
aMember = login.member ;
    

What's Next?

If you view the demo and enter phil1021 and seven you'll see that I've successfully completed a prototype that satisfied my very simple first use case. In the next iteration of the project, I'll add the capability for the LoginForm component to handle an incorrect username and password (right now just an Alert box pops up with the error message returned by the CFC). This will satisfy a branch of our first use case: If the user provides an incorrect username or password, provide the user with an error message and allow the user to try again. I may also had some validation to the LoginForm's textinput tags.

Please feel free to provide feedback, criticism, and even better suggestions/examples of how to do this better.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Hi Bruce,

Why not use setRemoteCredentials in your remoteObject and use cflogin at the server side ?
# Posted By João Fernandes | 3/18/07 6:02 AM
Joao:

I had actually used setRemoteCredentials and cflogin in a previous project and I wrote about it in my blog (see:
http://www.brucephillips.name/blog/index.cfm/2006/...).

However, I ran into a problem with cflogin. If the user did not actually logout, but just closed the browser, and then returned before their session expired on the ColdFusion server, they could not log back in. Therefore they could not advance to the main application. Flex would show the user the login form, the user would try to login again, and cflogin would not work correctly on the ColdFusion side.

I think this problem is discussed in the comments on the that blog entry.

Also, since I don't really need role security for the user, this simpler login system seems more robust.

Bruce
# Posted By Bruce | 3/18/07 7:07 AM
Bruce,

you could call ro.logout() or eventually a ro.someLoutOut function which could call <cflogout for you.

we use it and never run into problems.
# Posted By João Fernandes | 3/18/07 8:18 AM
Bruce, this suits an immediate need perfectly - thanks very much for your wonderful work with Flex/CF. In the near future, I might need to have a member-reporting type page, where a particular Module (in the 2.0.1 sense) and particular columns of a db are accessible based on who you log in as. Would studying your code here be useful to that end, if you consider it's my first crack at such a thing?

Thanks kindly,
Shawn
# Posted By shawn gibson | 3/21/07 2:36 PM
Shawn:

I think studying my Login Form blog entries would be helpful. If you right click on the application example, you can download all the source code. You'll need to change the structure of the member table to include a role column. Then you will be able to use the member's role value to determine which ActionScript and CFC functions that user can execute.
# Posted By Bruce | 3/21/07 5:18 PM
Hi Bruce,

Thank you so much for this sample, it work great. However, when I place Member.as under src.com.cfgenerated.models, I keep getting this error (1046: Type was not found or was not a compile-time constant: Member.) on LoginForm.mxml. Can you tell me where I need to update to make sure it LoginForm.mxml see it?

Thank again for your help.

Phong
# Posted By Phong | 6/5/07 11:46 PM
Phong:

Try adding an import statement to LoginForm.mxml. If you don't have the Member.as file directly under the project root then you'll need to also change the package statement in Member.as and import member.as into LoginForm.mxml.

Consult the Flex Developer's Guide for ActionScript and Package statements.
# Posted By Bruce | 6/6/07 12:21 PM
Got it! Thanks again.

Phong
# Posted By Phong | 6/6/07 12:35 PM
Hi Bruce,

Thank you so much for this sample, it work great. However, when I place Member.as under src.com.cfgenerated.models, I keep getting this error (1046: Type was not found or was not a compile-time constant: Member.) on LoginForm.mxml. Can you tell me where I need to update to make sure it LoginForm.mxml see it?

Thank again for your help.

Phong
I ran into the same problem with cflogin, then we enabled jsessions in cfadmin. this way the session is automatically ended to the user when they close the browser
# Posted By Jorge Tejada | 4/24/08 8:03 AM
Hello!
I found this tutorial really great, and helped me a lot to understand some things in flex...

THough i have a "small" issue!
When i am getting the result and passing it to the local object Member (in my case the call is aUser = login.user) then the aUser object is ALWAYS null...

And i don't get why... Any ideas???

//get the Member object from
//the LoginForm component
aMember = login.member ;
# Posted By Panos Zuratharamon | 6/20/08 8:37 AM
Finally it worked!
The path to map the User class to the CFC, was pointing to the wrong directory...
Strangely, there was NO error

?????
# Posted By Panos Zuratharamon | 6/23/08 2:52 AM
Do you know if it's possible to do a secure login like HTTPS using this technique?
# Posted By Scott | 7/1/08 4:46 AM
hello guyz i am having a problem with the login form. i am using the example of bruce phillps which can be found at this url
"Login-System-For-A-Flex-Application-With-ColdFusion-Backend-Part-3--Custom-Login-Form-Component.htm". here is where am confused: my custom login form is
called as a pop-up window from the main application while bruce's example it is called through another custom component(hope you understand what am saying),
anyhow my question is "HOW CAN I HANDLE LOGIN SUCCESS THROUGH A POPUP LOGIN FORM" here is my code

"upload_form.as"

// ActionScript file
         import mx.managers.PopUpManager;
         import mx.controls.Alert;
      import mx.rpc.events.ResultEvent;
      import mx.rpc.events.FaultEvent;
      import flash.events.Event;
      private var fileRef:FileReference;
      import mx.utils.ObjectUtil;
      import mx.events.ValidationResultEvent;


          // Define private variable to hold the Member
      //object returned by the CFC method
    private var __member:Member;
   
   
      // Define a getter method for the private variable.
      public function get member():Member {
         
         return __member;
      
      }//end get member
         
      
/*Called automatically when the result is returned by the
      CFC method getMemberByUserNameAndPassword
      */
      public function resultGetMemberByUserNameAndPassword(event:ResultEvent):void {
         
//used for debugging - shows details about result
//returned by the CFC method
//Alert.show( ObjectUtil.toString(event.result) );

/*result returned is a Member object
cast the result returned to a Member class
    and assign it to our Member object*/
__member = event.result as Member ;

//remove text in the login input fields
memUserName.text="";
memPassword.text="";

// Define event object, initialize it, then dispatch it.
dispatchEvent(new Event("loginSuccess"));
   

      } //end function getMemberByUserNameAndPassword
      
   
   /*
    automatically called if the CFC method call causes an error
   */   
   private function faultGetMemberByUserNameAndPassword(event:FaultEvent):void{
      
         //Alert.show( ObjectUtil.toString(event.fault) );
         
         //Alert.show(event.fault.faultString );
         
         currentState = 'loginError';
         
         
    }//end function faultGetMemberByUserNameAndPassword
   
   
    /*Function is called automatically when the user makes
    changes to any of the registration input fields
    if all input fields are valid, the register btn is enabled
    */
private function validateRegistration():void {
   var usernameResult:ValidationResultEvent = userNameValidator.validate();
   var passwordResult:ValidationResultEvent = passwordValidator.validate();
   var firstnameResult:ValidationResultEvent = firstNameValidator.validate();
   var lastnameResult:ValidationResultEvent = lastNameValidator.validate();
   var emailResult:ValidationResultEvent = emV.validate();
   var phoneResult:ValidationResultEvent = phV.validate();
   var photoResult:ValidationResultEvent = photoValidator.validate();
   var zipResult:ValidationResultEvent = zipValidator.validate();    
   var confirmResult:ValidationResultEvent = confirmPsV.validate();
   
   if (usernameResult.type == ValidationResultEvent.VALID &&
    passwordResult.type == ValidationResultEvent.VALID ) {
       
        loginButton.enabled = true;
   
   
    }
   
    else if(usernameResult.type == ValidationResultEvent.VALID &&
          passwordResult.type == ValidationResultEvent.VALID &&
          firstnameResult.type == ValidationResultEvent.VALID &&
          lastnameResult.type == ValidationResultEvent.VALID &&
          emailResult.type == ValidationResultEvent.VALID &&
          phoneResult.type == ValidationResultEvent.VALID &&
          photoResult.type == ValidationResultEvent.VALID &&
          zipResult.type == ValidationResultEvent.VALID &&
          confirmResult.type == ValidationResultEvent.VALID){
       
       loginButton.enabled = true;
    }
   
    else {
   
    loginButton.enabled = false;
   
    }//end if (firstNameResult.type..


}//end function handleValid

/*
Called when loginBtn is clicked
*/


private function processLogin():void {
   
   
    //change state back to the default so the errorLogin state doesn't appear again
   
    currentState = '';
   
      //Alert.show("Processing Login...");
      MemberService.getMemberByUserNameAndPassword(memUserName.text, memPassword.text);
      
      
}//end function processLogin


/*
    automatically called if the CFC method call causes an error
   */   
   private function faultCreateMemberService(event:FaultEvent):void{
      
         Alert.show( ObjectUtil.toString(event.fault) );
         //TODO: handle the faults and provide instructions to the user
         
    }//end function faultGetMemberByUserNameAndPassword


"login_reg.mxml"

<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"; creationComplete="PopUpManager.centerPopUp(this);init()" title="LogIn Form"
            titleStyleName="panelTitle" backgroundAlpha="0.8" backgroundColor="#000000" borderColor="#000000" borderAlpha=".8">
   
   <!-- Define the transition to animate the change of view state. -->
<mx:transitions>
<mx:Transition>
<mx:Parallel
targets="{[this, form1, registerLink, loginButton]}">
<mx:Resize duration="500" easingFunction="myEasingFunction"/>
<mx:Sequence target="{form1}">
<mx:Blur duration="200" blurYFrom="1.0" blurYTo="20.0"/>
<mx:Blur duration="200" blurYFrom="20.0" blurYTo="1"/>
</mx:Sequence>
</mx:Parallel>
</mx:Transition>
</mx:transitions>

   <mx:states>
      <mx:State name="reg">
         <mx:SetProperty target="{loginButton}" name="label" value="Register"/>
         <mx:SetProperty target="{registerLink}" name="label" value="Back to Login Form"/>
         <mx:SetEventHandler target="{registerLink}" name="click" handler="currentState=''"/>
         <mx:SetProperty target="{controlbar1}" name="y" value="238"/>
         <mx:SetProperty target="{formitem2}" name="y" value="150"/>
         <mx:SetProperty target="{formitem1}" name="y" value="150"/>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="0" y="0" label="First Name:">
               <mx:TextInput id="memFirstName"/>
            </mx:FormItem>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="251" y="0" label="Last Name:">
               <mx:TextInput id="memLastName"/>
            </mx:FormItem>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:HRule x="0" y="30" width="100%" height="22"/>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="29" y="60" label="Email:">
               <mx:TextInput id="memEmail"/>
            </mx:FormItem>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="251" y="60" label="Cell Phone:">
               <mx:TextInput id="memPhone"/>
            </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="9" y="90" label="Zip Code:">
               <mx:TextInput id="memZip"/>
            </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="268" y="90" label="Country:">
               <mx:ComboBox id="memCountry">
                  <mx:ArrayCollection>
               <mx:String>Ug</mx:String>
               <mx:String>Ke</mx:String>
               <mx:String>Rw</mx:String>
               <mx:String>Bj</mx:String>
               <mx:String>Tz</mx:String>
               </mx:ArrayCollection>
               </mx:ComboBox>
            </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{formitem1}" position="before">
            <mx:HRule x="0" y="120" width="100%" height="22"/>
         </mx:AddChild>
         <mx:SetProperty target="{formitem1}" name="x" value="-3"/>
         <mx:SetProperty target="{formitem2}" name="x" value="261"/>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="14" y="180" label="Confirm:">
                     <mx:TextInput id="memConfirm" displayAsPassword="true"/>
                  </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="280" y="180" label="Photo:">
                     <mx:TextInput id="memPhoto"/>
                     <mx:HBox width="100%">
                        <mx:Label id="message"/>
                        <mx:Spacer width="100%"/>
                        <mx:Button label="Upload" click="browseAndUpload();"/>
                     </mx:HBox>                     
                  </mx:FormItem>
         </mx:AddChild>
         <mx:SetProperty name="title" value="Registration Information"/>
         <mx:SetEventHandler target="{loginButton}" name="click" handler="insertHandler()"/>
         <mx:SetProperty target="{memPassword}" name="displayAsPassword" value="true"/>
      </mx:State>
   </mx:states>
   
   <mx:Metadata>
      [Event(name="loginSuccess", type="flash.events.Event")]
   </mx:Metadata>
   <mx:Script source="../logic/upload_form.as"/>
   <mx:RemoteObject
       id="agent"
       destination="ColdFusion"
       source="realestate portal.cfcs.MemberDAO">
      <mx:method name="create" fault="mx.controls.Alert.show(event.fault.faultString)" />
   </mx:RemoteObject>
   
   <mx:RemoteObject
      id="MemberService"
      destination="ColdFusion"
      source="realestate portal.cfcs.MemberService"
      showBusyCursor="true"
      >
      
    <mx:method name="getMemberByUserNameAndPassword"
       result="resultGetMemberByUserNameAndPassword(event)"
       fault="faultGetMemberByUserNameAndPassword(event)"/>       
   </mx:RemoteObject>
   
   <!--These validators are called automatically anytime input changes on the registration form -->
<mx:StringValidator id="userNameValidator" source="{memUserName}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter a user name."/>

<mx:StringValidator id="passwordValidator" source="{memPassword}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter a password."/>

<mx:StringValidator id="firstNameValidator" source="{memFirstName}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter your First Name."/>

<mx:StringValidator id="lastNameValidator" source="{memLastName}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter your Last Name."/>

<mx:EmailValidator id="emV" source="{memEmail}" property="text" required="true"
    requiredFieldError="You must enter an email address."/>
   
<mx:PhoneNumberValidator id="phV" source="{memPhone}" property="text" required="true"
    requiredFieldError="You must enter a Phone Number."/>
   
<mx:StringValidator id="photoValidator" source="{memPhoto}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter your Photo."/>

<mx:ZipCodeValidator id="zipValidator" source="{memZip}" property="text" required="true"
requiredFieldError="You must enter a Zip Code."/>

<mx:StringValidator id="confirmPsV" source="{memConfirm}" property="text" required="true"
maxLength="40"
requiredFieldError="You must Confirm your Password."/>

<!--End of Validators-->
   
<mx:NumberFormatter id="numberFormatter" />
<mx:Zoom id="zoom" />

   <mx:Canvas id="form1" width="100%" height="100%" backgroundAlpha="0">
      <mx:FormItem x="0" y="0" label="User Name:" id="formitem1">
               <mx:TextInput id="memUserName"/>
            </mx:FormItem>
   <mx:ControlBar id="controlbar1" y="62" width="100%">
      <mx:LinkButton id="registerLink" label="Need to Register?" click="currentState='reg'" color="#ffffff"/>
      <mx:Spacer width="100%" id="spacer1"/>
      <mx:Button label="Login" id="loginButton" click="processLogin()"
          enabled="{(memUserName.text.length == 0 || memPassword.text.length == 0) ? false : true}"
          toolTip="{loginButton.enabled == true ? 'Click to submit' : 'Enter username and password'}"/>
   </mx:ControlBar>    
   <mx:FormItem x="10" y="30" label="Password:" id="formitem2">
            <mx:TextInput id="memPassword" displayAsPassword="true"/>
         </mx:FormItem>
   </mx:Canvas>
</mx:TitleWindow>

"MemberService.cfc"

<cfcomponent displayname="MemberService" output="false" hint="Member Service">

<!---document each instance variable--->
   <cfproperty name="dsn" displayname="Datasource Name" hint="Datasource name used to connect CF to the database"
   type="string">
   
   <!---initilization area - acts like a constructor as its called automatically--->
   
   <cfscript>
   
      variables.dsn = 'info';
   
   </cfscript>
   
   <cffunction name="init" access="public" output="false" returntype="MemberService" hint="Constructor for this CFC">
      <!--- take in the datasource name as an argument and set it to the variables scope so it's available
            throughout the CFC --->
      <cfargument name="dsn" type="string" required="true" />
      <cfset variables.dsn = arguments.dsn />
      
      <!--- return the object itself --->
      <cfreturn this />
   </cffunction>
   
   
   
   <cffunction name="getMemberByUserNameAndPassword" access="public" output="false" returntype="Member"
         hint="Tries to find a record with a matching username and password. If only 1 record
            is found creates a Member object using the record's column values.">
      <cfargument name="aUserName" type="string" required="true" />
      <cfargument name="aPassword" type="string" required="true" />
      
      
      <cfset var getMember = "" />
      
      <cfscript>
      
       var aMember = createObject("Component","Member").init();
      
      
      </cfscript>
      
         
      <cftry>
      
      
      
      
         <cfquery name="getMember" datasource="#variables.dsn#">
         
            select memID, memUserName, memPassword, memFirstName, memLastName, memEmail, memPhone, memPhoto, memZip, memCountry, memConfirm
            from member
            where memUsername = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.aUserName#">
            and memPassword = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.aPassword#">
            
         </cfquery>
         
         
         <!---throw error if no records were found--->
         <cfif getMember.recordCount EQ 0>
         
            <cfthrow type="Member.NoRecordsFound" message="No members with with username of #arguments.aUserName# and password of #arguments.aPassword# were found">
         
         </cfif>

         <!---throw error if more than one record was found, violates business rule--->
         
         <cfif getMember.recordCount GT 1>
         
            <cfthrow type="Member.TooManyFound" message="Too many members with username of #arguments.aUserName# and password of #arguments.aPassword# were found" />
         
         </cfif>
      
         <cfcatch type="database"> <!---there was a problem with a query--->
         
            
            <cfthrow type="Member.select" message="#cfcatch.detail#" />
            
       </cfcatch>
         
         
      </cftry>
      
      <!---update the aMember object with values found in the record's columns--->
      
      <cfscript>
      
         aMember.setMemberID( getMember.memID);
         aMember.setUsername( getMember.memUserName);
         aMember.setPassword( getMember.memPassword);
         aMember.setFirstName( getMember.memFirstName);
         aMember.setLastName( getMember.memLastName);
         aMember.setEmail( getMember.memEmail);
         aMember.setPhone( getPhone.memPhone );
         aMember.setPhoto( getPhoto.memPhoto );
         aMember.setZip( getZip.memZip );
         aMember.setCountry( getCountry.memCountry );
         aMember.setConfirm( getConfirm.memConfirm );
      
      
      </cfscript>
      
      <cfreturn aMember />
      
   </cffunction><!---end function getMemberByUserNameAndPassword--->
   
   
   <cffunction name="createMemberService" access="public" output="false" returntype="void"
         hint="Pass Member object onto MemderDAO create function">
         
      <cfargument name="aMember" type="Member" required="yes" />
      
      <cfset var aMemberDAO = "" />
      
      
       <cftry>
          <cfscript>
          aMemberDAO = createObject("component", "MemberDAO").init('info');
          aMemberDAO.create(aMember);
          </cfscript>
      
          
       <cfcatch type="Member">
      
          <cfthrow type="Member.create" message="#cfcatch.message#" />
      
       </cfcatch>
      
       </cftry>
            
   </cffunction>
   
   
   
</cfcomponent>
# Posted By Arthurnasius | 12/1/08 9:08 AM
hello guyz i am having a problem with the login form. i am using the example of bruce phillps which can be found at this url
"Login-System-For-A-Flex-Application-With-ColdFusion-Backend-Part-3--Custom-Login-Form-Component.htm". here is where am confused: my custom login form is
called as a pop-up window from the main application while bruce's example it is called through another custom component(hope you understand what am saying),
anyhow my question is "HOW CAN I HANDLE LOGIN SUCCESS THROUGH A POPUP LOGIN FORM" here is my code

"upload_form.as"

// ActionScript file
         import mx.managers.PopUpManager;
         import mx.controls.Alert;
      import mx.rpc.events.ResultEvent;
      import mx.rpc.events.FaultEvent;
      import flash.events.Event;
      private var fileRef:FileReference;
      import mx.utils.ObjectUtil;
      import mx.events.ValidationResultEvent;


          // Define private variable to hold the Member
      //object returned by the CFC method
    private var __member:Member;
   
   
      // Define a getter method for the private variable.
      public function get member():Member {
         
         return __member;
      
      }//end get member
         
      
/*Called automatically when the result is returned by the
      CFC method getMemberByUserNameAndPassword
      */
      public function resultGetMemberByUserNameAndPassword(event:ResultEvent):void {
         
//used for debugging - shows details about result
//returned by the CFC method
//Alert.show( ObjectUtil.toString(event.result) );

/*result returned is a Member object
cast the result returned to a Member class
    and assign it to our Member object*/
__member = event.result as Member ;

//remove text in the login input fields
memUserName.text="";
memPassword.text="";

// Define event object, initialize it, then dispatch it.
dispatchEvent(new Event("loginSuccess"));
   

      } //end function getMemberByUserNameAndPassword
      
   
   /*
    automatically called if the CFC method call causes an error
   */   
   private function faultGetMemberByUserNameAndPassword(event:FaultEvent):void{
      
         //Alert.show( ObjectUtil.toString(event.fault) );
         
         //Alert.show(event.fault.faultString );
         
         currentState = 'loginError';
         
         
    }//end function faultGetMemberByUserNameAndPassword
   
   
    /*Function is called automatically when the user makes
    changes to any of the registration input fields
    if all input fields are valid, the register btn is enabled
    */
private function validateRegistration():void {
   var usernameResult:ValidationResultEvent = userNameValidator.validate();
   var passwordResult:ValidationResultEvent = passwordValidator.validate();
   var firstnameResult:ValidationResultEvent = firstNameValidator.validate();
   var lastnameResult:ValidationResultEvent = lastNameValidator.validate();
   var emailResult:ValidationResultEvent = emV.validate();
   var phoneResult:ValidationResultEvent = phV.validate();
   var photoResult:ValidationResultEvent = photoValidator.validate();
   var zipResult:ValidationResultEvent = zipValidator.validate();    
   var confirmResult:ValidationResultEvent = confirmPsV.validate();
   
   if (usernameResult.type == ValidationResultEvent.VALID &&
    passwordResult.type == ValidationResultEvent.VALID ) {
       
        loginButton.enabled = true;
   
   
    }
   
    else if(usernameResult.type == ValidationResultEvent.VALID &&
          passwordResult.type == ValidationResultEvent.VALID &&
          firstnameResult.type == ValidationResultEvent.VALID &&
          lastnameResult.type == ValidationResultEvent.VALID &&
          emailResult.type == ValidationResultEvent.VALID &&
          phoneResult.type == ValidationResultEvent.VALID &&
          photoResult.type == ValidationResultEvent.VALID &&
          zipResult.type == ValidationResultEvent.VALID &&
          confirmResult.type == ValidationResultEvent.VALID){
       
       loginButton.enabled = true;
    }
   
    else {
   
    loginButton.enabled = false;
   
    }//end if (firstNameResult.type..


}//end function handleValid

/*
Called when loginBtn is clicked
*/


private function processLogin():void {
   
   
    //change state back to the default so the errorLogin state doesn't appear again
   
    currentState = '';
   
      //Alert.show("Processing Login...");
      MemberService.getMemberByUserNameAndPassword(memUserName.text, memPassword.text);
      
      
}//end function processLogin


/*
    automatically called if the CFC method call causes an error
   */   
   private function faultCreateMemberService(event:FaultEvent):void{
      
         Alert.show( ObjectUtil.toString(event.fault) );
         //TODO: handle the faults and provide instructions to the user
         
    }//end function faultGetMemberByUserNameAndPassword


"login_reg.mxml"

<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"; creationComplete="PopUpManager.centerPopUp(this);init()" title="LogIn Form"
            titleStyleName="panelTitle" backgroundAlpha="0.8" backgroundColor="#000000" borderColor="#000000" borderAlpha=".8">
   
   <!-- Define the transition to animate the change of view state. -->
<mx:transitions>
<mx:Transition>
<mx:Parallel
targets="{[this, form1, registerLink, loginButton]}">
<mx:Resize duration="500" easingFunction="myEasingFunction"/>
<mx:Sequence target="{form1}">
<mx:Blur duration="200" blurYFrom="1.0" blurYTo="20.0"/>
<mx:Blur duration="200" blurYFrom="20.0" blurYTo="1"/>
</mx:Sequence>
</mx:Parallel>
</mx:Transition>
</mx:transitions>

   <mx:states>
      <mx:State name="reg">
         <mx:SetProperty target="{loginButton}" name="label" value="Register"/>
         <mx:SetProperty target="{registerLink}" name="label" value="Back to Login Form"/>
         <mx:SetEventHandler target="{registerLink}" name="click" handler="currentState=''"/>
         <mx:SetProperty target="{controlbar1}" name="y" value="238"/>
         <mx:SetProperty target="{formitem2}" name="y" value="150"/>
         <mx:SetProperty target="{formitem1}" name="y" value="150"/>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="0" y="0" label="First Name:">
               <mx:TextInput id="memFirstName"/>
            </mx:FormItem>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="251" y="0" label="Last Name:">
               <mx:TextInput id="memLastName"/>
            </mx:FormItem>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:HRule x="0" y="30" width="100%" height="22"/>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="29" y="60" label="Email:">
               <mx:TextInput id="memEmail"/>
            </mx:FormItem>
         </mx:AddChild>
         
         <mx:AddChild relativeTo="{form1}">
            <mx:FormItem x="251" y="60" label="Cell Phone:">
               <mx:TextInput id="memPhone"/>
            </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="9" y="90" label="Zip Code:">
               <mx:TextInput id="memZip"/>
            </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="268" y="90" label="Country:">
               <mx:ComboBox id="memCountry">
                  <mx:ArrayCollection>
               <mx:String>Ug</mx:String>
               <mx:String>Ke</mx:String>
               <mx:String>Rw</mx:String>
               <mx:String>Bj</mx:String>
               <mx:String>Tz</mx:String>
               </mx:ArrayCollection>
               </mx:ComboBox>
            </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{formitem1}" position="before">
            <mx:HRule x="0" y="120" width="100%" height="22"/>
         </mx:AddChild>
         <mx:SetProperty target="{formitem1}" name="x" value="-3"/>
         <mx:SetProperty target="{formitem2}" name="x" value="261"/>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="14" y="180" label="Confirm:">
                     <mx:TextInput id="memConfirm" displayAsPassword="true"/>
                  </mx:FormItem>
         </mx:AddChild>
         <mx:AddChild relativeTo="{form1}" position="lastChild">
            <mx:FormItem x="280" y="180" label="Photo:">
                     <mx:TextInput id="memPhoto"/>
                     <mx:HBox width="100%">
                        <mx:Label id="message"/>
                        <mx:Spacer width="100%"/>
                        <mx:Button label="Upload" click="browseAndUpload();"/>
                     </mx:HBox>                     
                  </mx:FormItem>
         </mx:AddChild>
         <mx:SetProperty name="title" value="Registration Information"/>
         <mx:SetEventHandler target="{loginButton}" name="click" handler="insertHandler()"/>
         <mx:SetProperty target="{memPassword}" name="displayAsPassword" value="true"/>
      </mx:State>
   </mx:states>
   
   <mx:Metadata>
      [Event(name="loginSuccess", type="flash.events.Event")]
   </mx:Metadata>
   <mx:Script source="../logic/upload_form.as"/>
   <mx:RemoteObject
       id="agent"
       destination="ColdFusion"
       source="realestate portal.cfcs.MemberDAO">
      <mx:method name="create" fault="mx.controls.Alert.show(event.fault.faultString)" />
   </mx:RemoteObject>
   
   <mx:RemoteObject
      id="MemberService"
      destination="ColdFusion"
      source="realestate portal.cfcs.MemberService"
      showBusyCursor="true"
      >
      
    <mx:method name="getMemberByUserNameAndPassword"
       result="resultGetMemberByUserNameAndPassword(event)"
       fault="faultGetMemberByUserNameAndPassword(event)"/>       
   </mx:RemoteObject>
   
   <!--These validators are called automatically anytime input changes on the registration form -->
<mx:StringValidator id="userNameValidator" source="{memUserName}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter a user name."/>

<mx:StringValidator id="passwordValidator" source="{memPassword}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter a password."/>

<mx:StringValidator id="firstNameValidator" source="{memFirstName}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter your First Name."/>

<mx:StringValidator id="lastNameValidator" source="{memLastName}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter your Last Name."/>

<mx:EmailValidator id="emV" source="{memEmail}" property="text" required="true"
    requiredFieldError="You must enter an email address."/>
   
<mx:PhoneNumberValidator id="phV" source="{memPhone}" property="text" required="true"
    requiredFieldError="You must enter a Phone Number."/>
   
<mx:StringValidator id="photoValidator" source="{memPhoto}" property="text" required="true"
maxLength="40"
requiredFieldError="You must enter your Photo."/>

<mx:ZipCodeValidator id="zipValidator" source="{memZip}" property="text" required="true"
requiredFieldError="You must enter a Zip Code."/>

<mx:StringValidator id="confirmPsV" source="{memConfirm}" property="text" required="true"
maxLength="40"
requiredFieldError="You must Confirm your Password."/>

<!--End of Validators-->
   
<mx:NumberFormatter id="numberFormatter" />
<mx:Zoom id="zoom" />

   <mx:Canvas id="form1" width="100%" height="100%" backgroundAlpha="0">
      <mx:FormItem x="0" y="0" label="User Name:" id="formitem1">
               <mx:TextInput id="memUserName"/>
            </mx:FormItem>
   <mx:ControlBar id="controlbar1" y="62" width="100%">
      <mx:LinkButton id="registerLink" label="Need to Register?" click="currentState='reg'" color="#ffffff"/>
      <mx:Spacer width="100%" id="spacer1"/>
      <mx:Button label="Login" id="loginButton" click="processLogin()"
          enabled="{(memUserName.text.length == 0 || memPassword.text.length == 0) ? false : true}"
          toolTip="{loginButton.enabled == true ? 'Click to submit' : 'Enter username and password'}"/>
   </mx:ControlBar>    
   <mx:FormItem x="10" y="30" label="Password:" id="formitem2">
            <mx:TextInput id="memPassword" displayAsPassword="true"/>
         </mx:FormItem>
   </mx:Canvas>
</mx:TitleWindow>

"MemberService.cfc"

<cfcomponent displayname="MemberService" output="false" hint="Member Service">

<!---document each instance variable--->
   <cfproperty name="dsn" displayname="Datasource Name" hint="Datasource name used to connect CF to the database"
   type="string">
   
   <!---initilization area - acts like a constructor as its called automatically--->
   
   <cfscript>
   
      variables.dsn = 'info';
   
   </cfscript>
   
   <cffunction name="init" access="public" output="false" returntype="MemberService" hint="Constructor for this CFC">
      <!--- take in the datasource name as an argument and set it to the variables scope so it's available
            throughout the CFC --->
      <cfargument name="dsn" type="string" required="true" />
      <cfset variables.dsn = arguments.dsn />
      
      <!--- return the object itself --->
      <cfreturn this />
   </cffunction>
   
   
   
   <cffunction name="getMemberByUserNameAndPassword" access="public" output="false" returntype="Member"
         hint="Tries to find a record with a matching username and password. If only 1 record
            is found creates a Member object using the record's column values.">
      <cfargument name="aUserName" type="string" required="true" />
      <cfargument name="aPassword" type="string" required="true" />
      
      
      <cfset var getMember = "" />
      
      <cfscript>
      
       var aMember = createObject("Component","Member").init();
      
      
      </cfscript>
      
         
      <cftry>
      
      
      
      
         <cfquery name="getMember" datasource="#variables.dsn#">
         
            select memID, memUserName, memPassword, memFirstName, memLastName, memEmail, memPhone, memPhoto, memZip, memCountry, memConfirm
            from member
            where memUsername = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.aUserName#">
            and memPassword = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.aPassword#">
            
         </cfquery>
         
         
         <!---throw error if no records were found--->
         <cfif getMember.recordCount EQ 0>
         
            <cfthrow type="Member.NoRecordsFound" message="No members with with username of #arguments.aUserName# and password of #arguments.aPassword# were found">
         
         </cfif>

         <!---throw error if more than one record was found, violates business rule--->
         
         <cfif getMember.recordCount GT 1>
         
            <cfthrow type="Member.TooManyFound" message="Too many members with username of #arguments.aUserName# and password of #arguments.aPassword# were found" />
         
         </cfif>
      
         <cfcatch type="database"> <!---there was a problem with a query--->
         
            
            <cfthrow type="Member.select" message="#cfcatch.detail#" />
            
       </cfcatch>
         
         
      </cftry>
      
      <!---update the aMember object with values found in the record's columns--->
      
      <cfscript>
      
         aMember.setMemberID( getMember.memID);
         aMember.setUsername( getMember.memUserName);
         aMember.setPassword( getMember.memPassword);
         aMember.setFirstName( getMember.memFirstName);
         aMember.setLastName( getMember.memLastName);
         aMember.setEmail( getMember.memEmail);
         aMember.setPhone( getPhone.memPhone );
         aMember.setPhoto( getPhoto.memPhoto );
         aMember.setZip( getZip.memZip );
         aMember.setCountry( getCountry.memCountry );
         aMember.setConfirm( getConfirm.memConfirm );
      
      
      </cfscript>
      
      <cfreturn aMember />
      
   </cffunction><!---end function getMemberByUserNameAndPassword--->
   
   
   <cffunction name="createMemberService" access="public" output="false" returntype="void"
         hint="Pass Member object onto MemderDAO create function">
         
      <cfargument name="aMember" type="Member" required="yes" />
      
      <cfset var aMemberDAO = "" />
      
      
       <cftry>
          <cfscript>
          aMemberDAO = createObject("component", "MemberDAO").init('info');
          aMemberDAO.create(aMember);
          </cfscript>
      
          
       <cfcatch type="Member">
      
          <cfthrow type="Member.create" message="#cfcatch.message#" />
      
       </cfcatch>
      
       </cftry>
            
   </cffunction>
   
   
   
</cfcomponent>
# Posted By Arthurnasius Amanyire | 12/1/08 9:13 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1.002. Contact Blog Owner