goog.net.XhrManagerを使ってみた!
Closure LibraryでXMLHttpRequestを扱うgoog.net.XhrManagerを使ってみました。
これは前回のgoog.net.XhrIoを内部に持ち、複数リクエストを簡単に扱えるようにしたものらしいです。
WEB開発メモ: Closure Library - XhrManager
まずはコンストラクタ実行時に、インスタンスを生成します。
app.js
/** * initialize. * @private */ xhrmanagerSample.App.prototype.initialize_ = function(){ this.eventHandler_ = new gevents.EventHandler(this); this.xhrManager_ = new goog.net.XhrManager(); //ここで生成 this.nextXhrId_ = 1; //リクエスト管理用のID var viewBookListButton = gdom.getElement('view-book-list-button'); this.eventHandler_.listen(viewBookListButton, EventType.CLICK, goog.bind(this.onClickViewBookListButton_, this)); var bookEntryForm = gdom.getElement('book-entry-form'); this.eventHandler_.listen(bookEntryForm, EventType.SUBMIT, goog.bind(this.onSubmit_, this)); };
this.nextXhrId_はXMLHttpRequestを管理するためのIDです。
リクエスト毎に一意になるように、連番で採番します。
残りのコードは、以下の2つの機能を実現するためのものです。
書籍の登録
ボタンを押すと書籍の一覧が出てくる
XMLHttpRequestを送信するための関数です。
app.js
/** * ajax send request. * @param {Object} path * @param {Object} query * @param {Object} callback * @param {Object} method * @param {Object} opt_content */ xhrmanagerSample.App.prototype.sendRequest = function(path, query, callback, method, opt_content){ var url = goog.Uri.parse('/closurelibrary/xhriosample/' + path); goog.object.forEach(query || {}, function(value, key){ url.setParameterValue(key, value); }, this); var headers = {}, body = null; if (opt_content) { body = goog.json.serialize(opt_content); headers['Content-Type'] = 'application/json'; } var error = gdom.getElement('error-message'); gdom.removeChildren(error); return this.xhrManager_.send(this.nextXhrId_++, url, method, body, headers, 0, goog.bind(this.processRequest_, this, callback), 5); };
XmlHttpRequestを送信しているのは最後のsend()です。
send()の仕様については、以下の通りです。
id {string} リクエストのID。他のリクエストと重ならない文字列を指定する url {string} リクエスト送信先のURL method {string=} リクエストするメソッド。デフォルトはGET content {string=} Postするデータ headers {Object|goog.structs.Map=} リクエストのヘッダー priority {*=} リクエストの優先順位。小さいほど優先順位が高い callback {Function=} リクエスト完了時に呼ばれるコールバック関数 maxRetries {number=} リトライの最大回数
GAEでは色々な理由でErrorが発生します。
その場合、リトライを行いたいのですが、XhrManagerを使えば、maxRetriesを指定するだけで実現できます。
リクエストの返答があった時に呼ばれる関数です。
これは標準で呼ばれるものではなく、XhrMnager.send()で私が指定したため、呼ばれます。
app.js
/** * ajax request process method. * @param {Object} callback * @param {Object} e */ xhrmanagerSample.App.prototype.processRequest_ = function(callback, e){ var xhr = e.target; var error = gdom.getElement('error-message'); if (xhr.isSuccess()) { callback && callback(xhr.getResponseJson('while(1);')); } else { gdom.removeChildren(error); if (xhr.getStatus() == '404') { gdom.append(error, gsoy.renderAsElement(templates.alert.error, { 'message': 'ページが見つかりませんでした。' })); } else { gdom.append(error, gsoy.renderAsElement(templates.alert.error, { 'message': 'エラーが発生しました。再度、試してみてください。' })); } } };
手抜きですが、Error処理なんかもちょこっと入れたりしています。
xhr.isSuccess()で結果が成功していた場合のみ、送信時に指定したコールバック関数を呼ぶようにしています。
xhrオブジェクトから、リクエスト時に指定したIDも取得できるので、ここで処理を分岐することもできると思います。
書籍一覧を取得するための関数です。
ボタン押下時に呼ばれる関数です。
app.js
/** * book list get. */ xhrmanagerSample.App.prototype.getBookList = function(){ var bookList = gdom.getElement('book-list'); var loadingIcon = gdom.createDom('span', { 'class': 'icon-loading16' }, ''); gdom.append(bookList, loadingIcon); this.sendRequest('list', {}, goog.bind(this.onGetBookListComplete, this), 'GET', {}); };
リクエストが返ってくるまでの間、くるくる回るアイコンを表示しています。
'list'がURLの最後のPath。
onGetBookListCompleteがリクエスト成功時に呼ばれるコールバック関数です。
書籍一覧取得成功時に呼ばれるコールバック関数です。
app.js
/** * ajax get book list compete. * @param {Object} e */ xhrmanagerSample.App.prototype.onGetBookListComplete = function(e){ var bookList = gdom.getElement('book-list'); gdom.removeChildren(bookList); var data = { bookList: [] }; for (var i = 0, len = e['bookList'].length; i < len; i++) { data.bookList[i] = { isbn: e['bookList'][i]['isbn'], name: e['bookList'][i]['name'], link: e['bookList'][i]['link'] }; } gdom.append(bookList, gsoy.renderAsElement(templates.book.bookList, data)); };
Closure Templatesを使っています。
この辺りは、以下のエントリーを見ていただくと分かりやすいです。
Closure Templatesを使ってみた! - SinDiary
書籍情報登録時に呼ばれる関数です。
app.js
/** * ajax book form put. * @param {Object} param */ xhrmanagerSample.App.prototype.putBook = function(param){ this.sendRequest('entry', param, goog.bind(this.onPutBookComplete, this), 'PUT', param); };
'entry'がURLの最後のPath。
onPutBookCompleteがリクエスト成功時に呼ばれるコールバック関数です。
paramの中に登録する書籍の情報が入っています。
書籍情報登録成功時に呼ばれる関数です。
app.js
/** * ajax book form put complete. * @param {Object} e */ xhrmanagerSample.App.prototype.onPutBookComplete = function(e){ var form = gdom.getElement('book-entry-form'); form.reset(); };
フォームをリセットしているだけです。
フォームSubmit時に呼ばれる関数です。
app.js
/** * book entry form submit. * @param {Object} e */ xhrmanagerSample.App.prototype.onSubmit_ = function(e){ e.preventDefault(); var form = gdom.getElement('book-entry-form'); var param = gdom.forms.getFormDataMap(form).toObject(); this.putBook(param); };
initialize_()でイベントハンドラーに登録した関数です。
フォームのSubmit時に呼ばれ、実際のSubmitを止めた後、値をXMLHttpRequestで投げるようにしています。
まだまだ、改善点はありますが、とりあえずはこんな感じです。
後、UnitTestをXhrIoの時のように作れれば良いのですが、まだ出来ていません・・・。
goog.net.XhrIoを使ってみた! - SinDiary
伊藤殿に質問して回答していただいたのですが、まだ動くものはできずorz
https://plus.google.com/u/0/114061805681880927213/posts/ZA6B9wthi3D
なんとかしたいと思ってます。
今回のソースはこちら。
app.js
Google Code Archive - Long-term storage for Google Code Project Hosting.