ajax comet basic tutorial

เพราะว่า server ไม่สามารถเปิด connection ไปยัง browser ได้
ทางเดียวที่เป็นไปได้คือ ให้ browser hold connection ไว้

เช่น ถ้าเป็น php ก็ใช้ sleep

แล้วไปแก้ปัญหา server timeout โดยการเปิด connection ใหม่ ( resetConnection )

resetConnection คร่าวๆ คือ ใข้ heartbeat

ก็คือ call function heartbeat ทุกครั้งที่ response กลับมา เพื่อกันไม่ให้ heartbeat มันไป resetConnection

var timeoutId = null;

function heartbeat(){

clearTimeout(timeoutId);

timeoutId = setTimeout(resetConnection, 10000); // 10 sec.

}

ถ้า connection มันขาดหายไป ( timeout ไปแล้ว ) มันก็จะ resetConnection ใหม่ให้เอง

ซึ่งปกติเขาใช้ iframe เป็นตัว connector กัน โดยเซต src ( location ) ของ iframe เป็นไฟล์ response เลย

แต่มันด้วยการที่มันเป็น iframe เลยมีปัญหานิดหน่อย คือ browser จะขึ้นสถานะเป็น Loading ตลอดเวลา เพราะว่า connection ไปยัง server ในหน้า iframe ไม่ได้ถูกปิดไป

วิธีแก้สำหรับ IE คือ

แทนที่จะใส่ iframe ตรงๆ ก็ไปใส่ iframe ไว้ใน ActiveRecord(“htmlfile”); ซึ่งเป็น object ของ IE

ซึ่ง htmlfile ของ IE มันแยกจากหน้าเวป ดังนั้น จะไม่มีสถานะ loading ใน browser มาให้วุ่นวายใจ

เช่น

oPage = new ActiveRecord("htmlfile");

oPage.open();

oPage.write("<html><body></body></html>");

oPage.close("");

# โดยอย่าลืมใส่ reference ไปที่หน้าปัจจุบันด้วย เพราะว่า htmlfile ของ IE มันแยกเด็ดขาดจากหน้าเพจจริงๆ ( ใช้ parent หรือ top เรียกไม่ได้ )

oPage.parentWindow._parent = self;  # คือ เซตให้เพจลูกเรียก parent._parent แล้วมาเจอ object นี้นั่นเอง
# ดังนั้นในเพจ response ใน iframe จึงเรียก parent._parent.heartbeat() ได้

# หมายเหตุ <em>_parent</em> เป็นชื่อเฉยๆ นะ จะเปลี่ยนเป็นอย่างอื่นก็ได้แหละ</strong>

oPage.body.innerHTML = "<iframe src=\"http://dsin.blogspot.com\"></iframe>"

วิธีแก้สำหรับ firefox คือ

firefox มีวิธีจัดการกับ HTTP streaming ได้ดีกว่า IE

แทนที่จะใช้ iframe ก็ให้ไปใช้ XMLHttpRequest แทน โดยดักจับ สถานะ .readyState เป็น 3 ( คือ ส่งแล้ว ยังไม่ complete )

ref : comet and reverse ajax book ( Apress )

AJAX in Rails

ใน view ( จริงๆ ยัดเข้าไปใน view/layout/application.html.erb ได้ด้วย )

<%= javascript_include_tag :defaults %>
# include javascript [ Prototype and Scriptaculous ]

<script src="/javascripts/prototype.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/effects.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/dragdrop.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/controls.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/application.js?1223017864" type="text/javascript"></script>

Sample Prototype code

ใส่ลงไปใน public/javascripts/application.js ก็ได้

function serverSideAlert() {
new Ajax.Request( '/xhr_test/myresponse', {
method: 'get',
onSuccess: function( request ) {
alert( request.responseText );
}
})
}
<a href="#" onclick="serverSideAlert()">Server side alert()</a>

หรือ มันมี function ไว้ ส่ง XHR และ filling in a DOM element ให้เลยนะ ( .innerHTML )

new Ajax.Updater( 'HTMLid', '/xhr_test/myresponse', { method: 'get' })

หรือ ใส่ append แทนที่จะเซต .innerHTML

insertion: ‘bottom’

new Ajax.Updater( 'HTMLid', '/xhr_test/myresponse',
{ insertion: 'bottom', method: 'get' })

อีกตัวอย่างหนึ่ง

evalScripts:true

new Ajax.Request('/ajax_test/cityforzip', {method:'get', asynchronous:true, evalScripts:true, parameters:'zip='+$('postalcodeInput').value});

evalScripts คือ ให้ execute response เป็น script

Sample Rails code

link_to_remote คือ สร้าง a ไปเรียก ajax แทนที่จะ refresh page

# Ajax:Request()

<%= link_to_remote “Inline alert”, :url => { :action => :myresponse },
:success => “alert( request.responseText )” %>

# Ajax:Updater(), set innerHTML ของ HTMLid เป็น response

<%= link_to_remote “Inline alert”, :url => { :action => :myresponse },
:update => :HTMLid %>

<p id=”HTMLid”></p>

# append rather than .innerHTML

<%= link_to_remote “Inline alert”, :url => { :action => :myresponse },
:update => :HTMLid,
:position => ‘bottom’ %>

วิธีเขียน callback
:before, :complete (XHR state 4), :success, and :failure

<%= link_to_remote “Check time”, :url => { :action => :get_time },
:update => :current_time, :before =>”$(‘indicator’).show()“, :complete => “$(‘indicator’).hide()“, :success => “$(‘current_time’).visualEffect(‘highlight’);”, :failure => “alert(‘There was an error.’)” %>

NOTE .visualEffect คือ scriptaculous visual effect

<span id=”indicator” style=”display: none;”>Loading…</span>

<p id=”current_time“></p>

NOTE : XHR ย่อมาจาก XML HTTP request

rjs

เก็บไว้ที่ view/controllerName/actionName.rjs

page[:cityInput].value = ‘test’  แปลเป็น javascript ได้เป็น $(‘cityInput’).value = “test”

มันใช้ตัวแปรของ rails ได้ด้วย เหมือน view ทุกอย่าง

page[:cityInput].value = @zipcode.city

Ajax autocomplete

ลอง request ไป http://www.geonames.org

เราสามารถส่ง postalcode ไปยัง geonames.org เพื่อเอาค่ากลับมาได้ เช่น

http://ws.geonames.org/postalCodeLookupJSON?postalcode=12120&country=TH

ดู reference ได้ที่

http://www.geonames.org/export
1.

XMLHttpRequest -> only allows connections to original server

ถ้าจะเรียก ajax ข้ามเครื่อง ให้ใช้ JSONscriptRequest ของ Yahoo! นั่นเอง

( JSONscriptRequest add javascript tag to the DOM ไม่ได้ เรียก ajax ข้ามเครื่อง )

Download Levitt’s JSONscriptRequest class definition from http://developer.yahoo.com/javascript/samples/dynamic/jsr_class.js

2. Add <script> tags to import jsr_class.js and http://www.geonames.org/export/geonamesData.js

3. onblur = “postalCodeLookup”

// This function is called when the user leaves the postal code input field.
// It calls the geonames.org JSON Web service to fetch an array of places for
// the given postal code
function postalCodeLookup() {
var country = escape( $( ‘countrySelect’ ).value );
// Check if country is supported

## geonamesPostalCodeCountries เป็นตัวแปรอยู่ใน geonamesData.js

if (geonamesPostalCodeCountries.toString().search(country) == -1) {
alert(‘Sorry, geonames.org does not support lookups in country ‘ +
country + ‘.’);
return;
}
var postalcode = escape( $(‘postalcodeInput’).value );
// Display “loading” message in search status box
$( ‘searchStatusElement’ ).show;
$( ‘searchStatusElement’ ).innerHTML =
‘<small><i>searching for postal code ‘ + postalcode + ‘ in ‘ +
country + ‘…</i></small>’;
// Create a new script object
var request = ‘http://ws.geonames.org/postalCodeLookupJSON?postalcode=&#8217; +escape( postalcode ) + ‘&country=’ + escape( country ) +’&callback=extractLocation‘;

// callback ใช้โดย JSONscriptRequest

// มันจะเรียก extractLocation({“postalcodes”:[{“postalcode”:”12120″,”countryCode”:”TH”,”placeName”:”Khlong Luang”}]});
aObj = new JSONscriptRequest( request );
// Build the <script> tag point to that url

aObj.buildScriptTag();
// adds the new element to the DOM, the browser fetches the JavaScript code from the remote server and executes it.

aObj.addScriptTag();
}
////////////////////////////////////////////////////////////////////////////////////////////////

// This function will be called when the JSON response arrives from the
// server. The parameter jData will contain an array with postalcode
// objects.
function extractLocation( jData ) {
$( ‘searchStatusElement’ ).innerHTML = ”;
$( ‘searchStatusElement’ ).hide;
if (jData == null) {
alert( ‘There was a problem looking up your postal code.’ );
return;
}
if (jData.postalcodes.length >= 1) {
$(‘cityInput‘).value = jData.postalcodes[0].placeName;
$(‘stateInput‘).value = jData.postalcodes[0].adminName1;
} else {
$(‘cityInput’).value = ‘UNKNOWN’;
$(‘stateInput’).value = ‘UNKNOWN’;
}
}