Version 7 of Simple AJAX Example

Updated 2006-10-23 13:02:57 by MJ

MAKR 2006/10/22: I've looked into AJAX and what it takes to use it with Tcl on the server's side. To get started I used a good example for PHP from [L1 ]. It's really easy. Most of the code involved is Javascript. But have a look for yourself ...

 #!/usr/local/ActiveTcl/bin/tclsh8.4
 package require ncgi

 # possible content types in this example
 set ctx "text/xml"
 set cth "text/html"

 # Check whether the name is used already
 #  fixed list for simplicity: Alice, Bob
 proc nameInUse {q} {  
   switch -- [string tolower $q] {
     alice - bob {return 1}
     default {return 0}
   }
 }

 ::ncgi::parse

 if {[::ncgi::exists q]} {
   # Background operation: check query 
   ::ncgi::header $ctx
   puts "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
 <response>
   <method>checkName</method>
   <result>[nameInUse [::ncgi::value q]]</result>
 </response>"

 } else {
   # Initial call: build page
   ::ncgi::header $cth
   puts "<script type=\"text/javascript\">\n<!--"
   puts {var req;
 function processReqChange() 
 {
     // only if req shows 'complete'
     if (req.readyState == 4) {
         // only if 'OK'
         if (req.status == 200) {
             // ...processing statements go here...
       response  = req.responseXML.documentElement;
       method    = response.getElementsByTagName('method')[0].firstChild.data;
       result    = response.getElementsByTagName('result')[0].firstChild.data;
       eval(method + '(\'\', result)');
         } else {
             alert("Problem while retrieving XML data:\n" + req.statusText);
         }
     }
 }
 function loadXMLDoc(url) 
 {
     // branch for native XMLHttpRequest object
     if (window.XMLHttpRequest) {
         req = new XMLHttpRequest();
         req.onreadystatechange = processReqChange;
         req.open("GET", url, true);
         req.send(null);
     // branch for IE/Windows ActiveX version
     } else if (window.ActiveXObject) {
         req = new ActiveXObject("Microsoft.XMLHTTP");
         if (req) {
             req.onreadystatechange = processReqChange;
             req.open("GET", url, true);
             req.send();
         }
     }
 }
 function checkName(input, response)
 {
   if (response != ''){ 
     // Response mode
     message   = document.getElementById('nameCheckFailed');
     if (response == '1'){
       message.className = 'error';
     }else{
       message.className = 'hidden';
     } 
   }else{
     // Input mode
     //url  = 'http://localhost/cgi-bin/webtest.cgi?q=' + input;
     url  = document.URL + '?q=' + input;
     loadXMLDoc(url);
   }
 }
 }
   puts "//-->\n</script>"
   puts "<style type=\"text/css\">\n<!--"
   puts "span.hidden{
   display: none;
 }

 span.error{
   display: inline;
   color: black;
   background-color: pink;  
 }"
   puts "-->\n</style>"
   puts "<input id=\"username\" name=\"username\" type=\"text\" onkeyup=\"checkName(this.value,'')\" />"
   puts "<span class=\"hidden\" id=\"nameCheckFailed\">
   This name is in use, please try another. 
 </span>"
 }

Execute this as CGI script. Tried with IE 6.0 and Firefox 1.5...


Some words about the functionality ...

The procedure nameInUse is the backend function. For simplicity it checks against a hardcoded list of names and returns true when it detects Alice or Bob - false otherwise.

The fist call to the script should be made without parameters. The complete page is sent to the browser in this case.

To understand whats happening now, observe your web server's log files. Enter a name. You can see now that whenever you release a key the browser sends a query to the server. And the server answers with a small amount of data. If you happen to enter Alice or Bob a colored box will immediately appear, informing that this name is already in use.


MJ - If you want to run this example using tclhttpd put the following code in the custom directory. Start tclhttpd and browse to http://server:port/ajax .

 # Check whether the name is used already
 #  fixed list for simplicity: Alice, Bob
 proc nameInUse {q} {
  switch -- [string tolower $q] {
    alice - bob {return 1}
    default {return 0}
  }
 }

 proc ajax {} {
  # Initial call: build page
  set ::ajax text/html
  set result "<script type=\"text/javascript\">\n<!--"
  set result [concat $result {var req;
      function processReqChange()
      {
        // only if req shows 'complete'
        if (req.readyState == 4) {
          // only if 'OK'
          if (req.status == 200) {
            // ...processing statements go here...
            response  = req.responseXML.documentElement;
            method    = response.getElementsByTagName('method')[0].firstChild.data;
            result    = response.getElementsByTagName('result')[0].firstChild.data;
            eval(method + '(\'\', result)');
          } else {
            alert("Problem while retrieving XML data:\n" + req.statusText);
          }
        }
      }
      function loadXMLDoc(url)
      {
        // branch for native XMLHttpRequest object
        if (window.XMLHttpRequest) {
          req = new XMLHttpRequest();
          req.onreadystatechange = processReqChange;
          req.open("GET", url, true);
          req.send(null);
          // branch for IE/Windows ActiveX version
        } else if (window.ActiveXObject) {
          req = new ActiveXObject("Microsoft.XMLHTTP");
          if (req) {
            req.onreadystatechange = processReqChange;
            req.open("GET", url, true);
            req.send();
          }
        }
      }
      function checkName(input, response)
      {
        if (response != ''){
          // Response mode
          message   = document.getElementById('nameCheckFailed');
          if (response == '1'){
            message.className = 'error';
          }else{
            message.className = 'hidden';
          }
        }else{
          // Input mode
          //url  = 'http://localhost/cgi-bin/webtest.cgi?q=' + input;
          url  = document.URL + '/q?name=' + input;
          loadXMLDoc(url);
        }
      }
    }]
  set result [concat $result "//-->\n</script>"]
  set result [concat $result "<style type=\"text/css\">\n<!--"]
  set result [concat $result "span.hidden{
      display: none;
    }

    span.error{
      display: inline;
      color: black;
      background-color: pink;
    }"]
  set result [concat $result "-->\n</style>"]
  set result [concat $result "<input id=\"username\" name=\"username\" type=\"text\" onkeyup=\"checkName(this.value,'')\" />"]
  set result [concat $result "<span class=\"hidden\" id=\"nameCheckFailed\">
    This name is in use, please try another.
    </span>"] 
 }

 proc ajax/q {name} {
  # Background operation: check query
  set ::ajax/q text/xml
  set result "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
  <response>
  <method>checkName</method>
  <result>[nameInUse $name]</result>
  </response>"
  puts $result
  return $result
 }

 Direct_Url /ajax ::ajax 

Category Internet