Oct

03

A Little Taste of Spry 1.6 Goodness

22 comments    print    email    feed   AJAX , Spry Framework , Web 2.0

A few days I blogged about Adobe's release (and sexy-fication) of the javascript framework Spry 1.6.  While I have not had a lot of time to play around with the absolutely unique elements, some cool additions are the improvements they made to the password verification and confirmation widgets.  With the root password widget, the developer can create a pretty robust set of validation rules for user-entered passwords, such as setting minimum and maximum lengths, required special characters/numbers, etc.  The best part is that the Spry framework makes these validations incredibly easy to implement on a normal HTML form.  For example, here is an example password field I have created:

 



<span id="passwordValidation">
    <input type="password" name="password" id="password" />
        <span class="passwordRequiredMsg">Please enter a password.</span>
        <span class="passwordMinCharsMsg">Your password must be at least 7 characters long.</span>
        <span class="passwordInvalidStrengthMsg">Your password must contain at least 1 number.</span>
</span>


That's it.  Basically, the entire password widget is a span wrap on the password field.  Within this, special validation messages can be defined.  On this example, I have set an error message for "required," "minimum characters" and "password strength". 

On the function side, the following is all that is required to fully validate this field:



var password = new Spry.Widget.ValidationPassword("passwordValidation", {minChars:7, minNumbers:1, validateOn:["blur", "change"]});


Basically, I simply pass the ID of the widget ("passwordValidation", the ID I gave to the span above) to the Spry.Widget.ValidationPassword() function and then define the various rules that I want to apply to it.  I finish the function call with the action I want to trigger the function (here, "blur").  That's it--very simple.

To further round out my example, I have also included a password confirmation field, a required security question field, and a custom-validation zip code field.  But most interesting (at least to me) is the functionality on the email field.  In addition to validating that the user has entered a syntactically correct email address (that is, one containing an "@" followed by "domain.com"), it also makes an asynchronous call to a ColdFusion component to check whether or not the email exists in the database.  It finishes up with displaying a message depending upon the result of the component call.  I do this with the following:



function checkEmail() {
    var email = document.getElementById('email');
    var emailText = document.getElementById('emailVerification');
    emailText.innerHTML = "";
    var email = email.value;
       Spry.Utils.loadURL("POST", "registration.cfc?method=checkEmail&email=" + email, true, emailResult);
}


Here, I am simply getting the values of the email input field, as well as clearing out any values that exist in the "emailVerification" field.  After this, I pass the value of the email field to my component.  The component queries the database, evaluates the input, and then returns the status of the email lookup.  Once returned, loadURL() fires the callback function "emailResult", listed below.

function emailResult(request) {
       var result = request.xhRequest.responseText;
       var xmldom = Spry.Utils.stringToXMLDoc(result);  
       var verifyEmail=xmldom.getElementsByTagName("email");
       var emailText = document.getElementById('emailVerification');
    var emailNode = verifyEmail.item(0);
       if (emailNode.getAttribute("id") == "yes") {
        emailText.innerHTML += "That email has already been taken";
    }
    else {
        emailText.innerHTML += "That email is available";
    }
}


In a nutshell, this function simply parses the returned value from the component call.  If the result is "yes" (i.e., the email exists in the database), I return an error message.  If "no," the user is told so.

Now obviously, strikingly absent from this example is any server-side validation.  If I were to implement this into an application, I would want to duplicate the various bits of functionality for server-side processing, for the end-user could easily disable javascript in their browser and render all of this fun, flashy stuff entirely moot.  Now of course, this means more work for the developer.  However, I think it is worth it as the kind of UI options outlined above can make the end-user experience more robust and significantly less frustrating (don't you hate getting error messages AFTER hitting submit and then have to go back and do it all over again?).

Anyway, this is long enough now.  Click here to see this code in action! 

 


22 comments logged

Please login to leave a comment


existdissolve
Demiurge
587 posts

October 17, 2007 at 05:41:55 PM
existdissolve checks many lines of code, then responds:

TB--

I am myself quite new to Spry, especially to the data-connectivity side, so I don't know if my answer is a "best practice". With that caveat, here is my suggestion for how to accomplish what you are wanting to do.

To begin, the way CFC's (at least in 7 and below) return data to Spry is somewhat less than helpful. For example, if you specify "returntype=string" and then do a normal "<cfreturn "" />, you will notice, in Firebug for example, that ColdFusion returns a WDDX packet. No good for Spry. And if you simply specify a returntype of "XML", ColdFusion throws in a bunch of extra information [even with whitespace suppressed AND output of "false" on the component and methods] that absolutely kills SpryData's parsing of the returned values.

So, return data from a CFC requires a bit of scrubbing. Here's what I do:

First and foremost, make absolutely sure that "output=no" is explicitly set on both the component tag and the function tag.

Second, make sure to include "<cfsetting enablecfoutputonly="yes" />" after the beginning of the component.

Third, run any method processing as normal. Then, when you are ready to return queries, strings, booleans, etc., wrap it into a xml format like so:

<cfsavecontent variable="verifyEmail">
<?xml version=""1.0"" encoding=""UTF-8""?>
<email id="no"/>
</cfsavecontent>
<cfset verifyEmail = xmlParse(verifyEmail) >
<cfcontent type="text/xml; charset=UTF-8">
<cfreturn verifyEmail />

I use cfsavecontent to create my variable, but I assume using cfxml would work as well. Anyway, the most important part of all of this is the final cfcontent tag--without this, Spry will break as before, so be sure to include it.

Once I have returned this data in a scrubbed, Spry-friendly format, I do something like this:

function emailResult(request) {
var result = request.xhRequest.responseText;
var xmldom = Spry.Utils.stringToXMLDoc(result);
var verifyEmail=xmldom.getElementsByTagName("email");

Basically, this simply walks Spry through the returned XML data from the CFC and gets the expected value from the appropriate node (here, "email").

Like I said at the beginning, I make no claims that this is a best practice. However, it is a fairly painless way to quickly and easily return data from a component to the asynchronous goodness of Spry.

Let me know how this works out for you, and if you have any links to the code you're working on, I'd love to take a look at it.


tb
Demiurge
10 posts

October 18, 2007 at 07:25:11 AM
tb responds:

Thanks for getting back to me, your code and comments were very helpful.

So clearly I suck at life cause I am still not getting it to work.

Either I am not quite getting this, or its just not working. It seems to make sense to me, so I am probably overlooking something really stupid.

I have ripped my function apart and put it back together and now have thrown it out and am trying your code. Here is my component page which is a complete plagarism of your code...

<cfcomponent name="species" hint="A security datastore object" output="no">
<cfsetting enablecfoutputonly="yes" />

<cffunction name="InsertNavItem" access="public" output="false" returntype="string">

<cfsavecontent variable="verifyEmail">
<?xml version=""1.0"" encoding=""UTF-8""?>
<email id="no" />
</cfsavecontent>
<cfset verifyEmail = xmlParse(verifyEmail) >
<cfcontent type="application/xml; charset=UTF-8">
<cfreturn verifyEmail />
</cffunction>

</cfcomponent>

Here is my JS

function InsertSuccess(request) {

var result = request.xhRequest.responseText;
var xmldom = Spry.Utils.stringToXMLDoc(result);
var verifyEmail = xmldom.getElementsByTagName("email");
var emailText = document.getElementById('emailVerification');
var emailNode = verifyEmail.item(1);
if (emailNode.getAttribute("id") == "yes")
{
alert("Yes");
}
else
{
alert("No");
}
}

When I try to run this, Firebug throws me a nice little message that reads "emailNode has no properties". This leads me to believe that the creation of the XML is not working properly.

Any ideas?

Thanks again for your help.


tb
Demiurge
10 posts

October 18, 2007 at 07:32:08 AM
tb responds:

Thanks for getting back to me, your code and comments were very helpful.

So clearly I suck at life cause I am still not getting it to work.

Either I am not quite getting this, or its just not working. It seems to make sense to me, so I am probably overlooking something really stupid.

I have ripped my function apart and put it back together and now have thrown it out and am trying your code. Here is my component page which is a complete plagarism of your code...

<cfcomponent name="species" hint="A security datastore object" output="no">
<cfsetting enablecfoutputonly="yes" />

<cffunction name="InsertNavItem" access="public" output="false" returntype="string">

<cfsavecontent variable="verifyEmail">
<?xml version=""1.0"" encoding=""UTF-8""?>
<email id="no" />
</cfsavecontent>
<cfset verifyEmail = xmlParse(verifyEmail) >
<cfcontent type="application/xml; charset=UTF-8">
<cfreturn verifyEmail />
</cffunction>

</cfcomponent>

Here is my JS

function InsertSuccess(request) {

var result = request.xhRequest.responseText;
var xmldom = Spry.Utils.stringToXMLDoc(result);
var verifyEmail = xmldom.getElementsByTagName("email");
var emailText = document.getElementById('emailVerification');
var emailNode = verifyEmail.item(1);
if (emailNode.getAttribute("id") == "yes")
{
alert("Yes");
}
else
{
alert("No");
}
}

When I try to run this, Firebug throws me a nice little message that reads "emailNode has no properties". This leads me to believe that the creation of the XML is not working properly.

Any ideas?

Thanks again for your help.


tb
Demiurge
10 posts

October 18, 2007 at 07:32:22 AM
tb responds:

Thanks for getting back to me, your code and comments were very helpful.

So clearly I suck at life cause I am still not getting it to work.

Either I am not quite getting this, or its just not working. It seems to make sense to me, so I am probably overlooking something really stupid.

I have ripped my function apart and put it back together and now have thrown it out and am trying your code. Here is my component page which is a complete plagarism of your code...

<cfcomponent name="species" hint="A security datastore object" output="no">
<cfsetting enablecfoutputonly="yes" />

<cffunction name="InsertNavItem" access="public" output="false" returntype="string">

<cfsavecontent variable="verifyEmail">
<?xml version=""1.0"" encoding=""UTF-8""?>
<email id="no" />
</cfsavecontent>
<cfset verifyEmail = xmlParse(verifyEmail) >
<cfcontent type="application/xml; charset=UTF-8">
<cfreturn verifyEmail />
</cffunction>

</cfcomponent>

Here is my JS

function InsertSuccess(request) {

var result = request.xhRequest.responseText;
var xmldom = Spry.Utils.stringToXMLDoc(result);
var verifyEmail = xmldom.getElementsByTagName("email");
var emailText = document.getElementById('emailVerification');
var emailNode = verifyEmail.item(1);
if (emailNode.getAttribute("id") == "yes")
{
alert("Yes");
}
else
{
alert("No");
}
}

When I try to run this, Firebug throws me a nice little message that reads "emailNode has no properties". This leads me to believe that the creation of the XML is not working properly.

Any ideas?

Thanks again for your help.


Recent Posts
  • A Feedreader with Spry

    I know I've been posting alot about Spry lately, but the more I use it, the more I love it! Using the simple-to-implement tools which Spry provides, I am able to spend more time dealing with server-... [more]


  • Making All Things New

    And He who sits on the throne said, "Behold, I am making all things new."

    In an age in which global warming, climate change and concerns about the viability of our planet's environment ar... [more]


  • Quick Post: blogactionday.org Reminder

    Just a quick reminder: blogactionday (October 15) is approaching rapidly--next Monday! This is a day where bloggers are encouraged to devote a posting to the environment, regardless of their views o... [more]


  • Application Framework Infidelity

    On the whole, I tend to be fairly loyal in media technologies. I do what I can to support Adobe --I use ColdFusion, the more]


  • CD Review - Emery's "I'm Only a Man"

    Within the last six months, my little brother has introduced me to some seriously cool music. One of my favorite bands from this "education" is Emery, a hard-hitting "screamo" band hailing from the ... [more]


  • A Little Taste of Spry 1.6 Goodness

    A few days I blogged about Adobe's release (and sexy-fication) of the javascript framework Spry 1.6. While I have not had a lot of time to play around with the absolutely unique elements, some cool ... [more]


  • Peacocke Tuesday - Randomness and Causality

    Over the last week, I have rolled through several chapters of Peacocke's book, "Theology for a Scientific Age," and I will not spend time going over the finer details of each discussion. I simply wi... [more]


  • A New Day, A New Spry

    Recently, I have blogged about how incredibly cool Adobe's javascript framework---Spry---is and what potential it has for making great dynamic web content. In those posts, I was talking about Spry p... [more]


  • A New Communion Experience

    Growing up in the Wesleyan Church, I've not had tremendously moving experiences with the celebration of communion. In the Wesleyan Church--as in many others--communion is served (by Discipline requi... [more]


  • CD Review - Rascal Flatt's "Still Feels Good"

    Since their self-titled debut in 2000, Rascal Flatts has consistently and with ever-growing force become a presence within the country-pop crossover music scene to be reckoned with. Boasting such awa... [more]


Extra Stuff
/*/ About Me

Welcome to my blog. I am often asked what "Exist/Dissolve" means. Well, that is certainly a good question, and I am currently in the process of discovering the answer myself. Prima facie, it strikes me as encapsulating the existensial crisis that is our lives as finite, contingent beings. For a brief moment, we exist, and the next we dissolve into the nothingness of non-existence. From a theological perspective, it is, for me, a sort of ad hoc apologetic for resurrection - i.e., if to exist/dissolve is the human dilemma, there is nothing inherent to the person that guarantees existence, either now or "after" death. Therefore, resurrection is at the same time both the height of absurdity (for it is a notion entirely alien to the paradigm of existence to which we are naturally enculturated) and the only hope for the human to persevere beyond the pale of death.

Do Something

/*/ My Awards

/*/ Non-Hostiles
/*/ Browser Happiness
/*/ Syndication

Syndicated feed of existdissolve.com

Semi-often podcast
(coming soon!)