Sunday, 27 April 2014

ColdFusion 11: <cfclient>... how does normal CFML code and <cfclient> code interact?

G'day:
Another quick one. I'm raising these quick-fire questions here because Adobe have declined to suggest a better place to raise them, other than as comments on one of their blog entries. Well that was Ram's suggestion (which I don't think is terribly-well thought out). He declined to react to my suggestion that the Adobe ColdFusion forums might be a good place. Anit suggested Twitter or just emailing him, but I think there'd be public interest in this stuff, so don't want to resort to email.

As I'm the master of what goes on on this blog: I'll clutter this thing up.

Say I want to have a mix of "normal" CFML and <cfclient>-based CFML in the same file. I can only presume the intent is to allow this, otherwise having <cfclient> as a tag rather than just a file extension seems like a poor approach. Obviously if one can have a start tag and an end tag, then code can come before (and I guess after) the <cfclient> tags themselves.

So I'd expect this to work:

<cfset message = "G'day World">
<cfclient>
<cfoutput>#message#</cfoutput>
</cfclient>

However all I get is an error in JS:


  1. Uncaught ReferenceError: message is not defined variablesScopeVariable.cfm:4

And, indeed, the only mention of message in the JS source is the one that's erroring (as it's on the right-hand side of an assignment).

So I thought perhaps <cfclient> worked like <cfthread> and I needed to pass attributes into it:

<cfset message = "G'day World">
<cfclient message="#message#">
<cfoutput>#message#</cfoutput>
</cfclient>

This doesn't compile:

Attribute validation error for the client tag.

The tag does not have an attribute called message. The valid attribute(s) are ''.
ColdFusion cannot determine the line of the template that caused this error.This is often caused by an error in the exception handling subsystem.

Note also there's an error in the error message itself. It's not the <client> tag, it's the <cfclient> tag.

Rightio then, so I tried just using the request scope instead (the code's the same as the variables-scoped example, except using the request scope). No dice: same JS error.

As a last ditch effort, I just tried to see if <cfclient> was aware of anything going on around it, by passing a value on the URL, and seeing if <cfclient> saw that, eg:

<cfclient>
<cfoutput>#URL.message#</cfoutput>
</cfclient>

This behaved differently from the variables- / request- scoped examples, in that I didn't get a JS error, I just got this on the screen:

undefined

And no JS error. It pains me to have to do this, but let's look at the generated JS to see why the behaviour is different:

Variables scope example:

<script type="text/javascript" src="/CFIDE/cfclient/cfclient_main.js"></script>
<script type="text/javascript" src="/CFIDE/cfclient/cffunctions.js"></script>
<meta name="viewport" content="width=device-width">
<script type='text/javascript'>
globalDivStruct=null;
var _$variablesScopeVariable_func=function(){
    var self=this;
    var variables={};
    self.__init=function(){
        var localdivstruct=globalDivStruct;
        var __output_var="";
        var tmpVarArray={};
        localdivstruct.outputvar+=message;
        return""
    }
};
function __startPage__$variablesScopeVariable(){
    document.write("\x3cdiv id\x3d'__cfclient_0'\x3e\x3c/div\x3e");
    window.ispgbuild=false;
    var clientDivStruct={
        divId        : "__cfclient_0",
        outputvar    :""
    };
    globalDivStruct=clientDivStruct;
    try{
        _$variablesScopeVariable=new _$variablesScopeVariable_func;
        _$variablesScopeVariable.__init()
    }
    catch(__eArg){
        if(__eArg!=="$$$cfclient_abort$$$")
            throw __eArg;
    }
    __$cf.__flush(clientDivStruct)
}
__startPage__$variablesScopeVariable();
</script>

The only significant difference (other than function names, based on the file names) between this and the URL-scoped example is the indicated line above is replaced by this in the URL example:

localdivstruct.outputvar+=__$cf.__arrayGet(URL,"message",true);

So it's like it's trying to do the right thing, but just failing. I thought it might be because CF does stupid thinks with scope-key casing, and changed the <cfclient> code to expect URL.MESSAGE not URL.message, but this didn't work either.

So I'm flummoxed. I can't find anything in any documentation which might point me in the right direction, so anyone know what the story is here?

Update:

At Joel's suggestion I tried this:
<script>
message = "G'day World";
</script>
<cfclient>
<cfoutput>#message#</cfoutput>
</cfclient>

This worked. Which elicits from me a mixture of "heh: cute" and "this is an abomination".

--
Adam