Code Monkey home page Code Monkey logo

ember-ajax's Introduction

ember-ajax

npm version Travis CI Build Status Ember Observer Score

Service for making AJAX requests in Ember applications.

  • customizable service
  • returns RSVP promises
  • improved error handling
  • ability to specify request headers

⚠️ Deprecated

ember-ajax is now deprecated. Please consider using ember-fetch, or ember-ajax-fetch as a more direct replacement.

Getting started

If you're just starting out, you already have ember-ajax installed! However, if it's missing from your package.json, you can add it by doing:

ember install ember-ajax

To use the ajax service, inject the ajax service into your route or component.

import Ember from 'ember';

export default Ember.Route.extend({
  ajax: Ember.inject.service(),
  model() {
    return this.get('ajax').request('/posts');
  }
});

Ajax Service

Basic Usage

The AJAX service provides methods to be used to make AJAX requests, similar to the way that you would use jQuery.ajax. In fact, ember-ajax is a wrapper around jQuery's method, and can be configured in much the same way.

In general, you will use the request(url, options) method, where url is the destination of the request and options is a configuration hash for jQuery.ajax.

import Ember from 'ember';

export default Ember.Controller.extend({
  ajax: Ember.inject.service(),
  actions: {
    sendRequest() {
      return this.get('ajax').request('/posts', {
        method: 'POST',
        data: {
          foo: 'bar'
        }
      });
    }
  }
});

In this example, this.get('ajax').request() will return a promise with the result of the request. Your handler code inside .then or .catch will automatically be wrapped in an Ember run loop for maximum compatibility with Ember, right out of the box.

HTTP-verbed methods

You can skip setting the method or type keys in your options object when calling request(url, options) by instead calling post(url, options), put(url, options), patch(url, options) or del(url, options).

post('/posts', { data: { title: 'Ember' } }); // Makes a POST request to /posts
put('/posts/1', { data: { title: 'Ember' } }); // Makes a PUT request to /posts/1
patch('/posts/1', { data: { title: 'Ember' } }); // Makes a PATCH request to /posts/1
del('/posts/1'); // Makes a DELETE request to /posts/1

Custom Request Headers

ember-ajax allows you to specify headers to be used with a request. This is especially helpful when you have a session service that provides an auth token that you have to include with the requests to authorize your requests.

To include custom headers to be used with your requests, you can specify headers hash on the Ajax Service.

// app/services/ajax.js

import Ember from 'ember';
import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  session: Ember.inject.service(),
  headers: Ember.computed('session.authToken', {
    get() {
      let headers = {};
      const authToken = this.get('session.authToken');
      if (authToken) {
        headers['auth-token'] = authToken;
      }
      return headers;
    }
  })
});

Headers by default are only passed if the hosts match, or the request is a relative path. You can overwrite this behavior by either passing a host in with the request, setting the host for the ajax service, or by setting an array of trustedHosts that can be either an array of strings or regexes.

// app/services/ajax.js

import Ember from 'ember';
import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  trustedHosts: [/\.example\./, 'foo.bar.com']
});

Custom Endpoint Path

The namespace property can be used to prefix requests with a specific url namespace.

// app/services/ajax.js
import Ember from 'ember';
import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  namespace: '/api/v1'
});

request('/users/me') would now target /api/v1/users/me

If you need to override the namespace for a custom request, use the namespace as an option to the request methods.

// GET /api/legacy/users/me
request('/users/me', { namespace: '/api/legacy' });

Custom Host

ember-ajax allows you to specify a host to be used with a request. This is especially helpful so you don't have to continually pass in the host along with the path, makes request() a bit cleaner.

To include a custom host to be used with your requests, you can specify host property on the Ajax Service.

// app/services/ajax.js

import Ember from 'ember';
import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  host: 'http://api.example.com'
});

That allows you to only have to make a call to request() as such:

// GET http://api.example.com/users/me
request('/users/me');

Custom Content-Type

ember-ajax allows you to specify a default Content-Type header to be used with a request.

To include a custom Content-Type you can specify contentType property on the Ajax Service.

// app/services/ajax.js

import Ember from 'ember';
import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  contentType: 'application/json; charset=utf-8'
});

You can also override the Content-Type per request with the options parameter.

Customize isSuccess

Some APIs respond with status code 200, even though an error has occurred and provide a status code in the payload. With the service, you can easily account for this behaviour by overwriting the isSuccess method.

// app/services/ajax.js

import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  isSuccess(status, headers, payload) {
    let isSuccess = this._super(...arguments);
    if (isSuccess && payload.status) {
      // when status === 200 and payload has status property,
      // check that payload.status is also considered a success request
      return this._super(payload.status);
    }
    return isSuccess;
  }
});

Error handling

ember-ajax provides built in error classes that you can use to check the error that was returned by the response. This allows you to restrict determination of error result to the service instead of sprinkling it around your code.

Built in error types

ember-ajax has built-in error types that will be returned from the service in the event of an error:

  • BadRequestError (400)
  • UnauthorizedError(401)
  • ForbiddenError(403)
  • NotFoundError (404)
  • InvalidError(422)
  • ServerError (5XX)
  • AbortError
  • TimeoutError

All of the above errors are subtypes of AjaxError.

Error detection helpers

ember-ajax comes with helper functions for matching response errors to their respective ember-ajax error type. Each of the errors listed above has a corresponding is* function (e.g., isBadRequestError).

Use of these functions is strongly encouraged to help eliminate the need for boilerplate error detection code.

import Ember from 'ember';
import {
  isAjaxError,
  isNotFoundError,
  isForbiddenError
} from 'ember-ajax/errors';

export default Ember.Route.extend({
  ajax: Ember.inject.service(),
  model() {
    const ajax = this.get('ajax');

    return ajax.request('/user/doesnotexist').catch(function(error) {
      if (isNotFoundError(error)) {
        // handle 404 errors here
        return;
      }

      if (isForbiddenError(error)) {
        // handle 403 errors here
        return;
      }

      if (isAjaxError(error)) {
        // handle all other AjaxErrors here
        return;
      }

      // other errors are handled elsewhere
      throw error;
    });
  }
});

If your errors aren't standard, the helper function for that error type can be used as the base to build your custom detection function.

Access the response in case of error

If you need to access the json response of a request that failed, you can use the raw method instead of request.

this.get('ajax')
  .raw(url, options)
  .then(({ response }) => this.handleSuccess(response))
  .catch(({ response, jqXHR, payload }) => this.handleError(response));

Note that in this use case there's no access to the error object. You can inspect the jqXHR object for additional information about the failed request. In particular jqXHR.status returns the relevant HTTP error code.

Usage with Ember Data

Ember AJAX provides a mixin that can be used in an Ember Data Adapter to avoid the networking code provided by Ember Data and rely on Ember AJAX instead. This serves as a first step toward true integration of Ember AJAX into Ember Data.

To use the mixin, you can include the mixin into an Adapter, like so:

// app/adapters/application.js
import DS from 'ember-data';
import AjaxServiceSupport from 'ember-ajax/mixins/ajax-support';

export default DS.JSONAPIAdapter.extend(AjaxServiceSupport);

That's all the configuration required! If you want to customize the adapter, such as using an alternative AJAX service (like one you extended yourself), hooks to do so are provided; check out the mixin's implementation for details.

Note that instead of using the Ember Data error checking code in your application, you should use the ones provided by Ember AJAX.

Stand-Alone Usage

If you aren't using Ember Data and do not have access to services, you can import the ajax utility like so:

import request from 'ember-ajax/request';

export default function someUtility(url) {
  var options = {
    // request options
  };

  return request(url, options).then(response => {
    // `response` is the data from the server
    return response;
  });
}

Which will have the same API as the ajax service. If you want the raw jQuery XHR object then you can use the raw method instead:

import raw from 'ember-ajax/raw';

export default function someOtherUtility(url) {
  var options = {
    // raw options
  };

  return raw(url, options).then(result => {
    // `result` is an object containing `response` and `jqXHR`, among other items
    return result;
  });
}

Local Development

This information is only relevant if you're looking to contribute to ember-ajax.

Compatibility

  • Node.js 6 or above
  • Ember CLI v2.13 or above

Installation

  • git clone this repository
  • npm install

Running

Running Tests

  • ember test
  • ember test --server

Building

  • ember build

For more information on using ember-cli, visit http://www.ember-cli.com/.

ember-ajax's People

Contributors

abrahamspaa avatar abuiles avatar alexlafroscia avatar bertdeblock avatar boris-petrov avatar cosmosgenius avatar dasnixon avatar dependabot[bot] avatar gnapse avatar greenkeeper[bot] avatar greenkeeperio-bot avatar jaswilli avatar jherdman avatar manoharank avatar mhretab avatar mike-north avatar mike183 avatar mwpastore avatar pepke41 avatar robclancy avatar rwjblue avatar scottkidder avatar sly7-7 avatar spruce avatar stefanpenner avatar taras avatar turbo87 avatar vikram7 avatar wagenet avatar webark avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ember-ajax's Issues

End of input when no JSON is returned with 200 OK from server.

Awesome addon! However, when used with Rails and response is set with the following:

respond_to do |format|
  format.json { head :ok }
end

You get an error:

SyntaxError: Unexpected end of input
    at Object.parse (native)
    at jQuery.parseJSON (http://localhost:4200/assets/vendor.js:7736:14)
    at ajaxConvert (http://localhost:4200/assets/vendor.js:8013:19)
    at done (http://localhost:4200/assets/vendor.js:8429:15)
    at XMLHttpRequest.<anonymous> (http://localhost:4200/assets/vendor.js:8819:9)

However, when you add some actual JSON content to the response, it seems to work fine.

Doesn't play nice with `wait()` in Integration tests

Just updated to v2.0.0-beta.1 in an addon project to test whether #28 was fixed - unfortunately the problem persists.

As mentioned there, Testing code (component integration test) like this...

// Trigger something that uses `ember-ajax` under the hood
this.$('button').click();

return wait().then(() => {
  // assertions
  asset.ok(...something);
});

...won't actually wait for the Ember Ajax request to return.

Will do a bit of investigating now - but just wanted to reopen this. Thank you~ ❤️

Warn user if improperly calling `.get()` on AjaxService

For a couple of minutes, I was wondering why this didn't work:

this.get('ajax').get('/someResource', { data: /* ... */ });

...before realizing that .get() is from Ember.Object. D:

Do you think it'd be a good idea to perhaps log an error if someone is using that method incorrectly? If so, maybe we could check if the second argument is an object, or if the first arg has a leading slash or something.

Always including headers could lead to unintentional leaking of token credentials

Per https://github.com/ember-cli/ember-ajax#custom-request-headers, if headers are defined they are send on all requests made with ember-ajax. The suggested use case here is to send headers including token authentication.

This seems risky. If anywhere in the application uses ember-ajax to make a request to a third-party, these credentials could be leaked.

Chatted with @taras, he suggested that as a solution headers could only be included by default when using the 'host' option or a relative URL.

JSON API Serialization

I am using json_api, and not getting the expected format when I do a post with Ember Ajax.

I figure the add-on should serialize my data if I set the right Content-Type. Is that right? Or do I need to structure the data I pass to ajax.post myself? I can do that no problem, but I suspect that's not the right approach.

So I'm making a post like this.

    ajax.post(ENV.API_ENDPOINT + 'line_items', {
      headers: {
        'Content-Type': 'application/vnd.api+json',
        'Accept': 'application/vnd.api+json'
      },
      data: {
        cart_id: cart.id,
        product_id: product.id,
        quantity: 1
      }

And it sending a request like this:

{
  "cart_id": "1160",
  "product_id": "57",
  "quantity": 1
}

But what I expect is:

{
  "data": {
    "attributes": {
      "cart_id": 1,
      "product_id": 57,
      "quantity": 1
    },
    "relationships": {
      "cart": {
        "data": null
      },
      "product": {
        "data": null
      },
    },
    "type": "line-items"
  }
}

Is the problem with me? :p

Integrate with Ember Data out of the box

Right now, unless there's something I've missed, there's no default way to have the ajax service used by Ember Data. As a result, I had to do a work-around like this to avoid duplication:

// app/adapters/application.js
import Ember from 'ember';
import ActiveModelAdapter from 'active-model-adapter';

const { inject } = Ember;
const { alias } = Ember.computed;

export default ActiveModelAdapter.extend({
  ajaxService: inject.service('ajax'),

  headers: alias('ajaxService.headers')
});

It would be awesome for this to happen automatically.

query params being serialized into one big object

Hi,

I'm running into an issue where my query params are being stringified into one big object. Here's my code:

      this.get('ajax').request('/appointments', {
          data: {
            startDate: start.toISOString(),
            endDate: end.toISOString()
          }
        })
        ...

And the URL being generated looks like: /appointments?{%22startDate%22:%222016-03-27%22,%22endDate%22:%222016-05-08%22}

Am I doing something wrong here?

Thanks!

Custom endpoint path ignored

import Ember from 'ember';
import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  namespace: '/api/v1',
  host: 'http://api.example.com'
});
import Ember from 'ember';

export default Ember.Route.extend({
  ajax: Ember.inject.service(),
  model() {
    return this.get('ajax').request('/posts');
  }
});

Results in: GET http://api.example.com/posts without namespace.

Serializing errors

If an error response returns a JSON payload, the ember-ajax error only returns [object Object] for the message. It would be useful to be able to customize the way it handles error payloads.

Support Fastboot

Note from @alexlafroscia: Basic support has been added in #75, but I want to keep this issue open until we can add Fastboot testing to our automated testing setup. Until then, things should work but we can't be sure, please make a separate issue with any problems you run into!

Note from @alexlafroscia (10/12/2016): There is actually ways to test Fastboot now, so we have the tools to add that and close this issue:

  • Add tests that verify ember-ajax works in Fastboot
  • Add test for header parsing (#162)

An addon to help with running the tests in Fastboot can be found here. Example usage can be found here.

Versions:

  • node 5.7.0
  • ember-cli 2.4.2
  • ember-ajax 0.7.1 or 2.0.0-beta.2 (or the current master branch)

Steps to reproduce:

$ ember new fastboot-test
$ ember install ember-cli-fastboot
$ ember install [email protected]

Inject and get the ajax service somewhere.
The following error gets printed to the console once the browser enters a route with the ajax service in use:
(NOTE: The error triggers as soon as I try call .get('ajax') to get the service.)

$ ember fastboot --serve-assets
version: 2.4.2
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://www.ember-cli.com/user-guide/#watchman for more info.
Built project successfully. Stored in "fastboot-dist".
Installing FastBoot npm dependencies
DEBUG: -------------------------------
DEBUG: Ember : 2.4.1
DEBUG: -------------------------------
Ember FastBoot running at http://[::]:3000
Error: Could not find module `url` imported from `(require)`
    at missingModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:164:1)
    at findModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:179:1)
    at requireModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:168:1)
    at Module.callback (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/ember-ajax/utils/url-helpers.js:30:1)
    at Module.exports (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:83:1)
    at Module.build (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:133:1)
    at findModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:181:1)
    at Module.reify (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:115:1)
    at Module.build (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:133:1)
    at findModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:181:1)
    at Module.reify (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:115:1)
    at Module.build (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:133:1)
    at findModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:181:1)
    at Module.reify (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:115:1)
    at Module.build (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:133:1)
    at findModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:181:1)
    at requireModule (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/vendor/loader/loader.js:168:1)
    at [object Object]._extractDefaultExport (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/ember-resolver/resolver.js:346:1)
    at [object Object].resolveOther (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/ember-resolver/resolver.js:81:1)
    at [object Object].superWrapper (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:23203:1)
    at [object Object].exports.default._emberRuntimeSystemObject.default.extend.resolve (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:5828:1)
    at resolve (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:2385:1)
    at Object.Registry.resolve (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:1898:1)
    at Object.Registry.resolve (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:1902:1)
    at has (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:2402:1)
    at Object.Registry.has (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:1988:1)
    at Object.Registry.validateInjections (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:2290:1)
    at instantiate (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:1474:1)
    at lookup (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:1330:1)
    at Object.Container.lookup (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:1257:1)
    at [object Object].lookup (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:33427:1)
    at /home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:28240:1
    at Object.exports.default._routerUtils.subclass.applyToState (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:52641:1)
    at Object.getTransitionByIntent (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:51642:1)
    at Object.Router.transitionByIntent (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:51754:1)
    at doTransition (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:52326:1)
    at Object.Router.handleURL (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:51796:1)
    at [object Object]._emberRuntimeSystemObject.default.extend._doURLTransition (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:28010:1)
    at [object Object]._emberRuntimeSystemObject.default.extend.handleURL (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:28006:1)
    at [object Object].ApplicationInstance.reopen.visit (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:3997:1)
    at tryCatch (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:53542:1)
    at invokeCallback (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:53557:1)
    at publish (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:53525:1)
    at /home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:32081:1
    at Queue.invoke (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:333:1)
    at Object.Queue.flush (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:397:1)
    at Object.DeferredActionQueues.flush (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:205:1)
    at Object.Backburner.end (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:560:1)
    at [object Object]._onTimeout (/home/dsenn/gitty/github/topaxi.ch/fastboot-dist/assets/bower_components/ember/ember.debug.js:1126:1)
2016-03-08T17:08:35.358Z 500 Unknown Error: Error: Could not find module `url` imported from `(require)` 277ms

I tried to fix this by adding

"fastbootDependencies": [ "url" ]

to my package.json without success.

normalizeErrorResponse should handle JSON responses

My API is returning a JSON response along with an error code status (not any of the ones that are handled), and normalizeErrorResponse is converting the payload to a string here. The results in the detail of the error object being "[object Object]".

Is this the intended behavior?

Overwrite error method?

I see an example for "globally" handling errors when the status is 200, but what about other status codes like 401 or 403?

I would think something like this would be possible:

// app/services/ajax.js

import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  isUnauthorized(status, headers, payload ) {
    // some custom logic
  }
});

Rather than handling specific errors on each request, is it possible to handle them globally?

Keep CHANGELOG.md updated

Hi, would it be possible to update CHANGELOG.md with each release? I know it seems like waste of time, but it really helps when all the thousands of users of this awesome library are deciding on upgrading :)

Thanks a lot in in advance

Custom request domain

A particular request might be on a different domain than the one specified in host.
this.get('ajax').request('http://tarunarora.xyz/');
So if http:// or https:// is present in the URL, host should not be taken into _buildURL

Remote url is prefixed with localhost

I'm on 2.0.0-beta.2.

this.get('ajax').request('https//api.github.com/search/') makes a request to http://localhost:4200/https//api.github.com/search/.

Doesn't seem to trigger jQuery Ajax Hooks

Hi Taras!

I'm using ember-ajax from inside a component (to post an email to a mailing list).

I'm trying to write integration tests for the component, using wait().

(The code for wait is here.)

I do something like

this.$('button').click();

return wait().then(() => {
  // assertions
  asset.ok(...something);
});

However when I call debugger inside the wait function (in the link above) the value of requests is an empty array. I'd assume that means jQuery(document).on('ajaxSend', incrementAjaxPendingRequests); and jQuery(document).on('ajaxComplete', decrementAjaxPendingRequests); aren't being called.

Do you have any thoughts / experience here?

Your handler code inside .then or .catch will automatically be wrapped in an Ember run loop for maximum compatibility with Ember, right out of the box.

I'm confused about what this means. Do the promises returned by ember-ajax have their own customized then method? If that's the case, that seems like a bad idea, since one common pattern with promises is to do

promise . then(functionReturningAPromise)

And in such a case, the returned promise is supposed to "subsume" or "substitute" for the promise returned from the function. If in fact then handlers are buried in ember run calls, it would seem like it could not work as it should. Instead, we end up with a promise whose value is a promise, which is semi-useless, which is what seems to be happening to me.

I have the following case of chaining API calls, the first which gets a token and the second which makes the "real" call, so I'm trying to do something like this:

postData(token, data) { return this.get('ajax').post(url, {data}); },
go() { 
  return this.getToken() . 
    then(response => this.postData(response, data)) . 
    then(response => console.log(response));
}

Unless I'm missing something, this is not working because the response => this.postData(response, data) action, which evaluates to a promise, which the promise chain should be adopting as its value, is perhaps instead getting put inside an Ember. run, with the promise being resolved to a promise? Then again, maybe I'm just having a major hallucination.

Having trouble with acceptance tests & error handling

I've attempted to create a ember-ajax wrapper with some convenience methods and a general error catch. The error catch looks like this:

...
  myConvenientGET(method, url, options) {
    let req = this.get('ajax').request(url, options);

    req.catch(error => {
      error.errors.forEach((error) => {
        this.get('flash').add({
          message: error.detail,
          type: 'alert-error'
        });
      });

      throw error;
    });

    return req;
  }

The problem I'm having is that in an acceptance test which includes a bad response, the thrown error bubbles all the way to the site of the acceptance test, but I don't see anyway of handling it. I've tried to wrap the test in try{}catch(e){} and qunit's throws. My question is when handling errors like these I want to throw so that the promise chain is rejected, but how can we handle these expected errors at the acceptance test level?

Is this how a general error catch should be done using ember-ajax?

Could not find module `ember-mixinify-class`

I just upgraded to version 2.0.1 (from 0.7.1) by running ember install ember-ajax. Now I am getting the error:

Could not find module `ember-mixinify-class` imported from `ember-ajax/services/ajax`

Is there an additional dependency that is not specified?

Add isAjaxError helper?

I'm updating some projects to use the new is* error helper functions and there are some cases where I don't care at all what specific AjaxError came back, just knowing that it's an AjaxError is enough.

For consistency, does it make sense to add an isAjaxError() helper as well? Otherwise it will be necessary to do a mix of instanceof checks and is* checks. It seems cleaner if everything could be handled with the helpers.

Documentation improvement

I searched for an answer to this, but I was wondering if you could clarify some things about this statement in the README.md:

ic-ajax also wraps requests in Ember.run which is no longer necessary on Ember 1.13+.

Do you mean that you no longer have to wrap requests or callbacks? I see in ic-ajax that they wrap the success callback in an Ember.run. Also, it looks like ember-ajax library does the same thing if I am reading it right.

So why is this no longer necessary? Ember 1.13 docs still state that wrapping async callbacks with Ember.run is a good idea. I couldn't find anything in the Ember docs that stated this.

Content-type and Encoding

Hi there,

I'm trying to send a custom ajax call to my backend server, which is a JSONAPI.org server. I don't know why, but when I try to send a Content-Type header (application/vnd.api+json) ember-ajax is form encoding the request.

      this.get('ajax').request('/api/v1/things', {
        method: 'POST',
        data: {
          // ......
        },
        headers: {
          'Content-Type': 'application/vnd.api+json',
          'Accept': 'application/vnd.api+json'
        }
      }).then( jsonapiResponse => {
         // .....
      });

Checking my server logs is getting the body sent as a big form encoded blob, rather than JSON.

Am I doing something wrong?

Server API version

As of now, I am using the host option to configure the server API version.

// app/services/ajax.js

import Ember from 'ember';
import AjaxService from 'ember-ajax/services/ajax';

export default AjaxService.extend({
  host: '/api/v1'
});

Is that ok?

Unclear Upgrade Path for ic-ajax/raw

I have a use case in my application wherein I inspect response codes during long polling:

return raw(membershipUrl, { type: 'HEAD', headers: { 'If-Modified-Since': ifModifiedSince } })
  .then((result) => {
    let jqXHR = result.jqXHR;

    // NOTE: if we have already manually refreshed or changed the membership, a different check will
    // already be in progress and we need not make any changes regarding the result of this stale update
    if (this.get('intervalId') !== lastInterval) {
      return;
    }

    if (jqXHR.status === 204) {
      this._newContentAvailable();
    } else {
      this.set('checkInProgress');
    }
  });

The upgrade path for such usage is unclear to me, it sort of seems like ¯\_(ツ)_/¯. In general, I don't feel like headers, and response codes should be totally scrubbed from the response. It'd be nice to opt in to a more advanced use case.

Error in connection with ember-cli-mirage

I don't really know where it comes from. But I have an issue with mirage and ember-ajax

My code is as follows:

//some controller
this.get('ajax').post('/api/resendEmail', {
        data: {
          email: email
        }
      }).then(function(/*data*/) {
        //yay
      }).catch(function(reason) {
        console.log(reason); // get's called
...
this.post('/resendEmail', function(db, request) { 
    return new Response(200, {}, "not verified"); // api is fix
  });

This should nicely call the success callback but instead it calls the catch with the following reason stack:

SyntaxError: Unexpected token o
    at Object.parse (native)
    at jQuery.parseJSON (http://localhost:4200/assets/vendor.js:7782:14)
    at ajaxConvert (http://localhost:4200/assets/vendor.js:8059:19)
    at done (http://localhost:4200/assets/vendor.js:8475:15)
    at http://localhost:4200/assets/vendor.js:8865:9
    at Object.<anonymous> (http://localhost:4200/assets/vendor.js:68364:9)
    at Object.dispatchEvent (http://localhost:4200/assets/vendor.js:68408:24)
    at Object._readyStateChange (http://localhost:4200/assets/vendor.js:68608:14)
    at Object._setResponseBody (http://localhost:4200/assets/vendor.js:68672:14)
    at Object.respond (http://localhost:4200/assets/vendor.js:68693:12)

As far as I can tell there is an object which gets parsed twice and thus tries to parse "object ..." to json which fails. Any ideas?

Upload with feedback

Apologies if this is clarified somewhere, but I've only had time to take a cursory glance at this project and related specs.

I understand that the fetch API will not have any upload progress callbacks since it'll use streams, and that the xhr object used to polyfill future behavior isn't exposed since it won't exist with fetch. I also assume, though haven't verified, that streams are currently not widely supported generally, and are not supported here.

I have an app that supports upload of large files (eg. wav pcm, flac, etc), and it's important that I can provide feedback on the state of an upload operation.

Currently using ic-ajax, I have a service that defines an xhr object so I can set callbacks on relevant event listeners:

_upload(file, url, headers, requestType, callbacks){
    return ajax({
        xhr: function(){
            var xhr = new window.XMLHttpRequest();
            xhr.upload.addEventListener('progress', function(evt){
                if (evt.lengthComputable) {
                    var progress =  Math.round((evt.loaded / evt.total)*100);
                    callbacks.upload.progress(progress);
                }
            }, false);
            xhr.upload.addEventListener('load', function(evt){
                callbacks.upload.load(evt);
            }, false);
            // etc
        },
        contentType : fileType,
        url         : url,
        type        : requestType,
        headers     : headers,
        data        : file,
        processData : false,
        dataType    : 'json'
    });
}

Since ember ajax initially provided swap out support for ic-ajax, does it offer some means to support similar behavior, or do I need to switch to using $.ajax for upload if I want to migrate to ember ajax?

windows CI

We should make sure this project has appveyor CI as well, as a large number of our consumers use windows.

Problems with non-json data.

I was attempting to use ember-json for a POST with FormData (Content-Type:multipart/form-data) for a file upload operation. Unfortunately, ember-ajax overrides the contentType and also passes the data through JSON.stringify.

https://github.com/ember-cli/ember-ajax/blob/master/addon/services/ajax.js#L132

Perhaps there can be an option to disable this feature or maybe disable it automatically if options.dataType or options.contentType are set to something other than json.

If not, then the documentation should clearly state that this service is designed ONLY for json data.

Question: How can I access response headers?

Our backend passes certain information through response headers that we need to be able to see/use on our ember application.

I see support for raw requests were removed, and I don't see anywhere that the full jqXHR is exposed.

Was there a philosophical reasoning behind this? Is there a workaround?

Assertion failed: TypeError: promise.then is not a function

Doing a this.post() and seeing this:

Assertion failed: TypeError: promise.then is not a function
    at null.handleActionPromise (http://localhost:4200/assets/vendor.js:204040:15)
    at Object.applyStr (http://localhost:4200/assets/vendor.js:34117:20)
    at Object.sendEvent (http://localhost:4200/assets/vendor.js:27633:28)
    at notifyObservers (http://localhost:4200/assets/vendor.js:31176:25)
    at Object.propertyDidChange (http://localhost:4200/assets/vendor.js:30972:5)
    at Object.set (http://localhost:4200/assets/vendor.js:31458:48)
    at exports.default._emberMetalMixin.Mixin.create.set (http://localhost:4200/assets/vendor.js:47227:31)
    at setUnknownProperty (http://localhost:4200/assets/vendor.js:204058:12)
    at set (http://localhost:4200/assets/vendor.js:31435:13)
    at callbackHandler (http://localhost:4200/assets/vendor.js:204000:9)
    at submitAnswer (http://localhost:4200/assets/bottle-rocket.js:1924:9)
    at _emberViewsViewsView.default.extend.send (http://localhost:4200/assets/vendor.js:58260:54)
    at _emberMetalMixin.Mixin.create.triggerAction (http://localhost:4200/assets/vendor.js:48132:29)
    at _emberViewsViewsView.default.extend.sendAction (http://localhost:4200/assets/vendor.js:58244:14)
    at click (http://localhost:4200/assets/vendor.js:204005:23)
    at _emberRuntimeSystemObject.default.extend.trigger (http://localhost:4200/assets/vendor.js:58764:23)
    at superWrapper [as trigger] (http://localhost:4200/assets/vendor.js:33723:20)
    at Object._emberMetalMerge.default.handleEvent (http://localhost:4200/assets/vendor.js:59712:21)
    at _emberViewsViewsCore_view.default.extend.handleEvent (http://localhost:4200/assets/vendor.js:61301:32)
    at Object.Backburner.run (http://localhost:4200/assets/vendor.js:10767:25)
    at Object.Backburner.join (http://localhost:4200/assets/vendor.js:10817:25)
    at Function.run.join (http://localhost:4200/assets/vendor.js:31623:28)
    at exports.default._emberRuntimeSystemObject.default.extend._bubbleEvent (http://localhost:4200/assets/vendor.js:57253:45)
    at HTMLAnchorElement.<anonymous> (http://localhost:4200/assets/vendor.js:57195:25)
    at HTMLBodyElement.jQuery.event.dispatch (http://localhost:4200/assets/vendor.js:4872:9)
    at HTMLBodyElement.elemData.handle (http://localhost:4200/assets/vendor.js:4540:28)
    at HTMLBodyElement.nrWrapper (http://localhost:4200/:54:12743)

Ember 1.13, ember-ajax 0.7.1.

Send "Content-Type": "application/json; charset=utf-8" by default

Hey folks, first of all great job with the library :)

So I notice that ember-ajax sends data with a content type of application/x-www-form-urlencoded by default. Since sending JSON is so common, would you think it's a good idea for application/json to be the default content type instead? (Like in superagent.)

For example, instead of this:

this.get('ajax').post(url, {
  contentType: 'application/json; charset=utf-8',
  data: JSON.stringify({
    hello: 'world',
    foo: 'bar'
  })
}

You could do something like this:

this.get('ajax').post(url, {
  data: {
    hello: 'world',
    foo: 'bar'
  }
})

I'm thinking it would just cut down on some boilerplate. What do you think?

Roadmap to 2.0

Ember Ajax was created to provide an up to date alternative to ic-ajax and to unify how Ember applications make XHR requests. A few weeks later, @tomdale wrote a thoughtful RFC titled Network Service, where he outlined a similar need from FastBoot prospective.

In Tom's follow up to the RFC, he notes that One of the explicit goals of Ember.js is to provide developers with the ability to use future features of the web platform today. It is our goal to make Ember Ajax use fetch API by default and do so without breaking apps that use ember-ajax. We'll do this in the Emberway™ - it will be transparent for most or require some refactoring for others.

From the start, the goal of this project was to unify the mechanism that Ember Data uses to make Ajax requests with that which we use to make ad-hoc requests in our applications. We already started a conversation with the Ember Data Core team to find a path to move forward on integrating Ember Ajax with Ember Data.

There are several issues that remain unresolved that were inherited from Ember Data code. The primarily revolve around the need to expose information about failed requests #22.

TODO

  • Remove ic-ajax related code - #35
  • Refactor Ember Ajax into an ES6 and consume it in Ajax Service - #31
  • Add missing Error types - #36
  • Add error detail handling from Ember Data #43

/cc @tomdale @stefanpenner @alexlafroscia

Doesn't work unless in pods

If I'm extending this service in my own app inside of app/services/ajax.js I get the following warning

DEPRECATION: In Ember 2.0 service factories must have an `isServiceFactory` property set to true. You registered [object Object] as a service factory.

And then when trying to use this.get('ajax').request('...') I get this error

ember.debug.js:28601 Error while processing route: holding Failed to create an instance of 'service:ajax'. Most likely an improperly defined class or an invalid module export. Error: Failed to create an instance of 'service:ajax'. Most likely an improperly defined class or an invalid module export.

Note that everything works fine with no errors if the service is inside pods/ajax/service.js

My service extension

import AjaxService from 'ember-ajax/services/ajax';
import config from 'mp3car-ember/config/environment';

export default AjaxService.extend({
});

My usage inside a route

import Ember from 'ember';

export default Ember.Route.extend({
    ajax: Ember.inject.service(),
    title: 'Holding Page',
    model() {
        let res = this.get('ajax').request('getHoldingPalets');
        console.log('res', res);
        return res;
    }
});

Using

ember-cli: 2.4.3
node: 5.9.1
os: darwin x64
ember: 
"ember-ajax": "0.7.1"

Error status codes are hidden?

It would seem that the status code for a failed request is not supplied with the AjaxError object making it impossible for application code to react to error types that are not built-in. Is this by design or is there some other way that errors are supposed to be handled?

The specific error codes that I'm having difficulty with are 413 (unsupported media type) and 415 (request entity too large), both of which can be raised by web servers in front of the API server so it's not possible to rely on a custom errors object being present.

Can't use .then() on ember-ajax's Promise after code 200

So, I'm using ember-ajax to call my user registration endpoint without problems, see code below. The endpoint returns a 201.

Note the use of registerSuccess, which I use in my template to inform the user of success.

export default Ember.Component.extend({
  ajax: Ember.inject.service(),

  actions: {
    register() {
      var self = this;
      var userdata = this.getProperties('identification', 'password', 'email');
      return this.get('ajax').post('/auth/register//', {
        data: JSON.stringify({
          username: userdata['identification'],
          password: userdata['password'],
          email: userdata['email']
        })
      }).then(
        () => self.set('registerSuccess', true)
      ).catch(function(error) {
        if (error.errors) {
          var rawErrors = error.errors[0].detail;
          self.set('errors', rawErrors);
          self.set('registerSuccess', false);
        }
      });
    }
  }
});

However, when I try to do the same thing for my 'change password' endpoint (code below), I'm not able to use .then() on the promise for some reason. This endpoint returns a 200 code, which is the only difference between the two. I made a hack below where I set pwChangeSuccess to True first, but that's ugly since the page will always display success first, even if it fails.

export default Ember.Component.extend({
  ajax: Ember.inject.service(),

  actions: {
    changePassword() {
      var self = this;
      var passwords = this.getProperties('oldPassword', 'newPassword', 'new2Password');
      // TODO: why can we do a .then() on a 201 (registration-form) work, but not on this 200?
      self.set('pwChangeSuccess', true);
      return this.get('ajax').post('/auth/password//', {
        data: JSON.stringify({
          new_password: passwords['newPassword'],
          re_new_password: passwords['new2Password'],
          current_password: passwords['oldPassword']
        })
      }).catch(function(error) {
        if (error.errors) {
          var rawErrors = error.errors[0].detail;
          self.set('errors', rawErrors);
          self.set('pwChangeSuccess', false);
        }
      });
    }
  }
});

I thought about customizing 'isSuccess`, but in debugging it showed that I never even reach this method.

I'm not sure if this is a bug, or if I'm doing something wrong.

Any help is appreciated. Thanks!

Docs: readme could mention jquery ajax api

It'd be helpful to mention the base library at the top of the readme, as some users will come to this having never used ic-ajax, and possibly without realising (or forgetting) it's jQuery based.

ic-ajax makes it clear near the start of the readme that

This lib simply wraps jQuery.ajax with two exceptions: … Other than that, use request exactly like $.ajax.

I just tripped over the 'data' parameter like this:

this.get('ajax').post('/admin/login', {email: email, password: password})

fills in the http request's password instead of putting it into the form data.

What I needed was

this.get('ajax').post('/admin/login', { data: {email: email, password: password} })

It's in the readme examples, but doesn't say what it is, and I thought it was structured data rather than request configuration.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.