#Restangular [![Build Status](https://travis-ci.org/mgonto/restangular.svg?branch=master)](https://travis-ci.org/mgonto/restangular) [![PayPayl donate button](http://img.shields.io/paypal/donate.png?color=yellow)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=martin%40gon%2eto&lc=US&item_name=Martin%20Gontovnikas¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted "Donate once-off to this project using Paypal") [![Donate on Gittip](http://img.shields.io/gittip/mgonto.svg)](https://www.gittip.com/mgonto/) Restangular is an AngularJS service that simplifies common GET, POST, DELETE, and UPDATE requests with a minimum of client code. It's a perfect fit for any WebApp that consumes data from a RESTful API. **Check out a, [live demo on plunkr](http://plnkr.co/edit/d6yDka?p=preview).** It uses the same example as the official [Angular Javascript Project](http://angularjs.org/#wire-up-a-backend)... but Restangularized! You can also **check out [a video introduction of a talk I gave at Devoxx France](http://www.parleys.com/play/535a189ee4b0c5ba17d43455/chapter1/about)** about Restangular #Table of contents - [Restangular](#restangular) - [Differences with $resource](#differences-with-resource) - [How do I add this to my project?](#how-do-i-add-this-to-my-project) - [Dependencies](#dependencies) - [Production apps using Restangular](#production-apps-using-restangular) - [Starter Guide](#starter-guide) - [Quick configuration for Lazy Readers](#quick-configuration-for-lazy-readers) - [Adding dependency to Restangular module in your app](#adding-dependency-to-restangular-module-in-your-app) - [Using Restangular](#using-restangular) - [Creating Main Restangular object](#creating-main-restangular-object) - [Let's code!](#lets-code) - [Configuring Restangular](#configuring-restangular) - [Properties](#properties) - [setBaseUrl](#setbaseurl) - [setExtraFields](#setextrafields) - [setParentless](#setparentless) - [setDefaultHttpFields](#setdefaulthttpfields) - [addElementTransformer](#addelementtransformer) - [setOnElemRestangularized](#setonelemrestangularized) - [setResponseInterceptor](#setresponseinterceptor) - [setResponseExtractor (alias of setResponseInterceptor)](#setresponseinterceptor) - [addResponseInterceptor](#addresponseinterceptor) - [setRequestInterceptor](#setrequestinterceptor) - [addRequestInterceptor](#addrequestinterceptor) - [setFullRequestInterceptor](#setfullrequestinterceptor) - [setErrorInterceptor](#seterrorinterceptor) - [setRestangularFields](#setrestangularfields) - [setMethodOverriders](#setmethodoverriders) - [setDefaultRequestParams](#setdefaultrequestparams) - [setFullResponse](#setfullresponse) - [setDefaultHeaders](#setdefaultheaders) - [setRequestSuffix](#setrequestsuffix) - [setUseCannonicalId](#setusecannonicalid) - [How to configure them globally](#how-to-configure-them-globally) - [Configuring in the config](#configuring-in-the-config) - [Configuring in the run](#configuring-in-the-run) - [How to create a Restangular service with a different configuration from the global one](#how-to-create-a-restangular-service-with-a-different-configuration-from-the-global-one) - [Decoupled Restangular Service](#decoupled-restangular-service) - [Methods description](#methods-description) - [Restangular methods](#restangular-methods) - [Element methods](#element-methods) - [Collection methods](#collection-methods) - [Custom methods](#custom-methods) - [Copying elements](#copying-elements) - [Enhanced promises](#enhanced-promises) - [Using values directly in templates](#using-values-directly-in-templates) - [Using Self reference resources](#using-self-reference-resources) - [URL Building](#url-building) - [Creating new Restangular Methods](#creating-new-restangular-methods) - [Adding Custom Methods to Collections](#adding-custom-methods-to-collections) - [Example:](#example) - [Adding Custom Methods to Models](#adding-custom-methods-to-models) - [Example:](#example-1) - [FAQ](#faq) - [How can I handle errors?](#how-can-i-handle-errors) - [I need to send one header in EVERY Restangular request, how do I do this?](#i-need-to-send-one-header-in-every-restangular-request-how-do-i-do-this) - [Can I cache requests?](#can-i-cache-requests) - [Can it be used in $routeProvider.resolve?](#can-it-be-used-in-routeproviderresolve) - [My response is actually wrapped with some metadata. How do I get the data in that case?](#my-response-is-actually-wrapped-with-some-metadata-how-do-i-get-the-data-in-that-case) - [I use Mongo and the ID of the elements is _id not id as the default. Therefore requests are sent to undefined routes](#i-use-mongo-and-the-id-of-the-elements-is-_id-not-id-as-the-default-therefore-requests-are-sent-to-undefined-routes) - [What if each of my models has a different ID name like CustomerID for Customer](#what-if-each-of-my-models-has-a-different-id-name-like-customerid-for-customer) - [How do I handle CRUD operations in a List returned by Restangular?](#how-do-i-handle-crud-operations-in-a-list-returned-by-restangular) - [When I set baseUrl with a port, it's stripped out.](#when-i-set-baseurl-with-a-port-its-stripped-out) - [How can I access the unrestangularized element as well as the restangularized one?](#how-can-i-access-the-unrestangularized-element-as-well-as-the-restangularized-one) - [Restangular fails with status code 0](#restangular-fails-with-status-code-0) - [Why does this depend on Lodash / Underscore?](#why-does-this-depend-on-lodash--underscore) - [Supported Angular versions](#supported-angular-versions) - [Server Frameworks](#server-frameworks) - [Releases Notes](#releases-notes) - [License](#license) **[Back to top](#table-of-contents)** ## Differences with $resource Restangular has several features that distinguish it from $resource: * **It uses [promises](http://docs.angularjs.org/api/ng.$q)**. Instead of doing the "magic" filling of objects like $resource, it uses promises. * **You can use this in $routeProvider.resolve**. As Restangular returns promises, you can return any of the methods in the `$routeProvider.resolve` and you'll get the real object injected into your controller if you want. * **It doesn't have all those `$resource` bugs**. Restangular doesn't have problem with trailing slashes, additional `:` in the URL, escaping information, expecting only arrays for getting lists, etc. * **It supports all HTTP methods**. * **It supports ETag out of the box**. You don't have to do anything. ETags and If-None-Match will be used in all of your requests * **It supports self linking elements** If you receive from the server some item that has a link to itself, you can use that to query the server instead of writing the URL manually. * **You don't have to create one $resource object per request**. Each time you want to do a request, you can just do it using the object that was returned by Restangular. You don't need to create a new object for this. * **You don't have to write or remember ANY URL**. With $resource, you need to write the URL Template. In here, you don't write any urls. You just write the name of the resource you want to fetch and that's it. * **It supports nested RESTful resources**. If you have Nested RESTful resources, Restangular can handle them for you. You don't have to know the URL, the path, or anything to do all of the HTTP operations you want. * **Restangular lets you create your own methods**. You can create your own methods to run the operation that you want. The sky is the limit. * **Support for wrapped responses**. If your response for a list of element actually returns an object with some property inside which has the list, it's very hard to use $resource. Restangular knows that and it makes it easy on you. Check out https://github.com/mgonto/restangular#my-response-is-actually-wrapped-with-some-metadata-how-do-i-get-the-data-in-that-case * **You can build your own URLs with Restangular objects easily**. Restangular lets you create a Restangular object for any url you want with a really nice builder. Let's see a quick and short example of these features ````javascript // Restangular returns promises Restangular.all('users').getList() // GET: /users .then(function(users) { // returns a list of users $scope.user = users[0]; // first Restangular obj in list: { id: 123 } }) // Later in the code... // Restangular objects are self-aware and know how to make their own RESTful requests $scope.user.getList('cars'); // GET: /users/123/cars // You can also use your own custom methods on Restangular objects $scope.user.sendMessage(); // POST: /users/123/sendMessage // Chain methods together to easily build complex requests $scope.user.one('messages', 123).one('from', 123).getList('unread'); // GET: /users/123/messages/123/from/123/unread ```` **[Back to top](#table-of-contents)** #How do I add this to my project? You can download this by: * Using bower and running `bower install restangular` * Using npm and running `npm install restangular` * Downloading it manually by clicking [here to download development unminified version](https://raw.github.com/mgonto/restangular/master/dist/restangular.js) or [here to download minified production version](https://raw.github.com/mgonto/restangular/master/dist/restangular.min.js) * Using [CdnJS CDN files](http://cdnjs.com/libraries/restangular/): ````html ```` **[Back to top](#table-of-contents)** #Dependencies Restangular depends on Angular and Lodash (or Underscore). **[Back to top](#table-of-contents)** # Production apps using Restangular Each time, there're more Production WebApps using `Restangular`. If your webapp uses it and it's not in the list, please create an issue or submit a PR: * **Life360** is using Restangular to build the WebApp version of their platform * **Thomson Reuters** is using Restangular for the new Webapp they've built * **Quran.com** is using Restangular for their alpha/beta app and soon to be main site **[Back to top](#table-of-contents)** #Starter Guide ## Quick Configuration (For Lazy Readers) This is all you need to start using all the basic Restangular features. ````javascript // Add Restangular as a dependency to your app angular.module('your-app', ['restangular']); // Inject Restangular into your controller angular.module('your-app').controller('MainCtrl', function($scope, Restangular) { // ... }); ```` The Restangular service may be injected into any Controller or Directive :) Note: When adding Restangular as a dependency it is not capitalized 'restangular' But when injected into your controller it is 'Restangular' **[Back to top](#table-of-contents)** ## Using Restangular ### Creating Main Restangular object There are 3 ways of creating a main Restangular object. The first one and most common one is by stating the main route of all requests. The second one is by stating the main route and object of all requests. ````javascript // Only stating main route Restangular.all('accounts') // Stating main object Restangular.one('accounts', 1234) // Gets a list of all of those accounts Restangular.several('accounts', 1234, 123, 12345); ```` **[Back to top](#table-of-contents)** ### Let's code! Now that we have our main Object let's start playing with it. ````javascript // First way of creating a Restangular object. Just saying the base URL var baseAccounts = Restangular.all('accounts'); // This will query /accounts and return a promise. baseAccounts.getList().then(function(accounts) { $scope.allAccounts = accounts; }); // Does a GET to /accounts // Returns an empty array by default. Once a value is returned from the server // that array is filled with those values. So you can use this in your template $scope.accounts = Restangular.all('accounts').getList().$object; var newAccount = {name: "Gonto's account"}; // POST /accounts baseAccounts.post(newAccount); // GET to http://www.google.com/ You set the URL in this case Restangular.allUrl('googlers', 'http://www.google.com/').getList(); // GET to http://www.google.com/1 You set the URL in this case Restangular.oneUrl('googlers', 'http://www.google.com/1').get(); // You can do RequestLess "connections" if you need as well // Just ONE GET to /accounts/123/buildings/456 Restangular.one('accounts', 123).one('buildings', 456).get() // Just ONE GET to /accounts/123/buildings Restangular.one('accounts', 123).getList('buildings') // Here we use Promises then // GET /accounts baseAccounts.getList().then(function (accounts) { // Here we can continue fetching the tree :). var firstAccount = accounts[0]; // This will query /accounts/123/buildings considering 123 is the id of the firstAccount $scope.buildings = firstAccount.getList("buildings"); // GET /accounts/123/places?query=param with request header: x-user:mgonto $scope.loggedInPlaces = firstAccount.getList("places", {query: param}, {'x-user': 'mgonto'}) // This is a regular JS object, we can change anything we want :) firstAccount.name = "Gonto" // If we wanted to keep the original as it is, we can copy it to a new element var editFirstAccount = Restangular.copy(firstAccount); editFirstAccount.name = "New Name"; // PUT /accounts/123. The name of this account will be changed from now on firstAccount.put(); editFirstAccount.put(); // PUT /accounts/123. Save will do POST or PUT accordingly firstAccount.save(); // DELETE /accounts/123 We don't have first account anymore :( firstAccount.remove(); var myBuilding = { name: "Gonto's Building", place: "Argentina" }; // POST /accounts/123/buildings with MyBuilding information firstAccount.post("Buildings", myBuilding).then(function() { console.log("Object saved OK"); }, function() { console.log("There was an error saving"); }); // GET /accounts/123/users?query=params firstAccount.getList("users", {query: params}).then(function(users) { // Instead of posting nested element, a collection can post to itself // POST /accounts/123/users users.post({userName: 'unknown'}); // Custom methods are available now :). // GET /accounts/123/users/messages?param=myParam users.customGET("messages", {param: "myParam"}) var firstUser = users[0]; // GET /accounts/123/users/456. Just in case we want to update one user :) $scope.userFromServer = firstUser.get(); // ALL http methods are available :) // HEAD /accounts/123/users/456 firstUser.head() }); }, function errorCallback() { alert("Oops error from server :("); }) // Second way of creating Restangular object. URL and ID :) var account = Restangular.one("accounts", 123); // GET /accounts/123?single=true $scope.account = account.get({single: true}); // POST /accounts/123/messages?param=myParam with the body of name: "My Message" account.customPOST({name: "My Message"}, "messages", {param: "myParam"}, {}) ```` **[Back to top](#table-of-contents)** ## Configuring Restangular ### Properties Restangular comes with defaults for all of its properties but you can configure them. **So, if you don't need to configure something, there's no need to add the configuration.** You can set all these configurations in **`RestangularProvider` or `Restangular` service to change the global configuration** or you can **use the withConfig method in Restangular service to create a new Restangular service with some scoped configuration**. Check the section on this later. #### setBaseUrl The base URL for all calls to your API. For example if your URL for fetching accounts is http://example.com/api/v1/accounts, then your baseUrl is `/api/v1`. The default baseUrl is an empty string which resolves to the same url that AngularJS is running, so you can also set an absolute url like `http://api.example.com/api/v1` if you need do set another domain. #### setExtraFields These are the fields that you want to save from your parent resources if you need to display them. By default this is an Empty Array which will suit most cases #### setParentless Use this property to control whether Restangularized elements to have a parent or not. So, for example if you get an account and then get a nested list of buildings, you may want the buildings URL to be simple `/buildings/123` instead of `/accounts/123/buildings/123`. This property lets you do that. This method accepts 1 parameter, it could be: * Boolean: Specifies if all elements should be parentless or not * Array: Specifies the routes (types) of all elements that should be parentless. For example `['buildings']` #### setDefaultHttpFields `$http` from AngularJS can receive a bunch of parameters like `cache`, `transformRequest` and so on. You can set all of those properties in the object sent on this setter so that they will be used in EVERY API call made by Restangular. This is very useful for caching for example. All properties that can be set can be checked here: http://docs.angularjs.org/api/ng.$http#parameters #### addElementTransformer This is a hook. After each element has been "restangularized" (Added the new methods from Restangular), the corresponding transformer will be called if it fits. This should be used to add your own methods / functions to entities of certain types. You can add as many element transformers as you want. The signature of this method can be one of the following: * **addElementTransformer(route, transformer)**: Transformer is called with all elements that have been restangularized, no matter if they're collections or not. * **addElementTransformer(route, isCollection, transformer)**: Transformer is called with all elements that have been restangularized and match the specification regarding if it's a collection or not (true | false) #### setTransformOnlyServerElements This sets whether transformers will be run for local objects and not by objects returned by the server. This is by default true but can be changed to false if needed (Most people won't need this). #### setOnElemRestangularized This is a hook. After each element has been "restangularized" (Added the new methods from Restangular), this will be called. It means that if you receive a list of objects in one call, this method will be called first for the collection and then for each element of the collection. **I favor the usage of `addElementTransformer` instead of `onElemRestangularized` whenever possible as the implementation is much cleaner.** This callback is a function that has 3 parameters: * **elem**: The element that has just been restangularized. Can be a collection or a single element. * **isCollection**: Boolean indicating if this is a collection or a single element. * **what**: The model that is being modified. This is the "path" of this resource. For example `buildings` * **Restangular**: The instanced service to use any of its methods This can be used together with `addRestangularMethod` (Explained later) to add custom methods to an element #### setResponseInterceptor **This is deprecated. Use addResponseInterceptor since you can add more than one**. #### addResponseInterceptor The responseInterceptor is called after we get each response from the server. It's a function that receives this arguments: * **data**: The data received got from the server * **operation**: The operation made. It'll be the HTTP method used except for a `GET` which returns a list of element which will return `getList` so that you can distinguish them. * **what**: The model that's being requested. It can be for example: `accounts`, `buildings`, etc. * **url**: The relative URL being requested. For example: `/api/v1/accounts/123` * **response**: Full server response including headers * **deferred**: The deferred promise for the request. Some of the use cases of the responseInterceptor are handling wrapped responses and enhancing response elements with more methods among others. The responseInterceptor must return the restangularized data element. #### setRequestInterceptor **This is deprecated. Use addRequestInterceptor since you can add more than one**. #### addRequestInterceptor The requestInterceptor is called before sending any data to the server. It's a function that must return the element to be requested. This function receives the following arguments: * **element**: The element to send to the server. * **operation**: The operation made. It'll be the HTTP method used except for a `GET` which returns a list of element which will return `getList` so that you can distinguish them. * **what**: The model that's being requested. It can be for example: `accounts`, `buildings`, etc. * **url**: The relative URL being requested. For example: `/api/v1/accounts/123` #### setFullRequestInterceptor **This is deprecated. Use addFullRequestInterceptor since you can add more than one**. #### addFullRequestInterceptor This adds a new fullRequestInterceptor. The fullRequestInterceptor is similar to the `requestInterceptor` but more powerful. It lets you change the element, the request parameters and the headers as well. It's a function that receives the same as the `requestInterceptor` plus the headers and the query parameters (in that order). It can return an object with any (or all) of following properties: * **headers**: The headers to send * **params**: The request parameters to send * **element**: The element to send * **httpConfig**: The httpConfig to call with If a property isn't returned, the one sent is used. #### setErrorInterceptor The errorInterceptor is called whenever there's an error. It's a function that receives the response, the deferred (for the promise) and the Restangular-response handler as parameters. The errorInterceptor function, whenever it returns `false`, prevents the promise linked to a Restangular request to be executed. All other return values (besides `false`) are ignored and the promise follows the usual path, eventually reaching the success or error hooks. The feature to prevent the promise to complete is useful whenever you need to intercept each Restangular error response for every request in your AngularJS application in a single place, increasing debugging capabilities and hooking security features in a single place. ````javascript var refreshAccesstoken = function() { var deferred = $q.defer(); // Refresh access-token logic return deferred.promise; }; Restangular.setErrorInterceptor(function(response, deferred, responseHandler) { if(response.status === 403) { refreshAccesstoken().then(function() { // Repeat the request and then call the handlers the usual way. $http(response.config).then(responseHandler, deferred.reject); // Be aware that no request interceptors are called this way. }); return false; // error handled } return true; // error not handled }); ```` #### setRestangularFields Restangular required 3 fields for every "Restangularized" element. These are: * id: Id of the element. Default: id * route: Name of the route of this element. Default: route * parentResource: The reference to the parent resource. Default: parentResource * restangularCollection: A boolean indicating if this is a collection or an element. Default: restangularCollection * cannonicalId: If available, the path to the cannonical ID to use. Useful for PK changes * etag: Where to save the ETag received from the server. Defaults to `restangularEtag` * selfLink: The path to the property that has the URL to this item. If your REST API doesn't return a URL to an item, you can just leave it blank. Defaults to `href` Also all of Restangular methods and functions are configurable through restangularFields property. All of these fields except for `id` and `selfLink` are handled by Restangular, so most of the time you won't change them. You can configure the name of the property that will be binded to all of this fields by setting restangularFields property. #### setMethodOverriders You can now Override HTTP Methods. You can set here the array of methods to override. All those methods will be sent as POST and Restangular will add an X-HTTP-Method-Override header with the real HTTP method we wanted to do. #### setJsonp By setting this value to true, both `get` and `getList` will be performed using JSonp instead of the regular GET. You will need to add the 'JSON_CALLBACK' string to your URLs (see [$http.jsonp](http://docs.angularjs.org/api/ng.$http#methods_jsonp)). You can use `setDefaultRequestParams` to accomplish this: ```javascript RestangularProvider.setDefaultRequestParams('jsonp', {callback: 'JSON_CALLBACK'}); ``` #### setDefaultRequestParams You can set default Query parameters to be sent with every request and every method. Additionally, if you want to configure request params per method, you can use `requestParams` configuration similar to `$http`. For example `RestangularProvider.requestParams.get = {single: true}`. Supported method to configure are: remove, get, post, put, common (all) ````javascript // set params for multiple methods at once Restangular.setDefaultRequestParams(['remove', 'post'], {confirm: true}); // set only for get method Restangular.setDefaultRequestParams('get', {limit: 10}); // or for all supported request methods Restangular.setDefaultRequestParams({apikey: "secret key"}); ```` #### setFullResponse You can set fullResponse to true to get the whole response every time you do any request. The full response has the restangularized data in the `data` field, and also has the headers and config sent. By default, it's set to false. Please note that in order for Restangular to access custom HTTP headers, your server must respond having the `Access-Control-Expose-Headers:` set. ````javascript // set params for multiple methods at once Restangular.setFullResponse(true); ```` Or set it per service ````javascript // Restangular service that uses setFullResponse app.factory('RestFulResponse', function(Restangular) { return Restangular.withConfig(function(RestangularConfigurer) { RestangularConfigurer.setFullResponse(true); }); }); // Let's use it in the controller app.controller('MainCtrl', function(Restangular, RestFulResponse) { // Uses full response configuration RestFulResponse.all('users').getList().then(function(response) { $scope.users = response.data; console.log(response.headers); }); }); ```` #### setDefaultHeaders You can set default Headers to be sent with every request. Send format: {header_name: header_value} ````javascript // set default header "token" RestangularProvider.setDefaultHeaders({token: "x-restangular"}); ```` #### setRequestSuffix If all of your requests require to send some suffix to work, you can set it here. For example, if you need to send the format like `/users/123.json` you can add that `.json` to the suffix using the `setRequestSuffix` method #### setUseCannonicalId You can set this to either `true` or `false`. By default it's false. If set to true, then the cannonical ID from the element will be used for URL creation (in DELETE, PUT, POST, etc.). What this means is that if you change the ID of the element and then you do a put, if you set this to true, it'll use the "old" ID which was received from the server. If set to false, it'll use the new ID assigned to the element. #### setEncodeIds You can set here if you want to URL Encode IDs or not. By default, it's true. **[Back to top](#table-of-contents)** ### Accessing configuration You can also access the configuration via `RestangularProvider` and `Restangular` via the `configuration` property if you don't want to use the setters. Check it out: ````js Restangular.configuration.requestSuffix = '/'; ```` **[Back to top](#table-of-contents)** ### How to configure them globally You can configure this in either the `config` or the `run` method. If your configurations don't need any other services, then I'd recommend you do them in the `config`. If your configurations depend on other services, you can configure them in the `run` using `Restangular` instead of `RestangularProvider` #### Configuring in the `config` ````javascript app.config(function(RestangularProvider) { RestangularProvider.setBaseUrl('/api/v1'); RestangularProvider.setExtraFields(['name']); RestangularProvider.setResponseExtractor(function(response, operation) { return response.data; }); RestangularProvider.addElementTransformer('accounts', false, function(element) { element.accountName = 'Changed'; return element; }); RestangularProvider.setDefaultHttpFields({cache: true}); RestangularProvider.setMethodOverriders(["put", "patch"]); // In this case we are mapping the id of each element to the _id field. // We also change the Restangular route. // The default value for parentResource remains the same. RestangularProvider.setRestangularFields({ id: "_id", route: "restangularRoute", selfLink: "self.href" }); RestangularProvider.setRequestSuffix('.json'); // Use Request interceptor RestangularProvider.setRequestInterceptor(function(element, operation, route, url) { delete element.name; return element; }); // ..or use the full request interceptor, setRequestInterceptor's more powerful brother! RestangularProvider.setFullRequestInterceptor(function(element, operation, route, url, headers, params, httpConfig) { delete element.name; return { element: element, params: _.extend(params, {single: true}), headers: headers, httpConfig: httpConfig }; }); }); ```` #### Configuring in the `run` ````javascript // Here I inject the service BaseUrlCalculator which I need app.run(function(Restangular, BaseUrlCalculator) { Restangular.setBaseUrl(BaseUrlCalculator.calculate()); }); ```` **[Back to top](#table-of-contents)** ### How to create a Restangular service with a different configuration from the global one Let's assume that for most requests you need some configuration (The global one), and for just a bunch of methods you need another configuration. In that case, you'll need to create another Restangular service with this particular configuration. This scoped configuration will inherit all defaults from the global one. Let's see how. ````javascript // Global configuration app.config(function(RestangularProvider) { RestangularProvider.setBaseUrl('http://www.google.com'); RestangularProvider.setRequestSuffix('.json'); }); // Restangular service that uses Bing app.factory('BingRestangular', function(Restangular) { return Restangular.withConfig(function(RestangularConfigurer) { RestangularConfigurer.setBaseUrl('http://www.bing.com'); }); }); // Let's use them from a controller app.controller('MainCtrl', function(Restangular, BingRestangular) { // GET to http://www.google.com/users.json // Uses global configuration Restangular.all('users').getList() // GET to http://www.bing.com/users.json // Uses Bing configuration which is based on Global one, therefore .json is added. BingRestangular.all('users').getList() }); ```` **[Back to top](#table-of-contents)** ### Decoupled Restangular Service There're some times where you want to use Restangular but you don't want to expose Restangular object anywhere. For those cases, you can actually use the `service` feature of Restangular. Let's see how it works: ````js // Declare factory module.factory('Users', function(Restangular) { return Restangular.service('users'); }); // In your controller you inject Users Users.get(2) // GET to /users/2 Users.post({data}) // POST to /users // GET to /users Users.getList().then(function(users) { var user = users[0]; // user === {id: 1, name: "Tonto"} user.name = "Gonto"; // PUT to /users/1 user.put(); }) ```` We can also use Nested RESTful resources with this: ````js var Cars = Restangular.service('cars', Restangular.one('users', 1)); Cars.getList() // GET to /users/1/cars ```` **[Back to top](#table-of-contents)** ## Methods description There are 3 sets of methods. Collections have some methods and elements have others. There are are also some common methods for all of them ### Restangular methods These are the methods that can be called on the Restangular object. * **one(route, id)**: This will create a new Restangular object that is just a pointer to one element with the route `route` and the specified id. * **all(route)**: This will create a new Restangular object that is just a pointer to a list of elements for the specified path. * **oneUrl(route, url)**: This will create a new Restangular object that is just a pointer to one element with the specified URL. * **allUrl(route, url)**: This creates a Restangular object that is just a pointer to a list at the specified URL. * **copy(fromElement)**: This will create a copy of the from element so that we can modify the copied one. * **restangularizeElement(parent, element, route, queryParams)**: Restangularizes a new element * **restangularizeCollection(parent, element, route, queryParams)**: Restangularizes a new collection **[Back to top](#table-of-contents)** ### Element methods * **get([queryParams, headers])**: Gets the element. Query params and headers are optionals * **getList(subElement, [queryParams, headers])**: Gets a nested resource. subElement is mandatory. **It's a string with the name of the nested resource (and URL)**. For example `buildings` * **put([queryParams, headers])**: Does a put to the current element * **post(subElement, elementToPost, [queryParams, headers])**: Does a POST and creates a subElement. Subelement is mandatory and is the nested resource. Element to post is the object to post to the server * **remove([queryParams, headers])**: Does a DELETE. By default, `remove` sends a request with an empty object, which may cause problems with some servers or browsers. [This](https://github.com/mgonto/restangular/issues/193) shows how to configure RESTangular to have no payload. * **head([queryParams, headers])**: Does a HEAD * **trace([queryParams, headers])**: Does a TRACE * **options([queryParams, headers])**: Does a OPTIONS * **patch(object, [queryParams, headers])**: Does a PATCH * **one(route, id)**: Used for RequestLess connections and URL Building. See section below. * **all(route)**: Used for RequestLess connections and URL Building. See section below. * **several(route, ids*)**: Used for RequestLess connections and URL Building. See section below. * **oneUrl(route, url)**: This will create a new Restangular object that is just a pointer to one element with the specified URL. * **allUrl(route, url)**: This creates a Restangular object that is just a pointer to a list at the specified URL. * **getRestangularUrl()**: Gets the URL of the current object. * **getRequestedUrl()**: Gets the real URL the current object was requested with (incl. GET parameters). Will equal getRestangularUrl() when no parameters were used, before calling `get()`, or when using on a nested child. * **getParentList()**: Gets the parent list to which it belongs (if any) * **clone()**: Copies the element. It's an alias to calling `Restangular.copy(elem)`. * **plain()**: Returns the plain element received from the server without any of the enhanced methods from Restangular. It's an alias to calling `Restangular.stripRestangular(elem)` * **withHttpConfig(httpConfig)**: It lets you set a configuration for $http only for the next call. Check the Local Config HTTP section for an example. * **save**: Calling save will determine whether to do PUT or POST accordingly **[Back to top](#table-of-contents)** ### Collection methods * **getList([queryParams, headers]): Gets itself again (Remember this is a collection)**. * **get([id]): Gets one item from the collection by id**. * **post(elementToPost, [queryParams, headers])**: Creates a new element of this collection. * **head([queryParams, headers])**: Does a HEAD * **trace: ([queryParams, headers])**: Does a TRACE * **options: ([queryParams, headers])**: Does a OPTIONS * **patch(object, [queryParams, headers])**: Does a PATCH * **remove([queryParams, headers])**: Does a DELETE. By default, `remove` sends a request with an empty object, which may cause problems with some servers or browsers. [This](https://github.com/mgonto/restangular/issues/193) shows how to configure RESTangular to have no payload. * **putElement(idx, params, headers)**: Puts the element on the required index and returns a promise of the updated new array * **getRestangularUrl()**: Gets the URL of the current object. * **getRequestedUrl()**: Gets the real URL the current object was requested with (incl. GET parameters). Will equal getRestangularUrl() when no parameters were used, before calling `getList()`, or when using on a nested child. * **one(route, id)**: Used for RequestLess connections and URL Building. See section below. * **all(route)**: Used for RequestLess connections and URL Building. See section below. * **several(route, ids*)**: Used for RequestLess connections and URL Building. See section below. * **oneUrl(route, url)**: This will create a new Restangular object that is just a pointer to one element with the specified URL. * **allUrl(route, url)**: This creates a Restangular object that is just a pointer to a list at the specified URL. * **clone()**: Copies the collection. It's an alias to calling `Restangular.copy(collection)`. * **withHttpConfig(httpConfig)**: It lets you set a configuration for $http only for the next call. Check the Local Config HTTP section for an example. **[Back to top](#table-of-contents)** ### Custom methods * **customGET(path, [params, headers])**: Does a GET to the specific path. Optionally you can set params and headers. * **customGETLIST(path, [params, headers])**: Does a GET to the specific path. **In this case, you expect to get an array, not a single element**. Optionally you can set params and headers. * **customDELETE(path, [params, headers])**: Does a DELETE to the specific path. Optionally you can set params and headers. * **customPOST([elem, path, params, headers])**: Does a POST to the specific path. Optionally you can set params and headers and elem. Elem is the element to post. If it's not set, it's assumed that it's the element itself from which you're calling this function. * **customPUT([elem, path, params, headers])**: Does a PUT to the specific path. Optionally you can set params and headers and elem. Elem is the element to post. If it's not set, it's assumed that it's the element itself from which you're calling this function. * **customOperation(operation, path, [params, headers, elem])**: This does a custom operation to the path that we specify. This method is actually used from all the others in this subsection. Operation can be one of: get, post, put, delete, head, options, patch, trace * **addRestangularMethod(name, operation, [path, params, headers, elem])**: This will add a new restangular method to this object with the name `name` to the operation and path specified (or current path otherwise). There's a section on how to do this later. Let's see an example of this: ````javascript // GET /accounts/123/messages Restangular.one("accounts", 123).customGET("messages") // GET /accounts/messages?param=param2 Restangular.all("accounts").customGET("messages", {param: "param2"}) ```` All custom methods have an alias where you replace `custom` by `do`. For example, `customGET` is equal to `doGET`. Just pick whatever syntax you prefer. **[Back to top](#table-of-contents)** ## Copying elements Before modifying an object, we sometimes want to copy it and then modify the copied object. We can't use `angular.copy` for this because it'll not change the `this` binded in the functions we add to the object. In this cases, you must use `Restangular.copy(fromElement)`. **[Back to top](#table-of-contents)** ## Enhanced promises Restangular uses enhanced promises when returning. What does this mean? All promises returned now have 2 additional methods and collection promises have 3. These are the methods: * **call(methodName, params*)**: This will return a new promise of the previous value, after calling the method called methodName with the parameters params. * **get(fieldName)**: This will return a new promise for the type of the field. The param of this new promise is the property `fieldName` from the original promise result. * **push(object)**: This method will only be in the promises of arrays. It's a subset of the call method that does a push. * **$object**: This returns the reference to the object that will be filled once the server responds a value. This means that if you call `getList` this will be an empty array by default. Once the array is returned from the server, this same `$object` property will get filled with results from the server. I know these explanations are quite complicated, so let's see an example :D. ````javascript var buildings = Restangular.all("buildings").getList(); // New promise after adding the new building // Now you can show in scope this newBuildings promise and it'll show all the buildings // received from server plus the new one added var newBuildings = buildings.push({name: "gonto"}); var newBuildingsSame = buildings.call("push", {name: "gonto"}); // This is a promise of a number value. You can show it in the UI var lengthPromise = buildings.get("length"); lengthPromise.then(function(length) { // Here the length is the real length value of the returned collection of buildings }); ```` **[Back to top](#table-of-contents)** ## Using values directly in templates Since Angular 1.2, Promise unwrapping in templates has been disabled by default and will be deprecated soon. **This means that the following will cease to work**: ````js $scope.accounts = Restangular.all('accounts').getList(); ```` ````html