FAQ

如何处理错误?

Errors can be checked on the second argument of the then.

Restangular.all("accounts").getList().then(function() {
  console.log("All ok");
}, function(response) {
  console.log("Error with status code", response.status);
});

如何在每次请求的时候加头?

You can use defaultHeaders property for this or $httpProvider.defaults.headers, whichever suits you better. defaultsHeaders can be scoped with withConfig so it's really cool.

我能缓存请求吗?

$http can cache requests if you send the property cache to true. You can do that for every Restangular request by using defaultHttpFields property. This is the way:

RestangularProvider.setDefaultHttpFields({cache: true});

能有在 $routeProvider.resolve 里吗?

Yes, of course. Every method in Restangular returns a promise so this can be used without any problem.

如何发送一个不带主题的 delete 方法?

You must add a requestInterceptor for this.

RestangularProvider.setRequestInterceptor(function(elem, operation) {
  if (operation === "remove") {
     return null;
  }
  return elem;
})

My response is actually wrapped with some metadata. How do I get the data in that case?

So, let's assume that your data is the following:

 // When getting the list, this is the response.
{
  "status":"success",
  "data": {
    "data": [{
      "id":1,
      // More data
    }],
    "meta": {
      "totalRecord":100
    }
  }
}

// When getting a single element, this is the response.
{
  "status":"success",
  "data": {
    "id" : 1
    // More data
  }
}

In this case, you'd need to use RestangularProvider's addResponseInterceptor. See the following:

app.config(function(RestangularProvider) {

    // add a response intereceptor
    RestangularProvider.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
      var extractedData;
      // .. to look for getList operations
      if (operation === "getList") {
        // .. and handle the data and meta data
        extractedData = data.data.data;
        extractedData.meta = data.data.meta;
      } else {
        extractedData = data.data;
      }
      return extractedData;
    });

});

I use Mongo and the ID of the elements is _id not id as the default. Therefore requests are sent to undefined routes

What you need to do is to configure the RestangularFields and set the id field to _id. Let's see how:

RestangularProvider.setRestangularFields({
  id: "_id"
});

What if each of my models has a different ID name like CustomerID for Customer

In some cases, peolpe have different ID name for each entity. For example, they have CustomerID for customer and EquipmentID for Equipment. If that's the case, you can override's Restangular's getIdFromElem. For that, you need to do:

RestangularProvider.configuration.getIdFromElem = function(elem) {
  // if route is customers ==> returns customerID
  return elem[_.initial(elem.route).join('') + "ID"];
}

With that, you'd get what you need :)

How can I send files in my request using Restangular?

This can be done using the customPOST / customPUT method. Look at the following example:

Restangular.all('users')
          .withHttpConfig({transformRequest: angular.identity})
          .customPOST(formData, undefined, undefined,
            { 'Content-Type': undefined });

This basically tells the request to use the Content-Type: multipart/form-data as the header. Also formData is the body of the request, be sure to add all the params here, including the File you want to send of course. There is an issue already closed but with a lot of information from other users and @mgonto as well: GitHub - Restangular

How do I handle CRUD operations in a List returned by Restangular?

The best option for doing CRUD operations with a list, is to actually use the "real" list, and not the promise. It makes it easy to interact with it.

Let's see an example :).

// Here we use then to resolve the promise.
Restangular.all('users').getList().then(function(users) {
  $scope.users = users;
  var userWithId = _.find(users, function(user) {
    return user.id === 123;
  });

  userWithId.name = "Gonto";
  userWithId.put();

  // Alternatively delete the element from the list when finished
  userWithId.remove().then(function() {
    // Updating the list and removing the user after the response is OK.
    $scope.users = _.without($scope.users, userWithId);
  });

});

When you actually get a list by doing

$scope.owners = house.getList('owners').$object;

You're actually assigning a Promise to the owners value of the $scope. As Angular knows how to process promises, if in your view you do an ng-repeat of this $scope variable, results will be shown once the promise is resolved (Response arrived). However, changes to that promise that you do from your HTML won't be seen in the scope, as it's not a real array. It's just a promise of an array.

Removing an element from a collection, keeping the collection restangularized

While the example above removes the deleted user from the collection, it also overwrites the collection object with a plain array (because of _.without) which no longer knows about its Restangular attributes.

If want to keep the restangularized collection, remove the element by modifying the collection in place:

userWithId.remove().then(function() {
  var index = $scope.users.indexOf(userWithId);
  if (index > -1) $scope.users.splice(index, 1);
});

When I set baseUrl with a port, it's stripped out.

It won't be stripped out anymore as I've ditched $resource :). Now you can happily put the port :).

How can I access the unrestangularized element as well as the restangularized one?

In order to get this done, you need to use the responseExtractor. You need to set a property there that will point to the original response received. Also, you need to actually copy this response as that response is the one that's going to be restangularized later

RestangularProvider.setResponseExtractor(function(response) {
  var newResponse = response;
  if (angular.isArray(response)) {
    angular.forEach(newResponse, function(value, key) {
      newResponse[key].originalElement = angular.copy(value);
    });
  } else {
    newResponse.originalElement = angular.copy(response);
  }

  return newResponse;
});

Alternatively, if you just want the stripped out response on any given call, you can use the .plain() method, doing something like this:


$scope.showData = function () {
  baseUrl.post(someData).then(function(response) {
    console.log(response.plain());
  });
};

Addendum : If you want originalElement to be the original response object instead of having an original value for each key in your newResponse array, replace

      newResponse[key].originalElement = angular.copy(value);

By

      newResponse.originalElement[key] = angular.copy(value);

Restangular fails with status code 0

This is typically caused by Cross Origin Request policy. In order to enable cross domain communication and get correct response with appropriate status codes, you must have the CORS headers attached, even in error responses. If the server does not attach the CORS headers to the response then the XHR object won't parse it, thus the XHR object won't have any response body, status or any other response data inside which typically will cause your request to fail with status code 0.

Why does this depend on Lodash / Underscore?

This is a very good question. I could've done the code so that I don't depend on Underscore nor Lodash, but I think both libraries make your life SO much easier. They have all of the "functional" stuff like map, reduce, filter, find, etc. With these libraries, you always work with immutable stuff, you get compatibility for browsers which don't implement ECMA5 nor some of these cool methods, and they're actually quicker. So, why not use it? If you've never heard of them, by using Restangular, you could start using them. Trust me, you're never going to give them up after this!