Tuesday, August 21, 2007

cfajaxproxy, Application.cfm, and pain

Recent experience has taught me that if you use cfajaxproxy on a page, and your Application.cfm page outputs anything to the screen, you will get a JavaScript error when you try to call any of the JavaScript functions that call proxy methods. So, for instance, consider the following (which gives an error):

<!-------- Application.cfm -------->
<cfapplication name=""blah">

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<!-------- End Application.cfm -------->



<!-------- Component.cfc -------->
<cfcomponent>

<cffunction name="test_proxy" access="remote" returntype="string">

<cfreturn "success" />

</cffunction>

</cfcomponent>
<!
-------- End Component.cfc -------->



<!-------- index.cfm -------->
<cfajaxproxy
cfc="component"
jsclassname="component_proxy">

<cfoutput>

<script>

var js_component_proxy = new component_proxy();

function js_test_proxy() {
alert(js_component_proxy.test_proxy());
}

</script>

<input type="button" name="a_button" value="Click Me" onclick="js_test_proxy();" />

</cfoutput>
<!-------- End index.cfm -------->

If you run index.cfm?cfdebug, and click the little button, the following error will appear in the little debug window:

window:global: parseJSON

This error took a long time to isolate, and a while to fix. I don't know of anywhere in the livedocs where this is documented. However, if you have any output in your Application.cfm, even a doctype declaration, beware!!!

Incidentally, we did find a moderately difficult fix. If you are interested in knowing how we fixed this problem, post a comment, and I will post the fix.

14 comments:

David McGuigan said...

I was so freaking frustrated until I read your blog (found it through a Google search). SAVED MY LIFE.

When I'm sketching up rapid application prototypes sometimes I include some rudimentary styles in the onRequestStart function of Application.cfc, sure as hell, that was breaking it.

What kind of fix did you guys come up with? Feel free to email me davidmcguigan AYATT gmail.

Thanks so much!!!

seancorfield said...

I would point out that it's considered bad practice to unconditionally output html in Application.cfm or Application.cfc.

If you think about how AJAX works, it should be obvious that the result of the request to the server must consist of just the valid JSON-encoded data.

If you're working with a framework, you need to ensure debug is turned off for AJAX requests.

David McGuigan said...

This seems like a quick fix:
Test the cgi.path_info variable for a '.cfc' extension (or .cfm if you prefer) from within Application.cfc, and only output the shared content if applicable.

For example:

!--- If it's a non-AJAX request, output the centralized content ---
openCfif right(cgi.path_info, 4) neq '.cfc'

!--- Output the shared resources ---

closeCfif

Dev Lord said...
This comment has been removed by the author.
Dev Lord said...

Oh my god, how to solve it ? I am struggled and tormented by this problem almost a month. In fact, I do not output anything in the Application.cfc but do the following in the onRequestStart() :

cfif Not IsUserLoggedIn()
cfinclude template="login.cfm"
cfif

Why cfinclude would be a problem ? Is that outputing something I don't know ? Any ideas would be appreciated.

David McGuigan said...

Including login.cfm counts as outputting. The .cfm file is output to the screen.

Look at my post right above yours. All you need to do is test to see if the request is for a .cfc file and if so ignore the cfinclude stuff. Your application will still work as expected.

Dev Lord said...

I have tried to use

cfif listlast(cgi.path_info,".") is "cfc"

to detect whether the request is for a cfc, however, it still not works for me. Any ideas ?

David McGuigan said...

That code should work. Post a bigger chunk of the code maybe?

Josh said...

I just ran into the problem and it is very frustrating. I have tried running it with an application.cfm file that has no sort of output and I still get the same issue.

What is really frustrating to me is if I load up a backup copy from two days ago it works just fine, though the current version does not. The Javascript that controls it has not changed in that time, only the CFC that is being called and some form elements in the CFM file.

If possible, could you please email me the fix you used to resolve the problem at regenshire (at) gmail.

Thank you.

Josh said...

I figured out the cause for the issue in my case. I had 2 dash Comments (ie < !-- ) in my CFC file.

When these were removed or changed to 3 dash Comments (ie < !---) I no longer experienced the issue.

David McGuigan said...

Nice work. HTML comments still count as output as far as breaking AJAX applications is concerned unfortunately.

Smapty said...

Hey, I just spent the last 4 hours trying to figure out what was going on.

This thread gave me the idea to just put an empty application.cfc file in the "CFC" directory and just like that... its working again.

Wasn't about to stop using my main application.cfc file for building headers and footers.

David McGuigan said...

Well, that's actually a quick and dirty solution that Sean Corfield came up with early on to solve the problem. Where that'll give you problems is when you want to access application or session-scoped variables, because the CFCs you're making calls to will have their own application and session scope (if sessions are even enabled). So, as posted earlier, the easiest, best solution I've come across so far is to test for the type of request (if it's a CFC, don't output the header/footer/whatever other shared content, otherwise do).

Basically all you do is wrap your header/footer/other shared content in a condition like this:

!--- If it's a non-AJAX request, output the shared content ---
cfif right(cgi.path_info, 4) neq '.cfc'

cfinclude

/cfif

Anonymous said...

Thank you guys for the fix .It works perfectly fine.
Awesome fix.