3

I know similar questions have been asked before and answered, but I'm having specific issues with my code. When the dropdown with id="SITE" is changed, I want the dropdown with id="YARD" to fill with yards from that site. Here is my component CFC code (in a page called AjaxFunctions.cfc):

<cfcomponent output="false">
<!---  Get YARDS dataset based on SITE  --->
    <cffunction name="getYards" access="remote" returntype="query" />
    <cfargument name="Site" type="string" required="true" /> 
    <cfquery name="rs_Yards" datasource="abc" username="notReal1" password="notReal2" >
        <!--- SQL code here --->
    </cfquery>
    <cfreturn rs_Yards />
    </cffunction>
 <cfcomponent>

And here is my receiving code in the head section of my calling page:

<script>
    $(document).ready(function() {
        $("#SITE").change(function() {
            alert( "SITE has changed." );// testing to see if jQuery works
            // empty YARD dropdown
            $("#YARD").empty();
            // perform ajax
            $.ajax({
                type: "GET",
                url: "AjaxFunctions.cfc",
                data: {
                    method: "getYards",
                    Site: $("#SITE").val()
                },
                datatype: "json",
                success: function(data) {
                    alert("We received the data."+data);
                    $.each(data, function () {
                        $("#YARD").append($("<option></option>").val(this['ITEMID']).html(this['ITEMDESC']));
                    }                   
                }
            });
        });
    });
</script>

Whey I try the code as is, absolutely nothing happens. When I comment out just these lines

$.each(data, function () {
    $("#YARD").append($("<option></option>").val(this['ITEMID']).html(this['ITEMDESC']));
} 

Then I get the notification "the SITE has changed" and the YARD dropdown empties, but the "We received the data..." alert looks like HTML code from an error page. I guess I can worry about updating the YARD dropdown later, right now I'm worried about just receiving the data from the query.

Todd K
  • 73
  • 1
  • 13
  • 1
    Please share what the error HTML code is that you are receiving. What happens when you just navigate directly to your CFC from the browser? – Miguel-F Mar 23 '18 at 15:09
  • 1
    *alert looks like HTML code from an error page* Try calling the CFC directly in the browser, write the response to `console.log` or use your browser's javascript console to view the response. Other issues that come to mind. 1. Should be `dataType: "json"` (capital "T") 2. The CF function doesn't return JSON. One way is request it in the url: `AjaxFunctions.cfc?method=yourCFFunctionName&returnformat=json` 3. The jquery code is expecting an array of structures. That's not what the CF function returns. – SOS Mar 23 '18 at 15:13
  • @Miguel-F When I call the page AjaxFunctions.cfc directly in a browser, I get a "404 error:404 - File or directory not found. The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable." The URL changed though, to one referencing "cfcexplorer.cfc?method=getcfcinhtml" – Todd K Mar 23 '18 at 16:01
  • When navigating from the browser remember to specify the method as well (or it will do what you saw). From your example something like `www.example.com/whateverfolder/AjaxFunctions.cfc?method=getYards`. – Miguel-F Mar 23 '18 at 16:13
  • 1
    see my comment above yours. that happens when you do not supply the method. if you need to pass parameters (like maybe `site`) then just append them to the URL with `&`. But what @Ageax mentioned still holds true - your ajax call is expecting JSON and your method is set to return a query object. See the comment above where @Ageax gave you a sample URL to test. – Miguel-F Mar 23 '18 at 16:13
  • @Miguel-F Aha. It looks like the password variable is not being recognized. It's using a global variable that is recognized by all .cfm pages, but evidently not .cfc pages – Todd K Mar 23 '18 at 16:22
  • What happens when you try to run the function with ColdFusion code only? – Dan Bracuk Mar 23 '18 at 17:32
  • @Miguel-F Okay, I figured out the datasource issue, and added 'returnformat="JSON" ' to my CFFUNCTION tag. Now when I call the CFC directly in a browser, I get the following: {"COLUMNS":["YARD"],"DATA":[["Yard-C "],["Yard-C5"],["Yard-E "],["Yard-G "]]} . But my ajax call is still not working. It will empty the YARD dropdown, but no alert is coming up, so it is evidently not getting a "success". – Todd K Mar 23 '18 at 17:52
  • Okay, I did away with the "data" options in ajax and just included it all in the url: (url: "AjaxFunctions.cfc?method=getYards&varSite="+$("#SITE").val(),). Now I'm getting success with the incoming data {"COLUMNS":["YARD"],"DATA":[["X-745-C "],["X-745-C5"],["X-745-E "],["X-745-G "]]} . Yay! Now, how do I populate the dropdown with that data? – Todd K Mar 23 '18 at 18:32
  • Should be able to use `data` to pass the "Site" argument, but yeah ... method name and returnformat should go in the URL. Are you able to change the function? CF's format for queries is clunky. Simpler to build an array of structures with the whatever key names jQuery is expecting, ie `struct = {ITEMID = "..", ITEMDESC"="..."}` – SOS Mar 23 '18 at 19:04

2 Answers2

4

Are you able to change the cffunction? ColdFusion's JSON format for queries is goofy. IMO, it's simpler to change the function. Return an array of structures with the whatever key names you want, like "yard". (Don't forget to scope the function variables and change the returntype to "array")

<cfcomponent output="false">
   <cffunction name="getYards" access="remote" returntype="array" >
       <cfargument name="Site" type="string" required="true" /> 

       <cfset var rs_Yards = "">
       <cfset var response = []>

       <!--- demo data. replace this with your cfquery --->
       <cfset rs_Yards = QueryNew("")>
       <cfset queryAddColumn(rs_Yards, "Yard", ["X-745-C ","X-745-C5","X-745-E ","X-745-G "])>

       <cfloop query="rs_Yards">
           <cfset arrayAppend(response, { "yard": rs_Yards.yard })>
       </cfloop>

       <cfreturn response />
   </cffunction>
</cfcomponent>

Incorporate the other changes I mentioned in the comments:

  • Javascript is case sensitive. Change datatype to dataType (capital "T")
  • Change the URL to request JSON from the component: AjaxFunctions.cfc?method=getYards&returnformat=json

Finally, use the new key name, "yard", in the $.each() loop.

JQuery:

$(document).ready(function() {
    $("#SITE").change(function() {
        $("#YARD").empty();
        $.ajax({
            type: "GET",
            url: "AjaxFunctions.cfc?method=getYards&returnformat=JSON",
            data: {Site: $("#SITE").val()},
            dataType: "json",
            success: function(data) {
                $.each(data, function() {
                     $("#YARD").append($("<option></option>").val(this.yard).text(this.yard));
                }); 
            }
        });
    });
});

HTML:

<form>
   <select id="SITE" name="SITE">
      <option value="123">One</option>
      <option value="456">Two</option>
   </select>
   <select id="YARD">
       <option value="123">select somthing</option>
   </select>
</form>
SOS
  • 6,430
  • 2
  • 11
  • 29
  • I implemented your code exactly as written. The only thing I added was an alert as the first line of the success function. The YARD dropdown emptied, but did not repopulate, and the success alert never came up. – Todd K Mar 26 '18 at 12:30
  • Worked fine for me. The only change was to use QueryNew in place of your db cfquery. (Silly question, but you added your real query in the function right?) Use your browser's javascript console to see what's going on with the ajax request. ie. Firefox web console > Network tab. – SOS Mar 26 '18 at 12:44
  • .. the network tab also shows the http response content. Most browser's have something similar. – SOS Mar 26 '18 at 12:54
  • I'm getting the following data returned to the calling page: {"COLUMNS":["YARD"],"DATA":[["YARD-C "],["YARD-C5"],["YARD-E "],["YARD-G "]]} So I know I'm receiving the data. It's just not filling the dropdown menu with the data returned. – Todd K Mar 26 '18 at 13:04
  • @ToddK - Doesn't look like you changed the cffunction, so it's still returning data in the old format. See my update above (only need to add your cfquery). It returns an array of structures like this `[{"yard":"X-745-C "},{"yard":"X-745-C5"},{"yard":"X-745-E "},{"yard":"X-745-G "}]` – SOS Mar 26 '18 at 13:53
  • I just now copied your code directly into my page. The only thing I added was my query. When I ran the cfc page directly from a browser, I got this: "YARD-C YARD-C5YARD-E YARD-G" When I ran it from the calling page, the YARD dropdown emptied out but did not refill. – Todd K Mar 26 '18 at 14:32
  • Interesting though. I just changed the "dataType:" back to a lowercase t (datatype:), and ran it again. Then I did an alert of the data received and got `data[{"yard":"YARD-C"},{"yard":"YARD-C5"},{"yard":"YARD-E"},{"yard":"YARD-G"}] ` – Todd K Mar 26 '18 at 14:39
  • So, just to clarify, I'm getting the data in the correct JSON format now, but the `$.each` statement is not doing anything. I tried an alert at the top of the `$.each` function `alert(this.yard);` to see if it would give me the individual yards, and it doesn't. So I think it's something in the `$.each(data,function() {` function. – Todd K Mar 26 '18 at 15:07
  • There shouldn't be ` ` at the end of the response. That would definitely break the jQuery and subsequent `$.each()`. Is that a copy paste error? **Edit: Also, don't forget to fix the case of `dataType` or jquery won't deserialize the response and the `$.each()`won't work** – SOS Mar 26 '18 at 15:23
  • Nope, that's not a copy/paste error. When I open the cfc page with a browser, I don't get that " – Todd K Mar 26 '18 at 16:54
  • Well that's likely what is breaking things and why it doesn't work for you. Maybe you didn't see the edit, but did you fix the "datatype" vs "dataType" issue (otherwise it won't work)? BTW, debugging is easier with `console.log` than the old `alert()` trick :-) – SOS Mar 26 '18 at 17:22
  • Yes, I fixed the datatype issue, I have it as dataType (capital T). Not sure why the result has that HTML on there, and a lot of spaces/returns as well. – Todd K Mar 26 '18 at 17:41
  • Is debugging enable maybe? Is your code inside a folder with an Application.cfc? – SOS Mar 26 '18 at 17:53
  • Nothing else is coming to mind aside from that or possible difference in the code https://pastebin.com/sZt16Eke – SOS Mar 26 '18 at 18:16
  • Sorry, are you talking about JQuery debugging or browser debugging? I read a post about a similar problem using PHP, and the user fixed it by posting `exit();` at the end of the processing code [https://stackoverflow.com/questions/43895454/jquery-ajax-request-recieves-doctype-html#43895518]. Not sure how that would work with ColdFusion. – Todd K Mar 26 '18 at 18:39
  • ColdFusion debugging (CF Admin). Since you're using a CFC, there shouldn't be any output other than what's in the function unless something else is adding it, like CF Debugging or an Application.cfc. It works fine for me, so again either your code is different or it's something in your environment. 1. Is there an Application.cfc covering your test files? 2. What happens when you run my example here ? https://pastebin.com/sZt16Eke. Make sure there's NO Application.cfc in the test or parent directories. – SOS Mar 26 '18 at 18:53
  • I did find an Application.cfm file in the parent directory (not cfc, but it looks like it does the same types of things). This is an old application built by someone else, what should I look for in the Application.cfm file? He may have debugging set up as well, where would I look for that? – Todd K Mar 27 '18 at 11:29
  • What happened when you ran the pastebin example in a directory without any Application.cfc/cfm files? – SOS Mar 27 '18 at 13:14
  • I couldn't run the pastebin example, I'm behind a firewall at work that won't allow me to access that site. However, I think I got it to work without the JSON. I converted the result to a comma-separated list and sent that plain text to the calling page. Then I parsed the received list with the `data.split(",");` function and populated the dropdown that way. The only problem I have now is that when it populates the dropdown box, the box is suddenly about 10 rows high. Any idea what might cause that? – Todd K Mar 27 '18 at 13:23
  • I'd guess it's something about the data, but it's hard to say without seeing the code. I updated my answer with the complete code so you can try it. Just plug in your cfquery. (Edit) *He may have debugging set up as well, where would I look for that?* If debugging was enabled, you should know it because the output displays at the bottom of each page. It could also be toggled at runtime using `cfsetting`. Also, maybe look for stray ` ` in the Application.cfm. – SOS Mar 27 '18 at 13:48
  • I found a workaround by sending the data just as a comma-separated list instead of a JSON array, and then separating the list to populate the drop-down. It's basic, but it works. Thanks so much for all your help. – Todd K Mar 27 '18 at 18:25
  • Now I'll always be wondering what the issue was in your environment! Just joking ;-) Glad you found another option. – SOS Mar 27 '18 at 19:52
1

Big thanks to @Ageax for all his help. I wound up going a much simpler route. Here is my function:

<cfcomponent output="false">
<!---  Get YARDS dataset based on SITE  --->
    <cffunction name="getYards" access="remote" returntype="string" returnformat="plain" >
        <cfargument name="Site" type="string" required="true" /> 
        <cfquery name="rs_Yards" datasource="MyDatasource" >
            <!--- SQL code here --->
        </cfquery>
        <cfset myList = ValueList(rs_Yards.Yard)>   <!--- convert query results to list --->
        <cfreturn MyList />
    </cffunction>
</cfcomponent>

And here is my jQuery on the calling page:

<!---  jQuery/AJAX to autofill dropdowns  --->
    <script>
    $(document).ready(function() {
        $("#SITE").change(function() {   // when the SITE dropdown changes 
            $("#YARD").empty();          // empty the YARD dropdown 
            $.ajax({
                type: "GET",
                url: "AjaxFunctions.cfc",
                data: {
                    Site: $("#SITE").val(),
                    Method: "getYards"
                    },
                dataType: "text",
                success: function(data) {
                    var options = data.split(",");              // convert returned list to array 
                    var select = document.getElementById('YARD');
                    for(var i=0;i<options.length; i++)
                    {
                        var options2 = $.trim(options[i]);      // clean up label portion 
                        $("#YARD").append($("<option></option>").val(options[i]).text(options2));   // append options to YARD dropdown 
                    }
                }
            });
        });
    });
    </script>

Hope this helps someone else.

Todd K
  • 73
  • 1
  • 13