Reputation: 3415
I'm adding AngularJS 1.x to a form for editing orders (being able to change the date, shipping, and address in this example). I have a Ruby on Rails JSON API with a few endpoints:
GET /addresses
[{ id: 1, line1: "...", line2: "...", zip: "..." }, ...]
GET /shippings
[{ id: 1, name: "priority" }, ...]
GET /orders/:id
{ date: "...",
shipping: { id: 1, name: "priority" },
address: { id: 1, line1: "...", line2: "...", zip: "..." } }
PATCH /orders/:id
{ date: "...",
shipping_id: 1,
address_attributes: { line1: "...", line2: "...", zip: "..." } }
Inside my OrdersController
I have the following:
var app = angular.module('app');
app.controller('OrdersController', ['$scope', 'Order', 'Address', 'Shipping',
function($scope, Order, Address, Shipping) {
$scope.order = Order.get({ id: 123 });
$scope.addresses = Address.query();
$scope.shippings = Shipping.query();
$scope.save = function() {
$scope.order.$save(function() {
// ...
}, function(error) { alert(error); });
};
}
]);
My form is pretty simple:
<form ng-submit="save()">
<input type="date" ng-model="order.date" />
<select ng-model="order.shipping"
ng-options="option.name for option in shippings track by option.id">
</select>
<input type="text" ng-model="order.address.line1" />
<input type="text" ng-model="order.address.line2" />
<input type="text" ng-model="order.address.zip" />
<input type="submit" value="Save"/>
</form>
The save function hits my API endpoint - however the parameters are incorrect. I need to find a way to transform the between these formats:
{ date: "...", shipping: { ... }, address: { ... } }
{ date: "...", shipping_id: 1, address_attributes: { ... } }
What is the recommended way of doing this in AngularJS? Is this something that should be done through interceptors? Should I be creating a new Order
resource and copying over the attributes in the desired form order?
Upvotes: 4
Views: 153
Reputation: 38032
AngularJS $resource
supports setting transformRequest
and transformResponse
on each action (see https://docs.angularjs.org/api/ngResource/service/$resource). For your example:
angular.module("app").factory("OrderSerializer", function() {
return function(data) {
var parameters = angular.copy(data);
if (parameters.address) {
parameters.address_attributes = parameters.address;
delete parameters.address;
}
if (parameters.shipping && parameters.s.id) {
parameters.shipping_id = parameters.shipping.id;
delete parameters.shipping;
}
return parameters;
};
});
angular.module("app").factory("Order", ["$resource", "OrderSerializer",
function($resource, serializer) {
return $resource("...", { id: "@id" }, {
"save": {
method: "...",
transformRequest: [serializer, angular.toJson],
},
});
}
]);
If advanced transforms are required it may be worth looking into a replacement for ng-resource
such as restangular.
Upvotes: 1
Reputation: 3987
Angular's $http (and $resource) service has a configuration option called transformRequest
that accepts a function (or an array of functions) that can be used to transform request data.
Assuming you are using one of these services, this would be the most appropriate place to handle object mapping.
Example: JSFiddle
function MyController($http) {
var vm = this;
vm.data = {
message: 'Hello World!'
};
vm.mapper = function(data, headersGetter) {
data.greeting = data.message;
delete data.message;
return data;
};
$http({
url: '/path/to/endpoint',
method: 'PUT',
data: vm.data,
transformRequest: [vm.mapper, angular.toJson]
});
}
Note that, in my example, I use an array of functions to transform my request. This is because, by default, $http
uses angular.toJson to stringify the data object and I still want that behavior after my mapper function runs.
Upvotes: 1