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!
Related
I want to send headers each time for CRUD operation from factory side.
Here is my factory
var appangular.module("LifeStyleFactModule",["ngResource"]);
app.constant("RES_URL", "http://localhost:9090/")
app.factory("CategoryFactory",function($resource,RES_URL){
var categoryinfo;
var categoryresource=$resource(RES_URL+"category/:id",{"id":"#id"},{update:{method:"PUT"}});
return{
getcategory:function(){
categoryinfo=categoryresource.query();
return categoryinfo;
},
addcategoryItem:function(categoryItem){
var category = new categoryresource(categoryItem);
category.$save(function(respdata){
categoryinfo.push(respdata);
},function(respdata){
});
},
deletecategoryItem:function(idx,id){
var category=new categoryresource({"id":id});
category.$delete(function(){
categoryinfo.splice(idx,1);
},function(){
})
},
updatecategoryItem:function(categoryItem,idx){
var category=new categoryresource(categoryItem);
category.$update({"id":categoryItem._id},function(data){
categoryinfo[idx]=data;
},function(){
})
}
}
})
the above functionality is working well. Now i want to send the token in the headers. How can i do that.
I have tried to do it by the following way
var categoryresource=$resource(RES_URL+"category/:id",{"id":"#id"},{update:{method:"PUT"},headers:{"token":"#token}});
but not getting how to send the token for CRUD operation.
Is procedure is correct, if so how can i send tokens.
Else let me know the way.
Instead of above method i tried the following way as
$resource(RES_URL+"category",{},{query:{method:"get",isArray:true,headers:{"token":token}}}).query({},function(res){});
this is working but the procedure for the first procedure.
Please after answering mark it as duplicate or down vote
dont say ( / { such things are missing.
The best solution as to me is to use interceptor. Here is a way to send token in headers, I've used in one of my projects.
angular
.module('app.core')
.config(config);
config.$inject = ['$httpProvider'];
function config($httpProvider) {
$httpProvider.interceptors.push(interceptor);
}
interceptor.$inject = ['$q', '$injector', 'AuthModel'];
function interceptor($q, $injector, AuthModel) {
return {
request: function (config) {
config.headers.Authorization = AuthModel.token;
return config;
},
responseError: function (rejection) {
}
};
}
Added a jsfiddle to demonstrate
https://jsfiddle.net/Sergey_Mell/c47js1zc/
Just click the Send button and check the request headers in developer tools
I am building an app with Ionic and MEAN stack. My express server is running on localhost:3000 while my Ionic public code is running on localhost:8100. From my research, it seems like Ionic can run on a different IP address from the server and should just use ngResource to send $http requests.
So I have a RESTful endpoint like this in server.js
router.get('/', function(req, res){
res.json({"name":"Abdul"});
});
And on the Ionic client code I am sending in a request like this:
app.controller('mainCtrl', function($scope, $resource){
$scope.test = $resource('localhost:3000/');
$scope.test_button = function(){
console.log($scope.test);
}
});
But when I click the test_button, instead of [{"name":"Abdul"}] being logged in the console, I get the following null message:
function Resource(value) {
shallowClearAndCopy(value || {}, this);
}
Can anyone help me out on connecting the client and server?
$resource object will only create an object with having get, save, update, etc. So for calling get method of server, you need to call get method of $resource object. That method will return $promise object will provide a promise. On which you can place .then promise, in which you will get data in success function.
One more thing is, when you are returning data from the server, you are returning object in array format. So in that case you need to specify get method will return array by having isArray: true option there.
$scope.test = $resource('http://localhost:3000/', {}, {get: { isArray: true}});
$scope.test.get().$promise.then(function(data){ //success function
$scope.test = data;
},function(error){ //error function
console.log(error);
})
to make your application more better, you could move up your $resource object to service/factory to make that call reusable.
app.service('dataService', function($resource){
var resourceUrl = $resource('http://localhost:3000/', {}, {get: { isArray: true} });
this.getData = function(){
return resourceUrl.get().$promise;
};
})
Controller
app.controller('mainCtrl', function($scope, dataService){
$scope.test_button = function(){
dataService.getData().then(function(data){ //success function
$scope.test = data;
},function(error){ //error function
console.log(error);
})
}
});
I have implemented resources in my single page angular app which fires to my REST client server. I have made different services for each resource. Now my REST server is sending a value in response header, now I want to know a proper way where I can retrieve that value from headers.
My service code:
app.service('$job', function($resource) {
var job = $resource(service_base_url+'jobs.json/:id');
return job;
});
My controller which is getting headers:
app.controllerProvider.register('JobPostsController',['$scope','$job', function($scope, $job) {
$scope.jobs = {};
$scope.job_titles = {};
$job.query(function(jobs,responseHeaders){
var headers = responseHeaders();
some_function(headers.user);
$scope.jobs = jobs.jobs;
});
}
]);
I am getting headers in my above code, but I don't want to inject it in all controllers. So is there a proper way to do it? Some single config code which will run for all future resources request or some kind of event which can be only triggered when successful resource response with 200 OK
Try interceptor.
I don't know exactly your logic. You could register a global interceptor which intercepts all requests:
angular.module('App', [])
.config(function ($httpProvider){
$httpProvider.interceptors.push(function() {
return {
'response': function(response) {
var headers = response.headers();
some_function(headers.user);
return response;
}
};
});
});
or just register an interceptor which runs only for all requests of this query.
app.service('$job', function($resource) {
var job = $resource(service_base_url+'jobs.json/:id',{}, {
'query': {
method:'GET',
isArray:true,
interceptor: {
'response': function(response) {
var headers = response.headers();
some_function(headers.user);
return response;
}
}
}
});
return job;
});
Side notes:
Should not use $ prefix for your service name as it's reserved for angular, it may conflict with angular future versions.
I guess you need .factory instead of .service
You can set up the service in a run block like:
angular.module('myApp', [])
.run(['$rootScope', '$job',function ($rootScope, $job) {
$rootScope.jobs = {};
$rootScope.job_titles = {};
$job.query(function(jobs,responseHeaders){
var headers = responseHeaders();
some_function(headers.user);
$rootScope.jobs = jobs.jobs;
});
}]);
The only drawback your service is global to the app
I would go with a base service factory. This would allow you to have common service related functionality in one place
app.factory('ServiceBase', function () {
function ServiceBase() {
this.responseHeaders = function responseHeaders(resp){
// todo
};
}
return ServiceBase;
});
app.service('$job', function($resource, ServiceBase) {
var service = function () {
// $job related functions here
};
angular.extend(service, new ServiceBase());
return service;
});
Now anything in the ServiceBase is accessible to the controller and to the service. This allows you to have common functionality, has no new injection dependencies (on the controller), and is easy to extend further.
I think angulrjs response interceptors can help u for this.
When I make a request and set on the request and session scope (server side) some attributes. I would like to know if is it possible to get with AngularJS these attributes at my server side.
If you are looking for any "session integration" for authentication purposes, take a look at Spring Session in conjunction with Spring Security. I wrote a little sample application that illustrates how to integrate AngularJS with Spring Security by exposing the session id as an HTTP header (x-auth-token). The corresponding blog post is here.
Yes it is possible, typically one can do as charlietfl said very easily when performing an asynchronous login. Just return some JSON containing upon a successful login.
I store the returned JSON in a ".value" service:
var app = angular.module('app', [])
.value('appData', {})
.factory('authService', ['$http', '$q', 'appData',
function authService($http, $q, appData) {
var setAppData = function(data) {
angular.copy(data, appData);
};
return {
login: function() {
var deferred = $q.defer();
$http.post('/auth', data)
.success(function(response){
deferred.resolve(response);
setAppData(response);
})
.error(function(reason){
deferred.reject(reason);
});
return deferred.promise;
},
logout: function() {
//...http call to logout
setAppData({});
}
};
}
])
;
I have an AngularJS app. In this app, I'm trying to ping a REST API. This API returns a list of orders.
I need to be able to handle the scenario where I successfully GET the orders. I also need to handle the
scenario where the request to GET the orders fails. In an attempt to do this, I'm using the ngResource
module. My controller looks like the following:
myController.js
myApp.controller('myController',
function myController($scope, myService) {
myService.getOrders(function(data) {
$scope.orders = data;
});
}
);
The definition of myService is stored in myService.js. That file looks like this:
app.factory("myyService", function($resource, $log) {
return {
getOrders: function(onSuccess) {
var orders = $resource("http://localhost:1000/orders", { fetch:{method:'JSON'} });
orders.fetch(function (response) {
console.log(response);
onSuccess(response.data);
});
}
};
});
When I run this code, I get a runtime error. The error says:
TypeError: Object function g(b){z(b||{},this)} has no method 'fetch'
Maybe there has to be something I don't understand. In my mind, I see fetch defined.
The other question I have is how do I set this up to handle failed requests? Like a 404 or 502?
You forgot the curly braces after the URL parameter...
Change: http://localhost:1000/orders", { fetch :
To: http://localhost:1000/orders", {}, { fetch :
app.factory("myyService", function($resource, $log) {
return {
getOrders: function(onSuccess) {
var orders = $resource("http://localhost:1000/orders", {}, { fetch : {method:'JSON'} });
orders.fetch(function (response) {
console.log(response);
onSuccess(response.data);
});
}
};
});
[EDIT]
To handle errors from the server side, you need to set the second function in the resource call.
Example :
orders.fetch(function success() {...},
function error() {... this will execute in a http error, 400 or 500}
);