I have created a service with RESTAngular in order to ease my communication with my REST API. Let's say that i have an object Person.
Here is my service:
myApp.factory('RESTService', [ 'Restangular', function (Restangular) {
var restAngular = Restangular.withConfig(function (Configurer) {
Configurer.setBaseUrl('/myAPI/');
});
var service = {};
service.Person= restAngular.service('person');
return service;
}]);
I can successfully:
GET the list of Person
RESTService.Person.getList().then(function (response) {
$scope.persons = response.plain();
})
GET one Person
RESTService.Person.one(id).get().then(function (response) {
$scope.person = response.plain();
})
POST (Update) Person
RESTService.Person.post($scope.person).then(
function (successResponse) {
// success stuff
},
function (errorResponse) {
console.log("fail", errorResponse.status);
}
)
But i can't do PUT (create a new record). Now let's say i have a form and the form's data is being kept in $scope.formData. I want to do make a PUT request passing the object contained in $scope.formData to my API. How do i do that?
EDIT: For clarification my API is REST, so i do
GET - /myAPI/person to get the list of all the persons
GET - /myAPI/person/123 to get the person with id=123
POST - /myAPI/person/123 to update the person with id=123
PUT - /myAPI/person to insert a new person in my database
When creating a person for which you don't know the id yet, your API should accept a POST on myAPI/person. I assume Restangular expects that.
You don't want to accept a PUT, because the request is not idempotent. This is part of the semantics of a PUT and you want to adhere to that.
This SO question explores this in more detail: PUT vs POST in REST
Related
I'm working on a simple weather app that will grab both the current and the weekly forecast from a weather API. To keep it simple, I'd really like to my weatherService function, getForecast, to somehow make two AJAX calls -- one for the weekly forecast, which I already have, and one for the current forecast (unfortunately I don't think this API has a means of retrieving a JSON return that contains both). I'm not sure about the best way to go about doing this, I'm very new to Angular.
Here's my service:
weather.service('weatherService', function($resource, $http){
this.currentForecast = null;
// default city
this.city = 'Chicago, IL';
this.getForecast = function(location, type) {
return $http({
method : "GET",
url : "http://api.openweathermap.org/data/2.5/forecast/daily?q="+location+"&mode=json&cnt=7&appid=e92f550a676a12835520519a5a2aef4b"
}).then(
function(response) {
return response.data;
}
)
};
});
And I'd like a second GET, retrieving from: http://api.openweathermap.org/data/2.5/weather?q=Chicago,IL&appid=e92f550a676a12835520519a5a2aef4b to be appended to the response, so that there's a single object returned.
Also, if this isn't the best way to go about doing this, I'm certainly open to suggestions.
What you are looking for is angular promises library $q
$q.all([$http(...), $http(...),...]).then(function(ret){
// ret has all results from all ajax calls
})
More specifically:
weather.service('weatherService', function($resource, $http, $q){
this.getForecast = function(location, type) {
return $q.all([
$http.get(url1(location, type)),
$http.get(url2(location, type))
])
}
})
...
weatherService.getForcast(location, type).then(function(ret){
console.log(ret[0].data)
console.log(ret[1].data)
})
There is excellent video on using $q.all at egghead.io
Well, you could use webworkers, but then you have 6 problems. You can also chain requests using then callbacks.
I am working on a application and I am using MEAN stack as technology. In AngularJS, I am using ngResource to CRUD operations. Can any one suggest how to send username and password to server and get response back to check if the credentials are valid. I need help in ngResource and mongoose code. Thanks.
Check out the mean.js boilerplate:
https://github.com/meanjs/mean
You'll see how they do it pretty quickly:
moduleName.client.controller.js will make an http call, using the injected http. Here is an example of the call being made from /modules/users/client/controllers/authentication.client.controller.js (with some edits to the code to make it easier to see what you're looking for):
AuthenticationController.$inject = ['$scope', '$state', '$http', 'Authentication'];
function AuthenticationController($scope, $state, $http, Authentication, ) {
...
vm.authentication = Authentication;
$http.post('/api/auth/signup', vm.credentials).success(function (response) {
// If successful we assign the response to the global user model
vm.authentication.user = response;
}).error(function (response) {
vm.error = response.message;
});
}
Now, this call is posted to '/api/auth/signup'. The file that handles this route is located in /modules/users/server/routes/auth.server.routes.js:
modules.exports = function(app) {
var users = require('../controllers/users.server.controller');
...
app.route('/api/auth/signup').post(users.signup);
}
As you can see, the route (the url) matches the one you called from the client controller. As that $http call from the controller was a $http.post(), the route entry must match. You can see that it does above.
The parameter users.signup passed above refers to a function in yet another file: /modules/users/server/controllers/users/users.authentication.server.controller.js. This is your main controller for the authentication part of the users module. Now, within this file we can see the signup function is exported:
/* note: there are global variables here, see below */
exports.signup = function (req, res) {
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
// Init user and add missing fields
var user = new User(req.body);
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function (err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
// Remove sensitive data before login
user.password = undefined;
user.salt = undefined;
req.login(user, function (err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
});
};
Now, a lot is going on here, but we can break it down.
The req variable is the post request as passed by $http. The res variable is the response the client expects to receive back.
Notice how vm.credentials was passed in $http.post('/api/auth/signup/', vm.credentials)? This is bound to req.body and may be accessed by your server controller from that.
So in this example, the req.body is the required data to create a new user on the server. This is done using mongoose, which has a schema called User. Access this by declaring globals at the top of your controller:
var mongoose = require('mongoose'),
User = mongoose.model('User');
You can see that a new user is instantiated above. It is saved via the mongoose call .save().
Lastly, your server functions should response to the client's request using the res variable passed to the function. See how once the user is created successfully, the function calls
res.jsonp(user);
This is success() to the client, which accepts the response and binds it to a local variable vm.authentication.user
Hope this helps!
I am learning about the MEAN stack, and have created a REST API which posts a review to a collection in MongoDB.
I have defined a service as given:
angular.module('myApp')
.constant('baseURL', 'http://localhost:8080/');
angular.module('myApp')
.service('addReviews', ['$resource', 'baseURL', function($resource, baseURL) {
this.getReviews = function() {
return $resource(baseURL+'reviews/', null, {'save': {method: 'POST'}});
};
}]);
Now, I am calling this service from my controller:
angular.module('myApp', ['ngResource'])
.controller('reviewController', ['$scope', 'addReviews', function($scope, addReviews) {
$scope.reviewSubmit = function() {
$scope.receivedReviews = false;
var review = {
// some data
};
$scope.reviews = addReviews.getReviews().query(
function(response) {
$scope.reviews = response;
$scope.receivedReviews = true;
},
function(response) {
$scope.reviews = response;
// print error message
}
);
console.log($scope.reviews); // showing empty array
};
}]);
In routes.js, I have configured my route as:
var Reviews = require('./models/reviews');
...
app.post('/reviews', function(req, res) {
Reviews.create(req.body, function(err, post) {
if (err) {
return res.send(err);
}
return res.json(post);
});
});
I am trying to post a new review to the Reviews collection. However, $scope.reviews is showing an empty array. I logged the requests, and it shows a GET request is being to /reviews instead of POST. I think I should use save() instead of query(), but I have seen some tutorials online where they used query() despite the method being PUT/POST in the service. I am really confused. Can anyone point out how I can post the data (in var review) to the Reviews collection?
There are some issues with your code on the angular side of things.
You want to use $resource as an all-purpose object to communicate with the API. It has built-in functionality to:
query: get all resources from a given API endpoint
get: a single resource, usually by specifying that resource's id
save: post, with an object sent across in the body of the request. NOTE: you don't need the {'save': {method: 'POST'}} in your $resource configuration, you get it for free.
remove and delete: self-explanatory
So you'd want to set up your reviews factory (incl. url constant) like:
angular.module('myApp', ['ngResource'])
.constant('baseURL', 'http://localhost:8080/')
.factory('Reviews', ['$resource', 'baseURL', function($resource, baseURL) {
return $resource(baseURL+'reviews/:id', {id: '#id'});
}]);
If you want to have access to all saved reviews in your controller, as $scope.reviews, you'd do something like:
angular.module('myApp')
.controller('reviewController', ['$scope', 'Reviews', function($scope, Reviews) {
// hit API endpoint to get all reviews
// will have to have app.get('/reviews', function(req, res) {...})
// configured in your node code
Reviews.query(function(data) {
$scope.reviews = data;
}, function(error) {
console.log(error);
});
// and if you want to take a user-written review, say $scope.userReview,
// from the view and save it to the database on click function submitReview()...
$scope.userReview = {
message: '',
createdTime: null
};
// ^ not sure what your ReviewSchema looks like on the backend, but for example...
$scope.submitReview = function() {
if ($scope.userReview.message.length) {
$scope.userReview.createdTime = Date.now();
Reviews.save($scope.userReview);
// ^ this will make POST request with the $scope.userReview object as the request body
}
};
}]);
The create method on your back end looks fine. The object (or maybe just string) you send across will have to match your review schema. You may want to log the request body to make sure you're getting what you expect.
Have a look at this short post on using $resource to interact with RESTful APIs, and (the slightly more confusing) angular $resource docs, for more information on the $resource service.
Hope this helps you!
I have a page with a main controller and a nested controller for showing details about a product. I want to use a a service in angular to call the server, retrieve a data object and hold that data object. The main controller will call the service to fetch the data and the details controller needs to know it was updated and then access the data. My service looks like this:
.service("productService", function ($http, $q) {
var product = {};
//interface that is returned
return ({
fetchProduct: fetchProduct,
clearProduct: clearProduct,
product: product
});
function fetchProduct(ID) {
var request = $http({
method: "get",
url: "/online/productdata.ashx?itemID=" + ID,
params: {
action: "get"
}
});
return (request.then(handleSuccess, handleError));
};
function clearProduct() {
product = {};
};
// Transform the error response, unwrapping the application dta from
// the API response payload.
function handleError(response) {
// The API response from the server should be returned in a
// nomralized format. However, if the request was not handled by the
// server (or what not handles properly - ex. server error), then we
// may have to normalize it on our end, as best we can.
if (
!angular.isObject(response.data) ||
!response.data.message
) {
return ($q.reject("An unknown error occurred."));
}
// Otherwise, use expected error message.
return ($q.reject(response.data.message));
};
// I attempt to transform the successful response and unwrap the application data
// from the API response payload.
function handleSuccess(response) {
product = response.data;
console.log("Found Data: " + angular.toJson(response.data))
return (response.data);
};
})
In my main controller I set a scope object to the service like this:
$scope.SelectedProduct = productService;
When the user clicks the button to show the product it is called via the $scope handle:
$scope.SelectedProduct.fetchProduct(ID);
The details controller has the same assignment for the $scope.SelectedProduct. I am new to using services but what I understood is that angular would bind to the service object and changes to the property product would trigger binding to any updates. That is not happening - in fact I do see the data after the fetch operation. In the service I have a console.log on the returned data and it is showing the correct data. However the product property is not getting updated. Can someone tell me what I am doing wrong please? Neither controller has access to the data after it is fetched. I understand that I am getting back a promise but the data is never there even after a timeout check.
Try it with a factory instead of a service.
AngularJS: Factory vs Service vs Provider
If i am not mistaken in RESTful services in order to remove a record you need to do this:
Delete a product: DELETE /api/product/id (reference)
But in RESTAngular when i do for example
product.remove();
A DELETE request is made to /api/product whith the whole product object in the Requests Body. This is not what i want!
Here is my code:
myApp.factory('RESTService', [ 'Restangular', function (Restangular) {
var restAngular = Restangular.withConfig(function (Configurer) {
Configurer.setBaseUrl('/myAPI/');
});
var service = {};
service.Product= restAngular.service('product');
return service;
}]);
GET one Product
RESTService.Product.one(id).get().then(function (response) {
$scope.product= response;
})
DELETE the Product
$scope.product.remove();
I want when i do product.remove() to send a DELETE Request to /myAPI/product/id. How can i do that?
// DELETE /accounts/123/buildings/456
Restangular.one("accounts", 123).one("buildings", 456).remove();
You can delete a record using RESTFul($http) Service like this
$http.delete('/myAPI/product' + id, {params: {id: id}});