This project is the follow-up of the Restangular. Ngx-restangular is an Angular 2+ 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.
You can check post about using ngx-restangular in RESTful API Angular Solution
Ngx-restangular almost all functionality was transferred from the Restangular.We are open to any cooperation in terms of its further development.
This project was renamed from ng2-restangular to ngx-restangular due to implementation of Semantic Versioning by Angular Core Team. NPM name has also changed, you can install actual version of the project with npm install ngx-restangular
.
You can download this by npm and running npm install ngx-restangular
. This will install latest version of ngx-restangular (v.2.0.0).
You can download this by npm and running npm install --save ngx-restangular@1.0.13
Versions from 1.0.14 to 1.1.4 are deprecated. Npm warns you after their installation. Those versions would be removed.
You can download this by npm and running npm install ng2-restangular
Restangular depends on Angular 2+ and Lodash.
This is all you need to start using all the basic Restangular features.
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { RestangularModule, Restangular } from 'ngx-restangular';
// Function for setting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider) {
RestangularProvider.setBaseUrl('http://api.restngx.local/v1');
RestangularProvider.setDefaultHeaders({'Authorization': 'Bearer UDXPx-Xko0w4BRKajozCVy20X11MRZs1'});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
// Importing RestangularModule and making default configs for restanglar
RestangularModule.forRoot(RestangularConfigFactory),
]
})
export class AppModule {
}
// later in code ...
@Component({
...
})
export class OtherComponent {
constructor(private restangular: Restangular) {
}
ngOnInit() {
// GET http://api.test.local/v1/users/2/accounts
this.restangular.one('users', 2).all('accounts').getList();
}
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.
// 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);
Now that we have our main Object let's start playing with it.
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
// Importing RestangularModule
RestangularModule,
]
})
export class AppModule {
}
@Component({
...
})
export class OtherComponent {
allAccounts;
accounts;
account;
constructor(private restangular: Restangular) {
}
ngOnInit() {
// First way of creating a this.restangular object. Just saying the base URL
let baseAccounts = this.restangular.all('accounts');
// This will query /accounts and return a observable.
baseAccounts.getList().subscribe(accounts => {
this.allAccounts = accounts;
});
let newAccount = {name: "Gonto's account"};
// POST /accounts
baseAccounts.post(newAccount);
// GET to http://www.google.com/ You set the URL in this case
this.restangular.allUrl('googlers', 'http://www.google.com/').getList();
// GET to http://www.google.com/1 You set the URL in this case
this.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
this.restangular.one('accounts', 123).one('buildings', 456).get();
// Just ONE GET to /accounts/123/buildings
this.restangular.one('accounts', 123).getList('buildings');
// Here we use Observables
// GET /accounts
let baseAccounts$ = baseAccounts.getList().subscribe(accounts => {
// Here we can continue fetching the tree :).
let firstAccount = accounts[0];
// This will query /accounts/123/buildings considering 123 is the id of the firstAccount
let buildings = firstAccount.getList("buildings");
// GET /accounts/123/places?query=param with request header: x-user:mgonto
let 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
let editFirstAccount = this.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();
}, () => {
alert("Oops error from server :(");
});
// Get first account
let firstAccount$ = baseAccounts$.map(accounts => accounts[0]);
// POST /accounts/123/buildings with MyBuilding information
firstAccount$.switchMap(firstAccount => {
var myBuilding = {
name: "Gonto's Building",
place: "Argentina"
};
return firstAccount.post("Buildings", myBuilding)
})
.subscribe(() => {
console.log("Object saved OK");
}, () => {
console.log("There was an error saving");
});
// GET /accounts/123/users?query=params
firstAccount$.switchMap(firstAccount => {
var myBuilding = {
name: "Gonto's Building",
place: "Argentina"
};
return firstAccount.getList("users", {query: 'params'});
})
.subscribe((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 :)
let userFromServer = firstUser.get();
// ALL http methods are available :)
// HEAD /accounts/123/users/456
firstUser.head()
}, () => {
console.log("There was an error saving");
});
// Second way of creating this.restangular object. URL and ID :)
var account = this.restangular.one("accounts", 123);
// GET /accounts/123?single=true
this.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"}, {})
}
}
@Component({
...
})
export class OtherComponent {
allAccounts;
accounts;
account;
constructor(private restangular: Restangular) {
}
ngOnInit() {
// First way of creating a this.restangular object. Just saying the base URL
let baseAccounts = this.restangular.all('accounts');
// This will query /accounts and return a promise.
baseAccounts.getList().toPromise().then(function(accounts) {
this.allAccounts = accounts;
});
var newAccount = {name: "Gonto's account"};
// POST /accounts
baseAccounts.post(newAccount);
// GET to http://www.google.com/ You set the URL in this case
this.restangular.allUrl('googlers', 'http://www.google.com/').getList();
// GET to http://www.google.com/1 You set the URL in this case
this.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
this.restangular.one('accounts', 123).one('buildings', 456).get();
// Just ONE GET to /accounts/123/buildings
this.restangular.one('accounts', 123).getList('buildings');
// Here we use Promises then
// GET /accounts
baseAccounts.getList().toPromise().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
this.buildings = firstAccount.getList("buildings");
// GET /accounts/123/places?query=param with request header: x-user:mgonto
this.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 = this.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).toPromise().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'}).toPromise().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 :)
this.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 this.restangular object. URL and ID :)
var account = this.restangular.one("accounts", 123);
// GET /accounts/123?single=true
this.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"}, {})
}
}
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 RestangularModule to change the global configuration, you can also use the withConfig method in Restangular service to create a new Restangular service with some scoped configuration or use withConfig in component to make specified Restangular
You can configure Restangular "withConfig" like in example below, you can also configure them globally RestangularModule or in service with withConfig
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider) {
RestangularProvider.setBaseUrl('http://www.google.com');
}
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
// Global configuration
RestangularModule.forRoot(RestangularConfigFactory),
]
})
export class AppModule {}
// Let's use it in the component
@Component({
...
})
export class OtherComponent {
constructor(private restangular: Restangular) {}
ngOnInit() {
restangular.withConfig((RestangularConfigurer) => {
RestangularConfigurer.setBaseUrl('http://www.bing.com');
}).all('users').getList()
}
};
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 Angular2 is running, but you can also set an absolute url like http://api.example.com/api/v1
if you need to set another domain.
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
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:
['buildings']
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)
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).
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 4 parameters:
buildings
This can be used together with addRestangularMethod
(Explained later) to add custom methods to an element
service.setOnElemRestangularized((element, isCollection, what, Restangular) => {
element.newField = "newField";
return element;
});
The responseInterceptor is called after we get each response from the server. It's a function that receives this arguments:
GET
which returns a list of element which will return getList
so that you can distinguish them.accounts
, buildings
, etc./api/v1/accounts/123
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.
RestangularProvider.addResponseInterceptor((data, operation, what, url, response)=> {
return data;
});
});
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:
RestangularProvider.addFullRequestInterceptor((element, operation, path, url, headers, params)=> {
return {
params: Object.assign({}, params, {sort:"name"}),
headers: headers,
element: element
}
});
If a property isn't returned, the one sent is used.
The errorInterceptor is called whenever there's an error. It's a function that receives the response, subject and the Restangular-response handler as parameters.
The errorInterceptor function, whenever it returns false, prevents the observable linked to a Restangular request to be executed. All other return values (besides false) are ignored and the observable follows the usual path, eventually reaching the success or error hooks.
The refreshAccesstoken function must return observable. It`s function that will be done before repeating the request, there you can make some actions. In switchMap you might do some transformations to request.
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider, authService) {
RestangularProvider.setBaseUrl('http://api.test.com/v1');
// This function must return observable
var refreshAccesstoken = function () {
// Here you can make action before repeated request
return authService.functionForTokenUpdate();
};
RestangularProvider.addErrorInterceptor((response, subject, responseHandler) => {
if (response.status === 403) {
refreshAccesstoken()
.switchMap(refreshAccesstokenResponse => {
//If you want to change request or make with it some actions and give the request to the repeatRequest func.
//Or you can live it empty and request will be the same.
// update Authorization header
const newHeaders = response.request.headers.set('Authorization', 'Bearer ' + refreshAccesstokenResponse);
const newRequest = response.request.clone({headers: newHeaders});
return response.repeatRequest(newRequest);
})
.subscribe(
res => responseHandler(res),
err => subject.error(err)
);
return false; // error handled
}
return true; // error not handled
});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
imports: [
// Importing RestangularModule and making default configs for restanglar
RestangularModule.forRoot([authService], RestangularConfigFactory),
],
})
Restangular required 3 fields for every "Restangularized" element. These are:
restangularEtag
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.
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.
RestangularProvider.setMethodOverriders(["Get","Put"]);
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)
// set params for multiple methods at once
RestangularProvider.setDefaultRequestParams(['remove', 'post'], {confirm: true});
// set only for get method
RestangularProvider.setDefaultRequestParams('get', {limit: 10});
// or for all supported request methods
RestangularProvider.setDefaultRequestParams({apikey: "secret key"});
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.
// set params for multiple methods at once
RestangularProvider.setFullResponse(true);
Or set it per service
// Restangular factory that uses setFullResponse
export const REST_FUL_RESPONSE = new InjectionToken<any>('RestFulResponse');
export function RestFulResponseFactory(restangular: Restangular) {
return restangular.withConfig((RestangularConfigurer) => {
RestangularConfigurer.setFullResponse(true);
});
}
// Configure factory in AppModule module
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [RestangularModule],
providers: [
{ provide: REST_FUL_RESPONSE, useFactory: RestFulResponseFactory, deps: [Restangular] }
]
})
export class AppModule {}
// Let's use it in the component
@Component({
...
})
export class OtherComponent {
users;
constructor(@Inject(REST_FUL_RESPONSE) public restFulResponse) {
}
ngOnInit() {
this.restFulResponse.all('users').getList().subscribe( response => {
this.users = response.data;
console.log(response.headers);
});
}
}
You can set default Headers to be sent with every request. Send format: {header_name: header_value}
import { NgModule } from '@angular/core';
import { RestangularModule, Restangular } from 'ngx-restangular';
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider) {
RestangularProvider.setDefaultHeaders({'Authorization': 'Bearer UDXPx-Xko0w4BRKajozCVy20X11MRZs1'});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
...
imports: [
// Importing RestangularModule and making default configs for restanglar
RestangularModule.forRoot(RestangularConfigFactory),
]
})
export class AppModule {
}
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
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.
You can set this to true
or false
. By default it's false. If set to true, data retrieved will be returned with no embed methods from restangular.
You can set here if you want to URL Encode IDs or not. By default, it's true.
You can also access the configuration via RestangularModule
and Restangular.provider
via the configuration
property if you don't want to use the setters. Check it out:
RestangularProvider.configuration.requestSuffix = '/';
You can configure this in either the AppModule
.
AppModule
import { RestangularModule } from 'ngx-restangular';
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider) {
RestangularProvider.setBaseUrl('http://api.restngx.local/v1');
RestangularProvider.setDefaultHeaders({'Authorization': 'Bearer UDXPx-Xko0w4BRKajozCVy20X11MRZs1'});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
RestangularModule.forRoot(RestangularConfigFactory),
]
})
export class AppModule {
}
AppModule
with Dependency Injection appliedimport { RestangularModule } from 'ngx-restangular';
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider, http) {
RestangularProvider.setBaseUrl('http://api.restngx.local/v1');
RestangularProvider.setDefaultHeaders({'Authorization': 'Bearer UDXPx-Xko0w4BRKajozCVy20X11MRZs1'});
// Example of using Http service inside global config restangular
RestangularProvider.addElementTransformer('me', true, ()=>{
return http.get('http://api.test.com/v1/users/2', {});
});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
RestangularModule.forRoot([Http], RestangularConfigFactory),
]
})
export class AppModule {
}
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.
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider) {
RestangularProvider.setBaseUrl('http://www.google.com');
}
//Restangular service that uses Bing
export const RESTANGULAR_BING = new InjectionToken<any>('RestangularBing');
export function RestangularBingFactory(restangular: Restangular) {
return restangular.withConfig((RestangularConfigurer) => {
RestangularConfigurer.setBaseUrl('http://www.bing.com');
});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
// Global configuration
RestangularModule.forRoot(RestangularConfigFactory),
],
providers: [
{ provide: RESTANGULAR_BING, useFactory: RestangularBingFactory, deps: [Restangular] }
]
})
export class AppModule {}
// Let's use it in the component
@Component({
...
})
export class OtherComponent {
constructor(
@Inject(Restangular) public Restangular,
@Inject(RESTANGULAR_BING) public RestangularBing
) {}
ngOnInit() {
// GET to http://www.google.com/users
// Uses global configuration
Restangular.all('users').getList()
// GET to http://www.bing.com/users
// Uses Bing configuration which is based on Global one, therefore .json is added.
RestangularBing.all('users').getList()
}
};
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:
// Restangular factory that uses Users
export const USER_REST = new InjectionToken<any>('UserRest');
export function UserRestFactory(restangular: Restangular) {
return restangular.service('users');
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [RestangularModule],
providers: [
{ provide: USER_REST, useFactory: UserRestFactory, deps: [Restangular] } // Configurating our factory
]
})
export class AppModule {
}
// Let's use it in the component
export class OtherComponent {
constructor(@Inject(USER_REST) public User) {
Users.one(2).get() // GET to /users/2
Users.post({data}) // POST to /users
// GET to /users
Users.getList().subscribe( 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:
var Cars = Restangular.service('cars', Restangular.one('users', 1));
Cars.getList() // GET to /users/1/cars
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
These are the methods that can be called on the Restangular object.
route
and the specified id.buildings
remove
sends a request with an empty object, which may cause problems with some servers or browsers. This shows how to configure RESTangular to have no payload.get()
, or when using on a nested child.Restangular.copy(elem)
.Restangular.stripRestangular(elem)
remove
sends a request with an empty object, which may cause problems with some servers or browsers. This shows how to configure RESTangular to have no payload.Restangular.all('users').getList()
.subscribe( users => {
users.putElement(2, {'name': 'new name'});
});
getList()
, or when using on a nested child.Restangular.copy(collection)
.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:
// GET /accounts/123/messages
Restangular.one("accounts", 123).customGET("messages")
// GET /accounts/messages?param=param2
Restangular.all("accounts").customGET("messages", {param: "param2"})
Before modifying an object, we sometimes want to copy it and then modify the copied object. We can use Restangular.copy(fromElement)
.
If you want to use values directly in templates use AsyncPipe
this.accounts = this.restangular.all('accounts').getList();
<tr *ngFor="let account of accounts | async">
<td>{{account.fullName}}</td>
</tr>
Sometimes, we have a lot of nested entities (and their IDs), but we just want the last child. In those cases, doing a request for everything to get the last child is overkill. For those cases, I've added the possibility to create URLs using the same API as creating a new Restangular object. This connections are created without making any requests. Let's see how to do this:
var restangularSpaces = Restangular.one("accounts",123).one("buildings", 456).all("spaces");
// This will do ONE get to /accounts/123/buildings/456/spaces
restangularSpaces.getList()
// This will do ONE get to /accounts/123/buildings/456/spaces/789
Restangular.one("accounts", 123).one("buildings", 456).one("spaces", 789).get()
// POST /accounts/123/buildings/456/spaces
Restangular.one("accounts", 123).one("buildings", 456).all("spaces").post({name: "New Space"});
// DELETE /accounts/123/buildings/456
Restangular.one("accounts", 123).one("buildings", 456).remove();
Let's assume that your API needs some custom methods to work. If that's the case, always calling customGET or customPOST for that method with all parameters is a pain in the ass. That's why every element has a addRestangularMethod
method.
This can be used together with the hook addElementTransformer
to do some neat stuff. Let's see an example to learn this:
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider) {
// It will transform all building elements, NOT collections
RestangularProvider.addElementTransformer('buildings', false, function(building) {
// This will add a method called evaluate that will do a get to path evaluate with NO default
// query params and with some default header
// signature is (name, operation, path, params, headers, elementToPost)
building.addRestangularMethod('evaluate', 'get', 'evaluate', undefined, {'myHeader': 'value'});
return building;
});
RestangularProvider.addElementTransformer('users', true, function(user) {
// This will add a method called login that will do a POST to the path login
// signature is (name, operation, path, params, headers, elementToPost)
user.addRestangularMethod('login', 'post', 'login');
return user;
});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
imports: [ // import Angular's modules
RestangularModule.forRoot(RestangularConfigFactory),
],
})
// Then, later in your code you can do the following:
// GET to /buildings/123/evaluate?myParam=param with headers myHeader: value
// Signature for this "custom created" methods is (params, headers, elem) if it's a safe operation (GET, OPTIONS, etc.)
// If it's an unsafe operation (POST, PUT, etc.), signature is (elem, params, headers).
// If something is set to any of this variables, the default set in the method creation will be overridden
// If nothing is set, then the defaults are sent
Restangular.one('buildings', 123).evaluate({myParam: 'param'});
// GET to /buildings/123/evaluate?myParam=param with headers myHeader: specialHeaderCase
Restangular.one('buildings', 123).evaluate({myParam: 'param'}, {'myHeader': 'specialHeaderCase'});
// Here the body of the POST is going to be {key: value} as POST is an unsafe operation
Restangular.all('users').login({key: value});
Create custom methods for your collection using Restangular.extendCollection(). This is an alias for:
RestangularProvider.addElementTransformer(route, true, fn);
// create methods for your collection
Restangular.extendCollection('accounts', function(collection) {
collection.totalAmount = function() {
// implementation here
};
return collection;
});
var accounts$ = Restangular.all('accounts').getList();
accounts$.subscribe( accounts => {
accounts.totalAmount(); // invoke your custom collection method
});
Create custom methods for your models using Restangular.extendModel(). This is an alias for:
RestangularProvider.addElementTransformer(route, false, fn);
Restangular.extendModel('accounts', function(model) {
model.prettifyAmount = function() {};
return model;
});
var account$ = Restangular.one('accounts', 1).get();
account$.subscribe(function(account) {
account.prettifyAmount(); // invoke your custom model method
});
Errors can be checked on the second argument of the subscribe.
Restangular.all("accounts").getList().subscribe( response => {
console.log("All ok");
}, errorResponse => {
console.log("Error with status code", errorResponse.status);
});
You can use setDefaultHeaders
or addFullRequestInterceptor
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { RestangularModule } from 'ngx-restangular';
import { authService } from '../your-services';
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider, authService) {
// set static header
RestangularProvider.setDefaultHeaders({'Authorization': 'Bearer UDXPx-Xko0w4BRKajozCVy20X11MRZs1'});
// by each request to the server receive a token and update headers with it
RestangularProvider.addFullRequestInterceptor((element, operation, path, url, headers, params) => {
let bearerToken = authService.getBearerToken();
return {
headers: Object.assign({}, headers, {Authorization: `Bearer ${bearerToken}`})
};
});
}
// AppModule is the main entry point into Angular2 bootstraping process
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
// Importing RestangularModule and making default configs for restanglar
RestangularModule.forRoot([authService], RestangularConfigFactory),
]
})
export class AppModule {
}
You can use defaultHeaders
property for this. defaultsHeaders
can be scoped with withConfig
so it's really cool.
You must add a requestInterceptor for this.
RestangularProvider.setRequestInterceptor(function(elem, operation) {
if (operation === "remove") {
return null;
}
return elem;
})
_id
not id
as the default. Therefore requests are sent to undefined routesWhat you need to do is to configure the RestangularFields
and set the id
field to _id
. Let's see how:
RestangularProvider.setRestangularFields({
id: "_id"
});
In some cases, people 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 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 :)
This can be done using the customPOST / customPUT method. Look at the following example:
Restangular.all('users')
.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.
Restangular.all('users').getList().subscribe( users => {
this.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().subscribe( () => {
// Updating the list and removing the user after the response is OK.
this.users = _.without(this.users, userWithId);
});
});
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().subscribe( () => {
let index = $scope.users.indexOf(userWithId);
if (index > -1) this.users.splice(index, 1);
});
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( (response) => {
var newResponse = response;
if (_.isArray(response)) {
_.forEach(newResponse, function(value, key) {
newResponse[key].originalElement = _.clone(value);
});
} else {
newResponse.originalElement = _.clone(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:
this.showData = function () {
baseUrl.post(someData).subscribe( (response) => {
console.log(response.plain());
});
};
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider) {
// Adding withCredential parametr to all Restangular requests
RestangularProvider.setDefaultHttpFields({ withCredentials: true });
}
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
],
imports: [
// Global configuration
RestangularModule.forRoot(RestangularConfigFactory),
]
})
export class AppModule {}
Users reported that this server frameworks play real nice with Ngx-restangular, as they let you create a Nested RESTful Resources API easily:
Please read contributing guidelines here.
The MIT License
之前几次升级 Angular 版本的经历总体还算顺利,因此原本对 Angular 8 升级到 9 的过程也比较乐观。虽然 Angular 9 开始将默认启动新的 Angular Ivy,但考虑到已经正式发布数月,甚至 Android 10 都已问世,自己的项目又没有用特别冷门的依赖,依然没有担心会遇到问题。可惜墨菲定律无处不在,最后还是花了比想象中更多的时间完成升级。 为此,本文简单介绍自己在从
ngx-weui 是一个使用 Angular 构建的 WeUI 组件。 在线示例以及API文档。
ngx-fastdfs 是 nginx + lua +fastdfs 实现分布式图片实时动态压缩。 install 进入docker目录docker build -t fastdfs:dev . 使用 docker -idt -p 80:80 fastdfs:dev /bin/bash进入容器执行/etc/rc.local 测试 进入容器执行test目录下的./test.sh或者直接执行下面脚本
ngx-markdown ngx-markdown is an Angular library that combines... Marked to parse markdown to HTML Prism.js for language syntax highlight Emoji-Toolkit for emoji support KaTeX for math expression rende
ngx-admin Who uses ngx-admin?| Documentation | Installation Guidelines | Angular templates New! Material theme for ngx-admin Material admin theme is based on the most popular Angular dashboard templat
@sweetalert2/ngx-sweetalert2 Official SweetAlert2 integration for Angular This is not a regular API wrapper for SweetAlert (which already works very well alone), it intends to provide Angular-esque ut
ngx-dropzone A lightweight and highly customizable Angular dropzone component for file uploads. For a demo see DEMO. And the CODE for the demo. Install $ npm install --save ngx-dropzone Usage // in ap
ngx-slick Support angular 6+, Slick 1.8.1 Example Installation To install this library, run: $ npm install ngx-slick --save Consuming your library Once you have published your library to npm, you can
Angular Module for displaying a feed of items in a masonry layout using https://github.com/desandro/masonry This package was originally a fork from https://github.com/jelgblad/angular2-masonry to allo