I am playing around with the MEAN stack. I have created a rest service to delete from mongo db which works fine but When I try to use angular Factory method and call it I get the above error
myApp.factory('methodFactory', ['$http', function ($http) {
return {
//id becomes undefined over here
removeContact:function($http, id){
//TODO add URL
var url = '/contactlist/'+id;
return $http({
method: 'DELETE',
url: url
});
}
};
}]);
myApp.controller('AppControl', ['$scope','$http','methodFactory', function($scope,$http,methodFactory) {
$scope.remove = function(id) {
console.log(id); //able to print correct id
methodFactory.removeContact(id).success(function(response){
console.log("remv"+response);
refresh();
});//tthiss throws the error
//this rest service works properly.
/*$http.delete('/contactlist/'+id).success(function(response){
console.log("remv"+response);
refresh();
});*/
};
};
This what node server looks like
app.delete('/contactlist/:id',function(req,res) {
var id = req.params.id;
console.log(id);
db.contactlist.remove({_id:mongojs.ObjectId(id)},function(err,doc){
res.json(doc);
});
console.log("exiting delete")
});
I am not sure if factory could be one of the way to call a rest service. What could cause the problem ?
Error
TypeError: string is not a function
at Object.removeContact (http://localhost:3000/controllers/controller.js:10:20)
at l.$scope.remove (http://localhost:3000/controllers/controller.js:85:23)
at hb.functionCall (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:198:426)
at Cc.(anonymous function).compile.d.on.f (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:215:74)
at l.$get.l.$eval (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:126:193)
at l.$get.l.$apply (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:126:419)
at HTMLButtonElement.<anonymous> (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:215:126)
at HTMLButtonElement.c (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:32:363)
Your removeContact method needs two parameters:
removeContact:function($http, id) ...
but you call it with only one:
methodFactory.removeContact(id) ...
Id is a string, I suppose, but it it will be assigned to the first parameter of the function $http, what must be executable according the code you provided.
Related
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 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'm trying to grab a single result from my expressjs api from within my AngularJS factory.
The factory looks like this and grabs all posts from my api(written in expressjs and getting data from mongodb), which is working fine:
angular.module('bonsaiService', ['ngResource']).
factory('bonsaiService', function($q,$resource) {
var bonsaiResource = $resource('http://localhost:8888/api/bonsais/:bonsaiId',{},{
get:{
method: 'GET',
params:{bonsaiId:''},
isArray: true
}
});
return {
get:function(){
var q = $q.defer();
bonsaiResource.get({
},
function(resp){
q.resolve(resp);
},function(httpResponse){
q.reject(httpResponse);
});
return q.promise;
}
//find by id
};
});
What i've tried so far is adding :bonsaiId after the $resource url and adding params for that id like this: params:{bonsaiId: ''}.
The server part (expressJS) look like this:
router.route('/bonsais/:bonsaiId')
.get(function(req,res){
Bonsai.findOne(req.params.bonsaiId,function(err,bonsai){
if(err)
res.send(err);
res.json(bonsai)
})
})
When I call a local url (with and existing _id from mongodb) it works fine and returns my data in json :
http://localhost:8888/api/bonsais/536be2e2ae54668818000001
Now in the controller im trying to get this data in my scope, which is not working.
bonsaiService.get({bonsaiId:$routeParams.bonsaiId}).then(
function(data){
$scope.trees = data;
console.log(data);
});
How do I make this work?
You could use a more simple approach here.
The query method for $resource already defines a GET on an array, which is QUERY.
Why not write your service this way :
.factory('bonsaiService', ['$resource',
function($resource) {
return $resource('http://localhost:8888/api/bonsais/:bonsaiId', {
bonsaiId: '#bonsaiId'
});
}
])
And in your controller, it would work like this :
bonsaiService.query({
bonsaiId: $routeParams.bonsaiId
}, function success() {
//Your code
}, function err() {
//Your code
});
Don't forget to inject the service in the controller or the app file, if it's not done already.
Hi I'am trying a simple example of using a controller and a factory to get some data back to the view but for some reason I can't print it.
I managed to get the ajax call to work.
If I type the
$scope.sampleStyles = [{ sample: 'text here', text : 'dasdas'}
and don't use the ajax call it works
UPDATE: if I add an alert before assigning to my scope it works (ajax has time to do his thing)
anyone know how to overcome that?
CODE:
var packageApp = angular.module("packageApp", []);
packageApp.controller("MyController", function($scope, myFactory){
$scope.sampleStyles = [];
function init(){
$scope.sampleStyles = myFactory.getSampleStyles();
}
init();
});
packageApp.factory('myFactory', function($http, $log){
var factory = {};
var sampleStyles = [];
var tempData = {};
factory.update = function(){
$.ajax({
type: 'POST',
url: '/account/fetch-sample-styles',
data: {
source: 'ajax'
},
success: function(data, textStatus, XMLHttpRequest){
tempData = data;
}
});
alert(tempData);
sampleStyles = tempData;
}
factory.getSampleStyles = function(){
factory.update();
return sampleStyles;
};
return factory;
});
Are you using the AngularJs $http service? If so it will return a promise which you then operate on. Here is more on promises from the AngularJs docs.
My guess is, you are using an ajax.get(...) with a success callback defined inside. The problem is probably due to the success callback not belonging to the "AngularJs world."
To fix this, you need to tell AngularJs that its scope has changed. Use the $[Root]scope.$apply() function, and have the scope injected into your service as a dependency.
Something like this inside the factory:
$.ajax({
url: "/api/some/end/:point",
...
success: function(data) {
$scope.$apply(function() {
$scope.sampleStyles = data; // etc
});
}
});
I strongly recommend that you look into the $http service, it makes the above code much nicer, and is designed to play nice with the $scope.
$http.get("/api/end/point").then(function(response) {
// response.data points at the page data sent back, assuming that your
// api endpoint sends back JSON of the likes of
// { status: "SUCCESS", styles: [...] }
$scope.sampleStyles = response.data.styles;
});
EDIT:
Now that you posted some code, it seems like the root of your issue is based on the fact that the ajax get is an async call. Why are you even messing with using a temporary variable? Why not the following?
factory.update = function(){
$.ajax({
type: 'POST',
url: '/account/fetch-sample-styles',
data: {
source: 'ajax'
},
success: function(data, textStatus, XMLHttpRequest){
sampleStyles = data;
}
});
}
If you really wanted to make the $.ajax call blocking, you can set async: false in the $.ajax properties.
EDIT 2:
Fixed some broken links, sorry I am a SO newb :(
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}
);