


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
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
<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
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.





There are no comments for this entry.
[Add Comment]