3

I would like to send a ColdFusion structure to Javascript. In looking at how I might do this I found the cfwddx tag. It seems to get me partway there. I created a test program

<head>
<script type = "text/javascript" src = "frag3.js"> </script>
</head>
<body>

<cfset str = StructNew()>
<cfset str['betty'] = 1>
<cfset str['nancy'] = 2>
<cfdump var = "#str#">

<cfwddx action="CFML2JS"
    input="#str#"
    toplevelvariable="xyz"
    output = "genstr">

<cfoutput>
<script type = "text/Javascript">
test('betty')
function test (arg) {
alert("got to test");
#genstr#
alert ("xyz " + xyz[arg]);
}
nothing()
</script> 
</cfoutput>

The cfdump gives the correct answer

output of cfdump

When I look at the output of #genstr# it contains the correct javascript code:

xyz = new Object(); xyz["betty"] = "1"; xyz["nancy"] = "2"; 

When I run the code inline as shown above, the alert gives the right answer. And when I invoke the "nothing()" function, which resides on frag3.js, an alert shows that it ran, so the src statement is working.

However, when I move the function test(arg) itself to frag3.js, it does not run.

Can anyone tell my how to get "test" working as an external function, instead of just inline in my ColdFusion code? BTW, I have not learned jquery, so jquery answers will not help much; I need basic javascript.

James A Mohler
  • 11,060
  • 15
  • 46
  • 72
Betty Mock
  • 1,373
  • 11
  • 23

3 Answers3

1

Instead of writing the variable bits (let's call them "initialization" or "configuration") to the source code of your website (or worse, to the source code of your otherwise static JS files), I recommend the following.

Create a component that has a remote-callable method which returns a struct with all the configuration. You can now call that method from ColdFusion code on the server (where you will receive a struct) and, via Ajax, from JavaScript code on the client (where you will receive JSON).

/** Config.cfc (or call it something similar)
  *
  */

component { 
    remote struct function getConfig() returnformat="JSON" {
        var str = StructNew();
        str['betty'] = 1;
        str['nancy'] = 2;
        return str;
    } 
}

your main.cfm (for the sake of simplicity of the example I include jQuery for handling the Ajax request):

<html>
<head>
  <script type="text/javascript" src="frag3.js"></script>
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
  <h1>Config, as seen by the server</h1>
  <cfinvoke component="Config" method="getConfig" returnvariable="cfg">
  <cfdump var="#cfg#">

  <h1>Config, as seen by the client</h1>
  <pre id="cfgDump"></pre>

  <script type="text/javascript">
  $(function () {
      $.get("Config.cfc?method=getConfig").done(function (cfg) {
          // cfg already is a regular JS object at this point
          // convert to JSON again for display purposes
          $("#cfgDump").text( JSON.serialize(cfg, null, 2) );
      });
  });
  </script>
</body>
</html>

Clean and simple.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • He said it was a test page. Using hard coded values in these circumstances is appropriate. It simpliflies things and allows him to focus on the part causing him difficulty. – Dan Bracuk Jan 04 '15 at 14:09
  • True. I couldn't resist lining out a "proper" approach, though. – Tomalak Jan 04 '15 at 14:15
  • proper on the client side would also include setting `$.ajax` dataType either as `$.get` argument or using `$.getJSON` – charlietfl Jan 04 '15 at 15:02
  • @charlietfl Not necessary. jQuery will handle that. ColdFusion will set the `Content-Type` to `application/json` and jQuery will pick it up, no need to fiddle with the Ajax configuration at all. Besides, `getJSON` is really only necessary for JSONP requests, which we don't have here. And besides *that*, I was trying to make a point about how to design the server side, my use of jQuery is entirely coincidental. – Tomalak Jan 04 '15 at 15:13
  • 1
    This looks promising to me, but I am unfamiliar with so much of it, it is going to take me some time to try it all out. I'll be back to you later with results. Thanks. – Betty Mock Jan 04 '15 at 18:57
0

The most effective solution to your problem is probably going to be including the dynamic content inline in the document itself.

When you copy the function test(arg) to the Javascript file, I suppose you're copying #genstr# as well? So it looks, in the js file, exactly as you have it above?

This is working as intended. .js files do not process cold fusion, and if you look at your developer console, you're probably getting a syntax error.

You can either keep it inline to the document, or change the extension of the js file to .cfm.

At this point, your js_main.cfm (example) file will be treated like any other browser-requested file, and it will drop headers and footers in if your request processing does that, so it will cause more errors. You need to protect these files from that. You can either do this with some cfifs in your application file like

<!--- This checks the path to make sure it doesn't contain livemedia. --->
<cfif not listfind(cgi.script_name,"livemedia","/")>
  ... do includes
</cfif>

Or you can do like I would probably do and lock these files in a folder just for serving JS, CSS files with cfm extension with their own application.cfc/cfm to override the site's global file, if I ran into a situation where I felt that a dynamically geerated js/css was truly the best option.

You can then do as you show in your OP and relay variables to the client side if you really need to. Alternatively, you can name the two "applications" the same, and while that makes data sharing really easy, I should still advise that you be careful what you share and you expose and making sure to properly secure the sub-application.

And yes, as others have suggested, #SerializeJSON()# is the modern approach to copying a server-side variable/object to the client side.

For instance..

<cfset CFStruct = {big = "little", red = "blue", subArray = ["A","B","C","D"]} />
<script>
  <cfoutput>JSObj = #SerializeJSON(CFStruct)#;</cfoutput>
</script>
<!--- While I use shorthand to form the struct and subArray, that's only for speed,
  structs, arrays, queries, etc formed in any fashion work just fine. --->

Creates JSObj with the elements big, red, subArray (which has the child elements A, B, C, D).

Still, no matter which method you choose (wddx or SerializeJSON()), these will not work in a .js file.

I must mention that the other option of allowing the server to parse .js files is there but this would be a lot more pain than it is worth. The many occasions where you'd use a plain js file would be interfered with.

Note that your files called via client side tags like <script> and <link> are entirely seperate requests. They will not share the url, form, request, variables scopes. However they can share cookies and client and session scope if the two applications have the same name.

Lastly the wonderful thing about external content called via html tags, regardless of extension, is that it's usually cached by default. This is wonderful for bandwidth and even for speed loading the pages, until you have a scenario where you want to load a non cached-copy consistently.

For this reason, when you feel like you have to dynamically generate something that you want to stick in an external file, you have a few methods.

  1. You can set no-cache in the header (CF or Htaccess can do this).
  2. You can provide toss a random number in the query string of the script call. <script src="/livemedia/js_main.cfm?randomizer=#randrange(1,1000000)#"></script>
  3. Perhaps the file doesn't need refreshed every time for every user, maybe simply that each user needs to see a different copy. You would still attach a query string because multiple users may log in from the same computer. It, however, would be user specific. <script src="/livemedia/js_main.cfm?#session.username#"></script>
  4. Reconsider how your page compiles and store the data that needs to be live inline in the page.

If you are attaching something to the query string of the javascript file, you should move the static contents of the file to a seperate file that doesn't need regenerated each time. There is no need to reload 15 javascript functions with each page request because 1 contains dynamic content.

Regular Jo
  • 5,190
  • 3
  • 25
  • 47
  • I'm going to start by trying the .cfm approach -- that may be the simplest. I must in fact regenerate the structure each time, and i can't count on things staying the same. At the moment I have one javascript function only in the external file. – Betty Mock Jan 04 '15 at 19:01
-1

I'm not a big CF developer but the simplest way I know to output structs and arrays for javascript consumption is as JSON using SerializeJson()

For example as a JS variable within a CF page:

<script>
    var myVar = <cfoutput>#SerializeJson(str)#</cfoutput>;
</script>

Although JSON is a string ... when it prints within the script tag, javascript will read it as array or object without any need for parsing it in client.

I use exact same output for ajax delivery also

charlietfl
  • 170,828
  • 13
  • 121
  • 150