HomeAbout Mark van HedelContact Mark van Hedel



Using FLEX for input forms in BlogCFC :: Part 6

Seems like we have it all working now, the last part of the working form is done, what I did for my form is set the build folder to a new folder in the webroot. This folder is flexblog. So now all I have to do is instead of pointing my code to the addcomment.cfm I just have to point it to flexblog/Comments.cfm

So after a small search in the application I found this next link in layout.cfm

cWin = window.open("#application.rooturl#/addcomment.cfm?id="+id,"cWin","width=550,height=700,menubar=yes,personalbar=no,dependent=true,directories=no,status=yes,toolbar=no,scrollbars=yes,resizable=yes");
And changed it to this.
cWin = window.open("#application.rooturl#/flexblog/Comments.cfm?id="+id,"cWin","width=550,height=700,menubar=yes,personalbar=no,dependent=true,directories=no,status=yes,toolbar=no,scrollbars=yes,resizable=yes");
This is the only thing that has to be changed in the original application and now it should all work in the application. One thing you might have seen I've added is of course a stylesheet. We can't use the regular stylesheet for this since that is using styles not known to Flex, I just made a simple stylesheet to match the colors of my blog, if you don't use a stylesheet here you will just have the default Flex controls and colors for your input form. You might notice I have not made any changes to the ColdFusion server I'm running, this is since the remoting-config.xml already contains everything it needs to make the connection work, so this does not need anything extra, we will just connect to the ColdFusion destination in there and it will make the connection work.

Now as I already mentioned before I'll add all the code I used to this post, in case you have been using these posts to create everything yourself I'll also just paste the 3 main files I used and the stylesheet here for you to continue your own creation. In case you don't have FlexBuilder downloaded or you don't want to compile everything yourself I'll also provide you with the .swf file and you can just use that in your current BlogCFC application without making any changes. Of course if you want you can always change the stylesheet since not a lot of people would want to go with the colors I used here.

RemoteUpdateConnection.cfc

<!---
   Name : RemoteUpdateConnection
   Author : Mark van Hedel
   Website : http://flexpair.org
   Created : April 3, 2008
   Purpose   : Blog CFC
--->

<cfcomponent displayname="RemoteUpdateConnection" hint="Update commments and trackbacks" output="false">


   <cffunction name="addComments" displayname="addComments" hint="Add comments to an entry" access="remote" output="false" returntype="void">
      <cfargument name="entryID" type="UUID" required="true">
      <cfargument name="commenterName" type="String" required="true">
      <cfargument name="email" type="string" required="true">
      <cfargument name="website" type="string" required="true">
      <cfargument name="comments" type="string" required="true">
      <cfargument name="subscribe" type="boolean" required="true">
      
      <cfset var rb = application.utils.getResource>

      <cfset var entry = retrieveEntry(arguments.entryID)>
      <cfset var commentID = application.blog.addComment(arguments.entryID,left(arguments.commenterName,50), left(arguments.email,50), left(arguments.website,255), arguments.comments, arguments.subscribe)>
      <cfset var subject = rb("commentaddedtoblog") & ": " & application.blog.getProperty("blogTitle") & " / " & rb("entry") & ": " & entry.title>
      <cfset var commentTime = dateAdd("h", application.blog.getProperty("offset"), now())>
      <cfsavecontent variable="email">
      <cfoutput>
#rb("commentaddedtoblogentry")#:   #entry.title#
#rb("commentadded")#:       #application.localeUtils.dateLocaleFormat(commentTime)# / #application.localeUtils.timeLocaleFormat(commentTime)#
#rb("commentmadeby")#:       #arguments.commenterName# <cfif len(arguments.website)>(#arguments.website#)</cfif>
#rb("ipofposter")#:         #cgi.REMOTE_ADDR#
URL: #application.blog.makeLink(arguments.entryID)###c#commentID#

   
#arguments.comments#
   
------------------------------------------------------------
#rb("unsubscribe")#: %unsubscribe%
This blog powered by BlogCFC #application.blog.getVersion()#
Created by Raymond Camden (ray@camdenfamily.com)
         </cfoutput>
         </cfsavecontent>
   
         <cfif not application.commentmoderation>
            <cfset application.blog.notifyEntry(entry.id, trim(email), subject, arguments.email)>
         <cfelse>
            <cfset application.blog.notifyEntry(entry.id, trim(email), subject, arguments.email, true)>
         </cfif>
      <cfmodule template="../../../tags/scopecache.cfm" scope="application" clearall="true">
   </cffunction>
   
   <cffunction name="retrieveEntry" displayname="retrieveEntry" hint="Retrieve current Entry" access="remote" output="false" returntype="struct">
      <cfargument name="entryID" type="String" required="true" displayname="entryID">
      <cfreturn application.blog.getEntry(arguments.entryID,true)>
   </cffunction>
   
   <cffunction name="retrieveRbValues" displayname="retrieveRbValues" hint="Retrieve resource bundle values" access="remote" output="false" returntype="struct">
      <cfargument name="page" type="string" required="true" displayname="page">
      <cfscript>
         // Create a new struct to add all values
         var rbValues = structNew();
         rb = application.utils.getResource;
         if (arguments.page == "addComments"){
            rbValues.mustincludename = rb("mustincludename");
            rbValues.mustincludeemail = rb("mustincludeemail");
            rbValues.invalidurl = rb("invalidurl");
            rbValues.mustincludecomments = rb("mustincludecomments");
            rbValues.comments = rb("comments");
         rbValues.postyourcomments = rb("postyourcomments");
            rbValues.name = rb("name");
            rbValues.emailaddress = rb("emailaddress");
            rbValues.website = rb("website");
            rbValues.remembermyinfo = rb("remembermyinfo");
            rbValues.subscribe = rb("subscribe");
            rbValues.subscribetext = rb("subscribetext");
            rbValues.cancel = rb("cancel");
            rbValues.cancelconfirm = rb("cancelconfirm");
            rbValues.post = rb("post");
            rbValues.commentsnotallowed = rb("commentsnotallowed");
         }
         return rbValues;
      </cfscript>
   </cffunction>
</cfcomponent>

Comments.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="creationCompleteHandler()">
   <mx:Style source="css/flexform.css"/>
   <mx:Script source="actionscript/CommentsAs.as"/>
   <mx:RemoteObject id="remoteUpdate" destination="ColdFusion" source="org.flexpair.blog.RemoteUpdateConnection" showBusyCursor="true"/>
   <mx:EmailValidator id="mailValidator" source="{email}" property="text" triggerEvent="" />
   <!-- Title -->
   <mx:ApplicationControlBar dock="true">
      <mx:Label text="{_rbValues.comments} : {_entry.title}" styleName="header" horizontalCenter="0" textAlign="center" width="100%"/>
   </mx:ApplicationControlBar>

   <mx:Panel layout="absolute" title="{_rbValues.postyourcomments}">
      <mx:Form width="100%" height="100%">
         <mx:FormItem label="{_rbValues.name} :">
            <mx:TextInput width="300" maxChars="50" id="commenterName"/>
         </mx:FormItem>
         <mx:FormItem label="{_rbValues.emailaddress} :">
            <mx:TextInput width="300" maxChars="50" id="email"/>
         </mx:FormItem>
         <mx:FormItem label="{_rbValues.website} :">
            <mx:TextInput text="http://" width="300" maxChars="255" id="website"/>
         </mx:FormItem>
         <mx:FormItem label="{_rbValues.comments} :">
            <mx:TextArea width="300" height="150" id="comments"/>
         </mx:FormItem>
         <mx:FormItem label="{_rbValues.remembermyinfo}">
            <mx:CheckBox id="remember"/>
         </mx:FormItem>
         <mx:FormItem label="{_rbValues.subscribe}">
            <mx:CheckBox id="subscribe"/>
         </mx:FormItem>
         <mx:Label text="{_rbValues.subscribetext}"/>
         <mx:FormItem>
            <mx:HBox width="100%">
               <mx:Button label="{_rbValues.cancel}" id="cancel" click="cancelFormHandler()"/>
               <mx:Button label="{_rbValues.post}" id="post" click="submitFormHandler(event)"/>
            </mx:HBox>
         </mx:FormItem>
      </mx:Form>
   </mx:Panel>
</mx:Application>

CommentsAs.as

/*
   Name : CommentsAs
   Author : Mark van Hedel
   Website     : http://flexpair.org
   Created : April 3, 2008
   Purpose       : Blog CFC
*/

// ActionScript file
import flash.events.MouseEvent;
import flash.external.ExternalInterface;
import flash.net.SharedObject;

import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.ValidationResultEvent;
import mx.rpc.AsyncToken;
import mx.validators.RegExpValidator;

import org.flexpair.blog.responders.EntryResponder;
import org.flexpair.blog.responders.ResourceBundleResponder;
import org.flexpair.blog.responders.SubmitResponder;
import org.flexpair.blog.vo.CommentVo;
import org.flexpair.blog.vo.EntryVo;
import org.flexpair.blog.vo.ResourceBundleVo;

// Responders
private var _rbResponder:ResourceBundleResponder;
private var _entryResponder:EntryResponder;

// resource bundle values
[Bindable]
private var _rbValues:ResourceBundleVo = new ResourceBundleVo();

// entry values
[Bindable]
private var _entry:EntryVo = new EntryVo();

// Comments Vo
private var _comments:CommentVo = new CommentVo();

// Shared object to hold user data on the users machine
private var _so:SharedObject = SharedObject.getLocal("BlogData");

/**
* Handle creation complete event to retrieve data and populate the form.
*/
private function creationCompleteHandler():void {
   // Retrieve resource bundle
   _rbResponder = new ResourceBundleResponder();
   _rbResponder.rbValues = _rbValues;
   var token:AsyncToken = remoteUpdate.retrieveRbValues("addComments");
   token.addResponder(_rbResponder);
   
   // Retrieve user settings
   fillComments();
   _entry.id = ExternalInterface.call("getID");

   // Retrieve entry
   _entryResponder = new EntryResponder();
   _entryResponder.entry = _entry;
   var tkn:AsyncToken = remoteUpdate.retrieveEntry(_entry.id);
   tkn.addResponder(_entryResponder);
   commenterName.getFocus();
}



/**
* Fill with default values if a user has decided to remember it's settings.
*/
private function fillComments():void {
   if (_so.data.commenterName != undefined) {
      commenterName.text = _so.data.commenterName;
      email.text = _so.data.email;
      website.text = _so.data.website;
   }else {
      commenterName.text = ExternalInterface.call("getName");
      email.text = ExternalInterface.call("getEmail")
      website.text = ExternalInterface.call("getWebsite");
   }
   if (commenterName.text.length >= 2 || email.text.length >= 5 || website.text.length >= 10){
      remember.selected = true;
   }
}

/**
* Handle form submission
*/
private function submitFormHandler(event:MouseEvent):void {
   if (validateForm()) {
      _comments.name = commenterName.text;
      _comments.email = email.text;
      _comments.comments = comments.text;
      if (website.text != "" && website.text != "http://") {
         _comments.website = website.text;
      } else {
         _comments.website = "";
      }
      _comments.remember = remember.selected;
      _comments.subscribe = subscribe.selected;
      // save data if a user wants to be remembered
      saveUserValues();
      // Save comments to the server
      saveComments();
   }
}


/**
* Handle the submission of the form to the server
*/
private function saveComments():void {
   var submitResponder:SubmitResponder = new SubmitResponder();
   var token:AsyncToken = remoteUpdate.addComments(_entry.id, _comments.name, _comments.email, _comments.website, _comments.comments, _comments.subscribe);
   token.addResponder(submitResponder);
}

/**
* since this is a flash form and not html anymore we can save the user information to a shared object on the users machine
* instead of saving it to cookies.
*/
private function saveUserValues():void {
   if (_comments.remember){
      _so.data.commenterName = _comments.name;
      _so.data.email = _comments.email;
      _so.data.website = _comments.website;
      // immediately store data to disk
      _so.flush();
   } else {
    _so.clear();
    _so.flush();
   }
}

/**
* Validate the form
*/
private function validateForm():Boolean {
   var submitAllowed:Boolean = true;
   // validate name
   if (commenterName.text.length == 0) {
      commenterName.errorString = _rbValues.mustincludename;
      submitAllowed = false;
   }
   // validate email
   var evt:ValidationResultEvent = mailValidator.validate();
   if (evt.type == ValidationResultEvent.INVALID )
   {
      email.errorString = _rbValues.mustincludeemail;
      submitAllowed = false;
   }
   // validate website
   if (website.text != "" && website.text != "http://") {
      var urlValidator:RegExpValidator = new RegExpValidator();
      urlValidator.source = website;
      urlValidator.property = "text";
      urlValidator.expression = "^(((https?:|ftp:|gopher:)\/\/))[-[:alnum:]\?%,\.\/&##!@:=\+~_]+[A-Za-z0-9\/]$";
      urlValidator.flags = "i";
      var vld:ValidationResultEvent = urlValidator.validate();
      if (vld.type == ValidationResultEvent.INVALID )
      {
         website.errorString = _rbValues.invalidurl;
         submitAllowed = false;
      }
   }
   // validate comments
   if (comments.text.length == 0){
      comments.errorString = _rbValues.mustincludecomments;
      submitAllowed = false;
   }
   return submitAllowed;
}


/**
* In case the form is cancelled ask for confirmation
*/
private function cancelFormHandler():void {
   Alert.show(_rbValues.cancelconfirm, "message", (Alert.YES | Alert.NO), this, confirmCancelHandler);
}

/**
* In case the confirm bar is clicked decide here if the the comments are cancelled or not.
*/
private function confirmCancelHandler(event:CloseEvent):void
{
   if (event.detail==Alert.YES)
   {
      // Close the form
      ExternalInterface.call("closeWindow");
   }
}

flexform.css

/* CSS file */
      Application {
       color: #ff0000;
       background-color: #000000;
      }
      
      ApplicationControlBar {
       dropShadowColor: #ff0000;
      }

      Panel {
       backgroundAlpha: 0;
       backgroundColor: #ffffff;
       dropShadowColor: #ff0000;
      }

      CheckBox {
       fillAlphas: 0.87, 0.79, 0.75, 0.8;
       fillColors: #ff0000, #ff0000, #ff0000, #ff0000;
      }

      .header {
         font-family: "Trebuchet MS", verdana, arial, sans-serif;
         color: #D00000;
         border:1px solid #999;
         font-size: 16;
         padding:5px;
         margin-bottom:10px;
         font-weight:normal;
      }

      Button {
       cornerRadius: 28;
       highlightAlphas: 0.3, 0;
       fillColors: #990000, #000000, #000000, #990000;
       color: #ff0000;
       textRollOverColor: #ff0000;
       fontWeight: bold;
      }
      TextInput {
       borderStyle: solid;
       borderColor: #990000;
       borderThickness: 2;
       cornerRadius: 11;
       backgroundAlpha: 0.45;
       backgroundColor: #990000;
       color: #ff0000;
       paddingLeft: 4;
       dropShadowEnabled: true;
      }
      TextArea {
       color: #ff0000;
       paddingLeft: 2;
       leading: 2;
       backgroundAlpha: 0.45;
       backgroundColor: #990000;
       borderColor: #990000;
       borderThickness: 2;
       cornerRadius: 11;
       dropShadowEnabled: true;
}

Now if you have any comments, remarks or found some kind of mistake in my posts or my code please let me know and I'll try to fix that. As you might have noticed I have it all running on my blog here, but I would be interested to know if other people have this running somewhere and if it is causing any problems, so please post a comment here if you tried this or leave me a mail. Since this blog is also the testcase of the form I wouldn't mind some testers with comments of what you think of the post or item.

Comments
BlogCFC was created by Raymond Camden. This blog is running version 5.9.002. Contact Blog Owner