Posted March 28, 2007 at
7:40 AM
Tags:
ColdFusion
I am a Huge fan of jQuery. I love the method chaining and the ability to apply methods to each element in a jQuery collection object. I was wondering if I would want ColdFusion to be able to do that. When I started to experiment, I realized that ColdFusion has two limitations that stop it from being like jQuery:
- The biggest limitation is simply the ability to create on-the-fly, nameless functions. It can be done, but it's hard and not very elegant. These anonymous functions are half of what make jQuery (and Javascript in general) so amazingly powerful.
- ColdFusion does not have a very solid structure like the browser's Document Object Model (DOM) over which actions can be taken. This is not a limitation of ColdFusion itself, but merely a fact of the sever-side world.
But, I figured there could be jQuery-like structures for ColdFusion. These turned out to be more like data type wrappers, but still, I thought they were very interesting. Here is a demo:
Launch code in new window » Download code as text file »
- <cfset objValues = StructNew() />
- <cfset objValues[ "testa" ] = "likes" />
- <cfset objValues[ "testb" ] = "Nancy." />
-
-
- <cfset arrValues = ListToArray( "Nancy,is" ) />
-
-
- <cfset $List = CreateObject( "component", "$List" ).Init() />
-
-
- #$List.Prepend( "Ben" )
- .Append( "naughty" )
- .InsertAt( 2, objValues )
- .InsertAt( 4, arrValues )
- .Each( EachHandler )
- .ToString()#
-
-
- <cffunction
- name="EachHandler"
- access="public"
- returntype="string"
- output="false"
- hint="Handles manipulation of each list item. Must return the item.">
-
- <cfargument
- name="Value"
- type="string"
- required="true"
- />
-
- <cfreturn REReplace(
- ARGUMENTS.Value,
- "^([a-z])",
- "\U\1",
- "ONE"
- ) />
- </cffunction>
The 6 chained method calls on $List result in the following output:
Ben,Likes,Nancy.,Nancy,Is,Naughty
This demonstrates an Each method that takes a list value and returns an updated value. However, just as in jQuery / Javascript, there is no requirement for the Each method to actually update the list. In this example, we can use the Each method as an iterator that populates a global, REQUEST-scoped value array:
Launch code in new window » Download code as text file »
- <cfset REQUEST.Values = ArrayNew( 1 ) />
-
- <cfset $List.Each( EachHandler2 ) />
-
-
- <cfdump
- var="#REQUEST.Values#"
- label="Global Value Array"
- />
-
-
- <cffunction
- name="EachHandler2"
- access="public"
- returntype="void"
- output="false"
- hint="Takes each value and adds it to a REQUEST-scope array.">
-
- <cfargument
- name="Value"
- type="string"
- required="true"
- />
-
- <cfset ArrayAppend( REQUEST.Values, ARGUMENTS.Value ) />
-
- <cfreturn />
- </cffunction>
CFDumping out the REQUEST.Values array, we get:
I like the idea. I think it has some merit, I am just not sure that ColdFusion is the proper place to apply this type of methodology. Does anyone have any thoughts on this type of thing?
Here is the code that created the $List.cfc ColdFusion component / jQuery-like data wrapper:
Launch code in new window » Download code as text file »
- <cfcomponent
- output="false"
- hint="Creates functionality around a list.">
-
-
- <cfset VARIABLES.Instance = StructNew() />
-
- <cfset VARIABLES.Instance.Values = ArrayNew( 1 ) />
-
- <cfset VARIABLES.Instance.Delimiter = "," />
-
-
- <cfset THIS.ToString = THIS.$ToString />
-
-
-
- <cffunction
- name="Init"
- access="public"
- returntype="any"
- output="false"
- hint="Returns an initialized list object.">
-
- <cfset var LOCAL = StructNew() />
-
- <cfset VARIABLES.Instance.Values = ArrayNew( 1 ) />
-
- <cfset VARIABLES.Instance.Delimiter = "," />
-
-
- <cfif (
- StructKeyExists( ARGUMENTS, "2" ) AND
- Len( ARGUMENTS[ 2 ] )
- )>
-
- <cfset VARIABLES.Instance.Delimiter = ARGUMENTS[ 2 ] />
-
- </cfif>
-
-
- <cfif StructKeyExists( ARGUMENTS, "1" )>
-
- <cfset THIS.Append(
- Values = ARGUMENTS[ 1 ],
- Delimiters = VARIABLES.Instance.Delimiter
- ) />
-
- </cfif>
-
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="Append"
- access="public"
- returntype="any"
- output="false"
- hint="Appends list items to the list.">
-
- <cfargument
- name="Values"
- type="any"
- required="true"
- hint="These are the values that you are appending. Can be list, array, struct."
- />
-
- <cfargument
- name="Delimiters"
- type="string"
- required="false"
- default="#VARIABLES.Instance.Delimiter#"
- hint="The characters use for list delimiters."
- />
-
-
- <cfset VARIABLES.Instance.Values.AddAll(
- THIS.ToValueArray(
- ArgumentCollection = ARGUMENTS
- )
- ) />
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="Delete"
- access="public"
- returntype="any"
- output="false"
- hint="This takes values (list, array, struct) and deletes them from the list.">
-
- <cfargument
- name="Values"
- type="any"
- required="true"
- hint="These are the values that you are deleting. Can be list, array, struct."
- />
-
- <cfargument
- name="Delimiters"
- type="string"
- required="false"
- default="#VARIABLES.Instance.Delimiter#"
- hint="The characters use for list delimiters."
- />
-
-
- <cfset VARIABLES.Instance.Values.RemoveAll(
- THIS.ToValueArray(
- ArgumentCollection = ARGUMENTS
- )
- ) />
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="Each"
- access="public"
- returntype="any"
- output="false"
- hint="Applies the given function to each list item.">
-
- <cfargument
- name="Method"
- type="any"
- required="true"
- />
-
- <cfset var LOCAL = StructNew() />
-
- <cfloop
- index="LOCAL.Index"
- from="1"
- to="#VARIABLES.Instance.Values.Size()#"
- step="1">
-
- <cfset LOCAL.Value = ARGUMENTS.Method(
- VARIABLES.Instance.Values[ LOCAL.Index ]
- ) />
-
- <cfif StructKeyExists( LOCAL, "Value" )>
-
- <cfset VARIABLES.Instance.Values[ LOCAL.Index ] = LOCAL.Value />
-
- </cfif>
-
- </cfloop>
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="InsertAt"
- access="public"
- returntype="any"
- output="false"
- hint="Inserts the given values at the given index.">
-
- <cfargument
- name="Index"
- type="numeric"
- required="true"
- hint="The index at which to insert the values. LTE 1 will prepend."
- />
-
- <cfargument
- name="Values"
- type="any"
- required="true"
- hint="These are the values that you are inserting. Can be list, array, struct."
- />
-
- <cfargument
- name="Delimiters"
- type="string"
- required="false"
- default="#VARIABLES.Instance.Delimiter#"
- hint="The characters use for list delimiters."
- />
-
-
- <cfset var LOCAL = StructNew() />
-
-
- <cfset LOCAL.Values = THIS.ToValueArray(
- ArgumentCollection = ARGUMENTS
- ) />
-
-
- <cfif (ARGUMENTS.Index LTE 1)>
-
- <cfreturn THIS.Prepend(
- ArgumentCollection = ARGUMENTS
- ) />
-
- <cfelseif (ARGUMENTS.Index GT VARIABLES.Instance.Values.Size())>
-
- <cfreturn THIS.Append(
- ArgumentCollection = ARGUMENTS
- ) />
-
- <cfelse>
-
- <cfset LOCAL.PreValues = VARIABLES.Instance.Values />
- <cfset LOCAL.PostValues = VARIABLES.Instance.Values />
-
-
- <cfloop
- index="LOCAL.Index"
- from="#VARIABLES.Instance.Values.Size()#"
- to="#ARGUMENTS.Index#"
- step="-1">
-
- <cfset ArrayDeleteAt(
- LOCAL.PreValues,
- LOCAL.Index
- ) />
-
- </cfloop>
-
-
- <cfloop
- index="LOCAL.Index"
- from="#(ARGUMENTS.Index - 1)#"
- to="1"
- step="-1">
-
- <cfset ArrayDeleteAt(
- LOCAL.PostValues,
- LOCAL.Index
- ) />
-
- </cfloop>
-
-
- <cfset LOCAL.PreValues.AddAll(
- LOCAL.Values
- ) />
-
- <cfset LOCAL.PreValues.AddAll(
- LOCAL.PostValues
- ) />
-
- <cfset VARIABLES.Instance.Values = LOCAL.PreValues />
-
- <cfreturn THIS />
-
- </cfif>
- </cffunction>
-
-
- <cffunction
- name="Prepend"
- access="public"
- returntype="any"
- output="false"
- hint="Prepends list items to the list.">
-
- <cfargument
- name="Values"
- type="any"
- required="true"
- hint="These are the values that you are prepending. Can be list, array, struct."
- />
-
- <cfargument
- name="Delimiters"
- type="string"
- required="false"
- default="#VARIABLES.Instance.Delimiter#"
- hint="The characters use for list delimiters."
- />
-
-
- <cfset var LOCAL = StructNew() />
-
-
- <cfset LOCAL.Values = THIS.ToValueArray(
- ArgumentCollection = ARGUMENTS
- ) />
-
- <cfset LOCAL.Values.AddAll( VARIABLES.Instance.Values ) />
-
- <cfset VARIABLES.Instance.Values = LOCAL.Values />
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="$ToString"
- access="public"
- returntype="string"
- output="false"
- hint="Returns a string representation of the list.">
-
- <cfreturn ArrayToList(
- VARIABLES.Instance.Values,
- VARIABLES.Instance.Delimiter
- ) />
- </cffunction>
-
-
- <cffunction
- name="ToValueArray"
- access="public"
- returntype="array"
- output="false"
- hint="Takes some sort of value (list, array, struct), and returns an array of values.">
-
- <cfargument
- name="Values"
- type="any"
- required="true"
- hint="These are the values that are being converted to an array. Can be list, array, struct."
- />
-
- <cfargument
- name="Delimiters"
- type="string"
- required="false"
- default="#VARIABLES.Instance.Delimiter#"
- hint="The characters use for list delimiters."
- />
-
-
- <cfset var LOCAL = StructNew() />
-
-
- <cfif IsSimpleValue( ARGUMENTS.Values )>
-
- <cfset LOCAL.Values = ListToArray(
- ARGUMENTS.Values,
- ARGUMENTS.Delimiters
- ) />
-
- <cfelseif IsArray( ARGUMENTS.Values )>
-
- <cfset LOCAL.Values = ARGUMENTS.Values />
-
- <cfelseif IsStruct( ARGUMENTS.Values )>
-
- <cfset LOCAL.Values = ArrayNew( 1 ) />
-
- <cfloop
- item="LOCAL.Key"
- collection="#ARGUMENTS.Values#">
-
- <cfif IsSimpleValue( ARGUMENTS.Values[ LOCAL.Key ] )>
-
- <cfset ArrayAppend(
- LOCAL.Values,
- ARGUMENTS.Values[ LOCAL.Key ]
- ) />
-
- </cfif>
-
- </cfloop>
-
- <cfelse>
-
- <cfset LOCAL.Values = ArrayNew( 1 ) />
-
- </cfif>
-
-
- <cfreturn LOCAL.Values />
- </cffunction>
-
- </cfcomponent>
Download Code Snippet ZIP File
Comments (9) |
Post Comment |
Ask Ben |
Permalink |
Other Searches |
Print Page
What Other People Are Searching For
[ local search ]
coldfusion jquery demo
[ local search ]
using jquery in coldfusion
JQuery is fun, no doubt. Certainly returning the jQuery object allows some great chaining and very concise syntax.
Your experiment with CF in a jQuery style is interesting though to be true, I'd be happy if I could define arrays and structs using a native notation rather than a series of tags.
Ex.
aStruct{ key1: 'value1', key2: 'value2' }
anArray[ 'one', 'and', 'uh', 'two', 'and', 'uh']
Posted by Dan Wilson
on Mar 28, 2007
at 9:00 AM
"The 6 chained method calls on $List result in"
code that is impossible to read.
Sorry, but I just don't like the syntax, and can't see the benefit.
Yes, having a 'for each' loop construct would be nice, but no more than that.
Posted by Tom Chiverton
on Mar 28, 2007
at 9:09 AM
Ben--you are a madman and I mean this as a compliment.
Posted by Rob Symonds
on Mar 28, 2007
at 9:27 AM
@Dan:
Here are 2 functions that give you almost what you want:
<cffunction name="arrayCreate" returntype="array" output="false">
<cfset var aReturn = arrayNew(1) />
<cfset var i = 0 />
<cfloop index="i" from="1" to="#arrayLen(arguments)#">
<!---//
you might want to using duplicate(arguments[i])
if you plan on using complex array items
//--->
<cfset arrayAppend(aReturn, duplicate(arguments[i])) />
</cfloop>
<cfreturn aReturn />
</cffunction>
<cfdump var='#arrayCreate("one", "two", "three", "four")#'>
<cfdump var='#arrayCreate()#'>
<cffunction name="structCreate" returntype="struct" output="false">
<cfset var stReturn = structNew() />
<cfset var sKey = 0 />
<cfloop item="sKey" collection="#arguments#">
<cfset stReturn[sKey] = duplicate(arguments[sKey]) />
</cfloop>
<cfreturn stReturn />
</cffunction>
<cfdump var='#structCreate(key1="value1", key2="value2")#'>
<cfdump var='#structCreate()#'>
Posted by Dan G. Switzer, II
on Mar 28, 2007
at 10:39 AM
@Dan,
I'm with you. I hate typing in TONS of tags to programmatically build a structure or array.
You could easily define you're arrays and structures in JSON format and avoid all the tags by using the JSON encode/decode library for CF to convert the JSON notation to a native CF data structure.
I've thought about doing this for a project I'm working on. I haven't done it yet, but there's on reason it shouldn't work.
CFJSON
http://www.epiphantastic.com/cfjson/
Posted by Kurt Bonnet
on Mar 28, 2007
at 1:49 PM
@Kurt:
I'd certainly stay away from using a string to define native CF variables. String parsing is notoriously slow in Java/CF.
Posted by Dan G. Switzer, II
on Mar 28, 2007
at 5:21 PM
@Tom,
I agree with you. At first the jQuery method chaining is awesome. But then, I quickly realize that I am not sure how to best format it.... and to me, when I don't know how to best format the code itself, that is red flag that it needs to be much more simple.
In jQuery, I tend to create the jQuery object and keep a reference to it. Then, I just make multiple calls on it:
o = $( "..." );
o.attr();
o.attr();
o.each();
I find not only is this easier to read, it actually pays off in the end because in the Each() methods, I now have a reference to stuff that might be needed later.
@Rob,
:) The mad scientist is always hard at work.
@Dan G.,
Good stuff. I would like to experiment with that.
@Kurt,
I am with Dan on this one. Plus, by using a string, you are forced to only use data structures that can fit into a string value.
Posted by Ben Nadel
on Mar 28, 2007
at 6:02 PM
Mostly, I just want the EACH() ability. But since ColdFusion is compiled, I guess this is not possible - seems to only be possible in interpreted scripting languages.
Posted by Ben Nadel
on Mar 28, 2007
at 6:02 PM
@Dan and @Ben,
My point was simply that sometimes there's a need to build a data structure within CF for whatever reason and that as long as all you're storing at the very end nodes of the data structure is simple data such as strings, JSON is a very clean and simple way to do it. It requires MUCH less typing than a bunch of CFSETs and is MUCH easier to understand.
Yes, string parsing is slow, but I'm guessing that most places this technique would be used is to convert a JSON string that never changes into a native CF value. In this case you could simply cache the data structure after it was initially parsed and decoded.
Sorry I wasn't clearer earlier.
Posted by Kurt Bonnet
on Apr 1, 2007
at 11:44 PM
Post Comment |
Ask Ben