-
-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ember Data Adapter Mixin to use fetch #31
Conversation
@nlfurniss how will this work with things like bigpipe? |
Yup it works fine with bigpipe (used v-web to test this). The only thing I need to confirm is that the proper tracking headers are attached to requests BPR sends. Have sent Mark and Rajul an email to figure out how to confirm. |
I like this as a stepping stone for ember-data users to use ember-fetch 👍 |
@@ -28,11 +28,14 @@ export default Ember.Route.extend({ | |||
}); | |||
``` | |||
|
|||
### Use with Ember Data | |||
To have Ember Data utilize `fetch` instead of jQuery.ajax to make calls to your backend, extend your project's `application` adapter with the `adapter-fetch` mixin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets give a quick code example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
further docs: https://github.com/github/fetch | ||
|
||
### Browser Support | ||
|
||
* evergreen / IE9+ / Safari 6.1+ https://github.com/github/fetch#browser-support | ||
* evergreen / IE10+ / Safari 6.1+ https://github.com/github/fetch#browser-support |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah is the polyfil IE10+ only now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, according to their readme: https://github.com/github/fetch#browser-support
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks
addon/mixins/adapter-fetch.js
Outdated
* @param {Object} options | ||
*/ | ||
export function mungOptionsForFetch(options) { | ||
const _options = assign({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tiny nit pick, but can we make the param be _options
but the one used throughout the function option
I think this reads nicer.
addon/mixins/adapter-fetch.js
Outdated
throw this.ajaxError(null, response, requestData); | ||
}) | ||
.catch((error, response, requestData) => { | ||
throw this.ajaxError(error, response, requestData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As implemented this catch
will also catch all errors thrown in the then
which precedes it. This includes its own this.ajaxError
That seems funky.
I believe simply re-ordering the then
and the catch
would address this issue.
this._ajaxRequest(hash)
.catch((error, response, requestData) => {
throw.this.ajaxError(error, response, requestData);
}).then(response => {
if (response.ok) {
const bodyPromise = response.json();
return this.ajaxSuccess(response, bodyPromise, requestData);
}
throw this.ajaxError(null, response, requestData);
});
addon/mixins/adapter-fetch.js
Outdated
body, | ||
requestData | ||
); | ||
} catch (error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this try/catch
is redundant and can be removed. the caught error will already be caught be the then
handler it is being called in.
addon/mixins/adapter-fetch.js
Outdated
requestData | ||
); | ||
} catch (e) { | ||
returnedError = e; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any downside to letting this just throw
?
Updated with @stefanpenner's suggestions |
so phantom doesn’t have do you want to shim Map/Headers, leave out the test, other? |
6c87c87
to
9f15853
Compare
@nlfurniss polyfilled |
@nlfurniss This is fantastic 👏 Counting the seconds to use it. |
addon/mixins/adapter-fetch.js
Outdated
// GET and HEAD requests can't have a `body` | ||
if (options.data && Object.keys(options.data).length) { | ||
if (options.method === 'GET' || options.method === 'HEAD') { | ||
options.url += `?${serialiazeQueryParams(_options.data)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found one error. This does not serialize nested objects properly.
Example:
serialiazeQueryParams(_options.data)
with this implementation generates page=%5Bobject%20Object%5D
.
The expected queryParams that is generated by $.ajax
is page%5Bnumber%5D=1&page%5Bsize%5D=3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been playing in the console, this snippet seems to work well for arbitrary nested objects:
function serialiazeQueryParams(queryParamsObject, prefix = null) {
return Object.keys(queryParamsObject).sort().map((key) => {
let value = queryParamsObject[key];
if (prefix) {
key = `${prefix}[${key}]`;
}
if (value !== null && typeof value === 'object') {
return serialiazeQueryParams(value, key);
} else {
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
}).join('&');
}
I haven't checked how arrays should work with arrays.
For what is worth, this is the implementation of $.param
, which is the thing used by $.ajax
: https://github.com/jquery/jquery/blob/master/src/serialize.js#L17-L90
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We spoke offline and this functionality has been added, along with tests
addon/mixins/adapter-fetch.js
Outdated
// GET and HEAD requests can't have a `body` | ||
if (options.data && Object.keys(options.data).length) { | ||
if (options.method === 'GET' || options.method === 'HEAD') { | ||
options.url += `?${serialiazeQueryParams(_options.data)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been playing in the console, this snippet seems to work well for arbitrary nested objects:
function serialiazeQueryParams(queryParamsObject, prefix = null) {
return Object.keys(queryParamsObject).sort().map((key) => {
let value = queryParamsObject[key];
if (prefix) {
key = `${prefix}[${key}]`;
}
if (value !== null && typeof value === 'object') {
return serialiazeQueryParams(value, key);
} else {
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
}).join('&');
}
I haven't checked how arrays should work with arrays.
For what is worth, this is the implementation of $.param
, which is the thing used by $.ajax
: https://github.com/jquery/jquery/blob/master/src/serialize.js#L17-L90
@nlfurniss Why was this closed? |
an accident of those history fixes I did :-\ But it's back! |
@nlfurniss You scared the s**** out of me |
addon/mixins/adapter-fetch.js
Outdated
* @override | ||
*/ | ||
_ajaxRequest(options) { | ||
const _options = mungOptionsForFetch(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found another issue. This is not respecting the Headers, as in jQuery the options are set using a beforeSend
function and in fetch
headers must be a Headers
object passed on the options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a PR for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stefanpenner, how do you feel about adding ED to this addon?
addon/mixins/adapter-fetch.js
Outdated
}) | ||
.then((response) => { | ||
if (response.ok) { | ||
const bodyPromise = response.json(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line also fails if the server responds with a 204 No content
, since JSON.parse throws an error trying to parse an empty response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is using the Content-Length
header reliable enough? or is 204 an edge case?
let bodyPromise;
if (response.ok && response.status !== 204) {
bodyPromise = response.json();
} else {
bodyPromise = {};
}
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No idea. I've asked in slack what the right way of handing 204s is.
After some debugging I found that this approach doesn't work if you also run your app in fastboot. I opened this issue in there: ember-fastboot/ember-cli-fastboot#419 (comment) The tl;dr is that fastboot has an initializer that also replaces the The solution I see is to put this mixing in a standalone addon, not in here, so that addon can explicitly initialize itself after Another approach if the people in ember-cli-fastboot agree would be to move the ember-data compatibility layer elsewhere. Perhaps to ember-data itself, perhaps to an |
We will need to tackle this independent of this issue. I believe fastboot XHR/FETCH interactions needs some love. |
addon/mixins/adapter-fetch.js
Outdated
var i, len, key; | ||
|
||
if (prefix) { | ||
if (isArray(obj)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array.isArray
addon/mixins/adapter-fetch.js
Outdated
*/ | ||
export function serialiazeQueryParams(queryParamsObject) { | ||
var s = [], rbracket = /\[\]$/; | ||
function isArray(obj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't need this helper, Array.isArray
is available in our target browsers and is also faster.
addon/mixins/adapter-fetch.js
Outdated
function isArray(obj) { | ||
return Object.prototype.toString.call(obj) === '[object Array]'; | ||
} | ||
function add(k, v) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets not redefine this method on each invocation of serialiazeQueryParams
, instead hoist it up.
addon/mixins/adapter-fetch.js
Outdated
* @returns {String} | ||
*/ | ||
export function serialiazeQueryParams(queryParamsObject) { | ||
var s = [], rbracket = /\[\]$/; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rbracket should likely be const RRACKET = /...
at the top of the module (no need to redefine it.
@cibernox you mentioned the fastboot thing, do you feel that is a blocker here? |
@stefanpenner I'd say that the inability if use ember-fetch (this mixin only, the rest works well) along with fastboot might be a problem for ppl building PWAs without jquery. TBH, I think that in this case fastboot's approach is a bit intrusive. |
Why is fastboot's approach a bit intrusive? I think the problem is the union with |
Fix headers of Ember-Data Mixin
I think fastboot may want to not monkey patch ember-data, but rather provide an XMLHTTPRequest and Fetch global. I think that would be less intrusive, and work with more libraries (pretender/mirage etc). This idea needs more investigation/exploration etc... |
released as v3.0.0 🎉 issue for fastboot + ED compat: #38 |
Agreed the monkey patching is bad. but I also think |
I would have liked to do some more testing of the adapter methods, let's discuss how best to do that.