update based on pull request comments:

- separate g-xhr to a component (component-as-module-pattern)
- dispatch Events instead
- add jsdoc to public methods
- update comment
- use classList.enable
- use handlers="click: clickHandler" instead of experimental event
binding
This commit is contained in:
frankiefu
2012-10-15 11:42:30 -07:00
parent 2a7224122c
commit 4c32ee8bdb
3 changed files with 108 additions and 67 deletions

View File

@@ -7,76 +7,25 @@
-->
<element name="g-ajax" attributes="url, handleAs, params">
<link rel="components" href="g-component.html">
<link rel="components" href="g-xhr.html">
<template>
<g-xhr id="xhr"></g-xhr>
<content></content>
</template>
<script>
var xhr = {
request: function(inOptions) {
var transport = this.getTransport();
var url = inOptions.url;
var method = inOptions.method || 'GET';
var async = !inOptions.sync;
var params = this.toQueryString(inOptions.params);
if (params && method == 'GET') {
url += (url.indexOf('?') > 0 ? '&' : '?') + params;
}
transport.open(method, url, async);
this.makeReadyStateHandler(transport, inOptions.callback);
this.setRequestHeaders(inOptions.headers);
transport.send(method == 'POST' ? (inOptions.body || params) : null);
if (!async) {
transport.onreadystatechange(transport);
}
return transport;
},
getTransport: function() {
try {
return new XMLHttpRequest();
} catch (e) {}
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {}
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {}
return false;
},
makeReadyStateHandler: function(inXhr, inCallback) {
inXhr.onreadystatechange = function() {
if (inXhr.readyState == 4) {
inCallback && inCallback.apply(null, [inXhr.responseText, inXhr]);
}
};
},
setRequestHeaders: function(inXhr, inHeaders) {
if (inHeaders) {
for (var name in inHeaders) {
xhr.setRequestHeader(name, inHeaders[name]);
}
}
},
toQueryString: function(inParams) {
var r = [];
for (var n in inParams) {
var v = inParams[n];
n = encodeURIComponent(n);
r.push(v === undefined || v === null ? n : (n + '=' + encodeURIComponent(v)));
}
return r.join('&');
}
};
this.component({
created: function(inSuper) {
this.context = this.context || window;
if (this.getAttribute('autogo')) {
this.go();
}
},
prototype: {
/**
* Performs an Ajax request to the url specified.
*/
go: function() {
var params = JSON.parse(this.params);
return xhr.request({url: this.url, callback: this.receive.bind(this), params: params});
return this.$.xhr.request({url: this.url, callback: this.receive.bind(this), params: params});
},
receive: function(inResponse, inXhr) {
if (this.isSuccess(inXhr)) {
@@ -90,21 +39,23 @@
var status = inXhr.status || 0;
return !status || (status >= 200 && status < 300);
},
fire: function(inMethod, inArgs) {
var fn = this.context[this.getAttribute(inMethod)];
fn && fn.apply(this.context, inArgs);
dispatchAjaxEvent: function(inType, inResponse, inXhr) {
this.dispatchEvent(new CustomEvent(inType,
{detail: {response: inResponse, xhr: inXhr}}));
},
response: function(inXhr) {
var response = this.evalResponse(inXhr);
this.fire('onresponse', [response, inXhr]);
this.dispatchAjaxEvent('response', response, inXhr);
// Here we want to expose the response as a model so that it can be
// consumed by other components.
this.model = this.model || {};
this.model.response = response;
},
error: function(inXhr) {
this.fire('onerror', [inXhr.status + ': ' + inXhr.responseText, inXhr]);
this.dispatchAjaxEvent('error', inXhr.status + ': ' + inXhr.responseText, inXhr);
},
complete: function(inXhr) {
this.fire('oncomplete', [inXhr]);
this.dispatchAjaxEvent('complete', inXhr.status, inXhr);
},
evalResponse: function(inXhr) {
return this[(this.handleAs || 'text') + 'Handler'](inXhr);

View File

@@ -5,11 +5,11 @@
* license that can be found in the LICENSE file.
*/
-->
<element name="g-togglebutton" attributes="value">
<element name="g-togglebutton" attributes="value" handlers="click: clickHandler">
<link rel="components" href="g-component.html">
<link rel="stylesheet" href="css/g-togglebutton.css" />
<template>
<div id="toggle" class="toggle" onclick="x('clickHandler')">
<div id="toggle" class="toggle">
<span class="on">ON</span>
<span class="off">OFF</span>
<span class="thumb"></span>
@@ -22,12 +22,12 @@
},
prototype: {
valueAttributeChanged: function() {
this.$.toggle.classList[this.trueValue() ? 'add' : 'remove']('on');
this.$.toggle.classList.enable('on', this.trueValue());
},
clickHandler: function() {
this.value = !this.trueValue();
},
// note: should base component auto-convert string value to boolean?
// TODO(ffu): remove this when base component handles auto-converting string value to boolean
trueValue: function() {
return this.value != 'false' && Boolean(this.value);
}

90
src/g-xhr.html Normal file
View File

@@ -0,0 +1,90 @@
<!--
/*
* Copyright 2012 The Toolkitchen Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
-->
<element name="g-xhr">
<link rel="components" href="g-component.html">
<template>
<style>
@host {
display: none;
}
</style>
</template>
<script>
this.component({
prototype: {
/**
* Sends a HTTP request to the server and returns the XHR object.
*
* @param inOptions A set of key/value pairs that configure the request.
* @param inOptions.url The url to which the request is sent.
* @param inOptions.method The HTTP method to use, default is GET.
* @param inOptions.sync By default, all requests are sent asynchronously.
* To send synchronous requests, set to true.
* @param inOptions.params Data to be sent to the server.
* @param inOptions.body The content for the request body for POST method.
* @param inOptions.headers HTTP request headers.
* @param inOptions.callback Called when request is completed.
* @returns XHR object.
*/
request: function(inOptions) {
var transport = this.getTransport();
var url = inOptions.url;
var method = inOptions.method || 'GET';
var async = !inOptions.sync;
var params = this.toQueryString(inOptions.params);
if (params && method == 'GET') {
url += (url.indexOf('?') > 0 ? '&' : '?') + params;
}
transport.open(method, url, async);
this.makeReadyStateHandler(transport, inOptions.callback);
this.setRequestHeaders(inOptions.headers);
transport.send(method == 'POST' ? (inOptions.body || params) : null);
if (!async) {
transport.onreadystatechange(transport);
}
return transport;
},
getTransport: function() {
try {
return new XMLHttpRequest();
} catch (e) {}
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {}
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {}
return false;
},
makeReadyStateHandler: function(inXhr, inCallback) {
inXhr.onreadystatechange = function() {
if (inXhr.readyState == 4) {
inCallback && inCallback.apply(null, [inXhr.responseText, inXhr]);
}
};
},
setRequestHeaders: function(inXhr, inHeaders) {
if (inHeaders) {
for (var name in inHeaders) {
xhr.setRequestHeader(name, inHeaders[name]);
}
}
},
toQueryString: function(inParams) {
var r = [];
for (var n in inParams) {
var v = inParams[n];
n = encodeURIComponent(n);
r.push(v === undefined || v === null ? n : (n + '=' + encodeURIComponent(v)));
}
return r.join('&');
}
}
});
</script>
</element>