Posted At : Dec 04, 2009 14:04 PM | Posted By : Ed Tabara
Related Categories: ColdFusion, AJAX, JavaScript

  • Given
  • Have a form with 3 fields: Zip (text field), City (text field), State (dropdown field).

  • Problem description
  • The user fills in the Zip. Then, when starting to fill the City, AutoComplete appear (from those that have filled Zip). When/If one is selected the State is filled automatically.

  • Solution
  • Nowadays there are many solutions for AJAX AutoComplete but i choosed the jQuery plugin from devbridge.com. It seem to work very well and on the site are given details on how to use it.
    So after incuding the library with
    <script type="text/javascript" src="js/jquery.autocomplete.js"></script>
    creating the form:
    <form name="frm" action="someactionhere" method="post">
    <strong>Zip:</strong> <input size="5" name="Zip" id="Zip" type="Text" value="#Zip#">
     <strong>City:</strong> <input size="30" name="City" id="City" type="Text" value="#City#">
     <strong>State:</strong> 
    <select name="State" id="State">
       <option value=""<cfif State eq ""> selected</cfif>>
       <cfloop query="getStates">
          <option value="#stateCode#"<cfif State eq stateCode> selected</cfif>> #stateName#
       </cfloop>
    </select>
    </form>

    and ading the JS:
    <script language="JavaScript" type="text/javascript">
       <!--
          var options, a;
          jQuery(function(){
             options =
                {
                   serviceUrl:'index.cfm?action=buyers.getCity&loadContent=true&Zip='+$('##Zip').val()
                   , maxHeight:150
                }
             a = $('##City').autocomplete(options);
          });
       //-->
    </script>

    i started to test it in order to move farther.

    And of course issues started. First problem was that the value from the Zip field was not passed to index.cfm?action=buyers.getCity. Instead of the value just entered was passed the one that was in the field at the start, so it wasn't exactly what i needed. Maybe there exist an easier way, i don't know, but after quite long playing with it i came to the following solution. I created a new JS function doAutoSuggest() and moved existing code into it and then made the call to it from City's onclick
    <form name="frm" action="someactionhere" method="post">
    <strong>Zip:</strong> <input size="5" name="Zip" id="Zip" type="Text" value="#Zip#">
     <strong>City:</strong> <input size="30" name="City" id="City" onclick="doAutoSuggest();" type="Text" value="#City#">
     <strong>State:</strong> 
    <select name="State" id="State">
       <option value=""<cfif State eq ""> selected</cfif>>
       <cfloop query="getStates">
          <option value="#stateCode#"<cfif State eq stateCode> selected</cfif>> #stateName#
       </cfloop>
    </select>
    </form>

    <script language="JavaScript" type="text/javascript">
       <!--
          function doAutoSuggest()
          {
             options =
                {
                   serviceUrl:'index.cfm?action=buyers.getCity&loadContent=true&Zip='+$('##Zip').val()
                   , maxHeight:150
                };
             $('##City').autocomplete(options);
          }
       //-->
    </script>

    The script that retrieves cities data and pass it back on the AJAX call is:
    <cfsetting showdebugoutput="No" enablecfoutputonly="Yes">
    <cfsilent>
       <cfparam name="query" default="">
       <cfparam name="Zip" default="">
       <cfquery name="getCities" datasource="#application.dsn#">
          Select distinct top 50 City + ', ' + st as ct, City + '|' + st as ct2
          From zips
          Where City like '#query#%'
             <cfif Len(Zip)>
                and zip = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#Zip#">
             </cfif>
          Order by ct asc
       </cfquery>
       <cfset items = QuotedValueList(getCities.ct, ",")>
       <cfset items2 = QuotedValueList(getCities.ct2, ",")>
    </cfsilent>
    <cfoutput>{ query:'#query#',suggestions:[#items#],data:[#items2#] }</cfoutput>

    Note that the Zip may not be entered and so there may be City with same name for different State. Because of it, in the suggestions i return city, state pairs and in data i place same pairs but without the blank after the comma so that it to can be used for later easy processing.
    What is left now is to make sure the City and State form fields are filled with the right values when something is selected in the AutoComplete dropdown.
    Because the data come in a city,state format we can split it and so onSelect to place it in needed form fields. But because the State is a dropdown element you can't just place the right value into it, you need to change the current selection. So, i created a JS function that taking as attribute the new value, will set it AND make the right selection.
    function getOptionInde<img src="images/smiles/14.gif" border="0">opt)
    {
       document.frm.State.value=opt;
       return document.getElementById('State').selectedIndex;
    }

    And so, the final code is as follow
    <form name="frm" action="someactionhere" method="post">
    <strong>Zip:</strong> <input size="5" name="Zip" id="Zip" type="Text" value="#Zip#">
     <strong>City:</strong> <input size="30" name="City" id="City" onclick="doAutoSuggest();" type="Text" value="#City#">
     <strong>State:</strong> 
    <select name="State" id="State">
       <option value=""<cfif State eq ""> selected</cfif>>
       <cfloop query="getStates">
          <option value="#stateCode#"<cfif State eq stateCode> selected</cfif>> #stateName#
       </cfloop>
    </select>
    </form>

    <script language="JavaScript" type="text/javascript">
       <!--
          function doAutoSuggest()
          {
             options =
                {
                   serviceUrl:'index.cfm?action=buyers.getCity&loadContent=true&Zip='+$('##Zip').val()
                   , maxHeight:150
                   , onSelect: function(value, data){ qwe=data.split("|");document.frm.City.value=qwe[0];document.frm.State.selectedIndex=getOptionInde<img src="images/smiles/14.gif" border="0">qwe[1]); }
                };
             $('##City').autocomplete(options);
          }

          function getOptionInde<img src="images/smiles/14.gif" border="0">opt)
          {
             document.frm.State.value=opt;
             return document.getElementById('State').selectedIndex;
          }
       //-->
    </script>

    Enjoy!

    Comments Comments (0) | Print Print | Email Send | 677 Views | 3% / 0% Popularity


    Posted At : Jun 19, 2008 23:03 PM | Posted By : Ed Tabara
    Related Categories: AJAX

    Be aware of incorrect formatted HTML!

    Yesterday i was checking a script that was using AJAX to load some form fields into the form based on the selection in a drop down. Everything there was working fine except the fact that when submitting the form, all AJAX loaded fields was missing in the FORM structure. And this was happening in FireFox while working fine in Internet Explorer.
    To be short i will get right to the solution.
    The problem was that the FORM tag was right after the TABLE tag (read FORM inside TABLE). Right after i changed the order, all worked fine.


    Comments Comments (1) | Print Print | Email Send | 3189 Views | 16% / 6% Popularity


    Posted At : May 03, 2007 6:14 AM | Posted By : Ed Tabara
    Related Categories: ColdFusion, Other, AJAX

    Not long ago was doing some AJAX code that would allow 3rd party sites load data from other site. Everything worked fine when having the "base site" and the "test sites" in same place, but when tryin to run that with 2 different domains.... no luck... Came out that "the browser security model does not allow using XMLHttpRequest (XHR) from one web page domain to contact an URL on another domain". Nice huh?

    To see more details on it, visit this link.


    Comments Comments (0) | Print Print | Email Send | 1053 Views | 5% / 0% Popularity


    Posted At : Mar 31, 2007 15:26 PM | Posted By : Ed Tabara
    Related Categories: ColdFusion, AJAX

    Had an interesting issue this week with Safari and Opera.

    description:
    Have a form with 3 drop downs. First one have values, the second is populated based on the selected value in first select, the 3rd is populated with values based on the selection in the 2nd drop down.

    Problem:
    All work fine with IE and FF, but not in Safari and Opera. The actual population work fine, BUT right after the 3rd drop down is populated with values, the second drop down disappear at all. And as result, when the form is submitted, that field and it's value are not passed.

    Solution:
    I did not find yet a good/real solution for this issue, but just a workaround. What i've done, was to create a hidden form field that get populated with the selected value in the second drop down on the onchange action.


    To illustrate this better here is an emulated example:
    The JS code look like this:
    <script language="JavaScript" type="text/javascript">
    <!--
    var xmlhttp
    if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
    try {
    xmlhttp = new XMLHttpRequest();
    } catch (e) {
    xmlhttp=false
    }
    }

    function loadFragmentInToElement(fragment_url, element_id)
    {
    var element = document.getElementById(element_id);
       var theURL = fragment_url
    element.innerHTML = '<center><img src="images/loading_.gif"></center>';
    xmlhttp.open("GET", fragment_url);
    xmlhttp.onreadystatechange = function()
       {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
          {
    element.innerHTML = xmlhttp.responseText;
    }
          else element.innerHTML = ';
    }
       xmlhttp.setRequestHeader('Accept','message/x-jl-formresult')
    xmlhttp.send(null);
       return false;
    }
    //-->
    </script>

    The base script (1.cfm):
    <form name="frm" action="_1.cfm" method="post">
       <table>
          <tr>
             <td>value 1:</td>
             <td>
                <cfoutput>
                <select name="fld1" id="fld1" onchange="return loadFragmentInToElement('_2.cfm?rnd=#RandRange(1,99999)#&fld1v='+this.value, 'spn1')">
                   <option value="">
                   <option value="1">1
                   <option value="2">2
                   <option value="3">3
                   <option value="4">4
                   <option value="5">5
                   <option value="6">6
                   <option value="7">7
                   <option value="8">8
                   <option value="9">9
                </select>
                </cfoutput>
             </td>
          </tr>
          <tr>
             <td>Value 2:</td>
             <td>
                <span id="spn1">
                   <select name="fld2" id="fld2">
                      <option value="">
                   </select>
                </span>
             </td>
          </tr>
          <tr>
             <td>Value 3:</td>
             <td>
                <span id="spn2">
                   <select name="fld3" id="fld3">
                      <option value="">
                   </select>
                </span>
             </td>
          </tr>
          <tr>
             <td colspan="2"><input type="Submit" value="Submit"></td>
          </tr>
       </table>
    </form>

    The code for _2.cfm:
    <cfsetting showdebugoutput="No">
    <cfoutput>
       <select name="fld2" id="fld2" onchange="return loadFragmentInToElement('_3.cfm?rnd=#RandRange(1,99999)#&fld1v=#fld1v#&fld2v='+this.value, 'spn2')">
          <option value="#fld1v#1">#fld1v#1
          <option value="#fld1v#2">#fld1v#2
          <option value="#fld1v#3">#fld1v#3
          <option value="#fld1v#4">#fld1v#4
          <option value="#fld1v#5">#fld1v#5
          <option value="#fld1v#6">#fld1v#6
          <option value="#fld1v#7">#fld1v#7
          <option value="#fld1v#8">#fld1v#8
          <option value="#fld1v#9">#fld1v#9
       </select>
    </cfoutput>

    The code for _3.cfm:
    <cfsetting showdebugoutput="No">
    <cfoutput>
       <select name="fld3" id="fld3">
          <option value="#fld1v##fld2v#1">#fld1v##fld2v#1
          <option value="#fld1v##fld2v#2">#fld1v##fld2v#2
          <option value="#fld1v##fld2v#3">#fld1v##fld2v#3
          <option value="#fld1v##fld2v#4">#fld1v##fld2v#4
          <option value="#fld1v##fld2v#5">#fld1v##fld2v#5
          <option value="#fld1v##fld2v#6">#fld1v##fld2v#6
          <option value="#fld1v##fld2v#7">#fld1v##fld2v#7
          <option value="#fld1v##fld2v#8">#fld1v##fld2v#8
          <option value="#fld1v##fld2v#9">#fld1v##fld2v#9
       </select>
    </cfoutput>

    So, the current solution was to add
    <input name="hfld2" type="Hidden" value=""> to _1.cfm and
    onchange="document.frm.hfld2.value=this.value;return loadFragmentInToElement('_3.cfm?rnd=#RandRange(1,99999)#&fld1v=#fld1v#&fld2v='+this.value, 'spn2')" instead of
    onchange="return loadFragmentInToElement('_3.cfm?rnd=#RandRange(1,99999)#&fld1v=#fld1v#&fld2v='+this.value, 'spn2')" to _2.cfm .


    If anyone have any clue about a real solution to this problem, please don't hesitate to comment on it.

    Comments Comments (0) | Print Print | Email Send | 962 Views | 5% / 0% Popularity