I'm using the following Yeoman full stack AngularJS NPM: generator-angular-fullstack
When calling update from the client controller, I receive the following error: Error: undefined is not a function (evaluating 'User.update') I'm expecting to see the following in my Web Inspector Logs:
'5586c4e7214a22b5efbd1672'
'updateUser Called' <-- Never routes to server controller
server/api/route:
//Tried PATCH and PUT
router.patch('/:id', auth.isAuthenticated(), controller.update);
//router.put('/:id', auth.isAuthenticated(), controller.update);
server/api/controller:
exports.update = function(req, res, next) {
console.log('updateUser Called');
};
client/app/controller:
'use strict';
angular.module('demoApp')
.controller('SandboxCtrl', function ($scope, $http, $location, Auth, User) {
$scope.getCurrentUser = Auth.getCurrentUser;
$scope.user = {};
$scope.profiles = {};
$scope.allergens = {};
$http.get('/api/users/me').success(function (user) {
$scope.user = user;
$scope.profiles = user.profiles;
console.log(user.name);
console.log(user.profiles);
});
// Update existing User
$scope.update = function () {
var user = $scope.user;
console.log(user._id);
User.update(function () {
$location.path('/' + user._id);
}, function (errorResponse) {
$scope.error = errorResponse.data.message;
});
};
});
/Client/User/Factory:
'use strict';
angular.module('demoApp')
.factory('User', function ($resource) {
return $resource('/api/users/:id/:controller', {
id: '#_id'
},
{
changePassword: {
method: 'PUT',
params: {
controller:'password'
}
},
update: { //<-- I was missing this!
method: 'PATCH'
},
get: {
method: 'GET',
params: {
id:'me'
}
}
});
});
In AngularJS NPM generator-angular-fullstack, the factory/service is tucked away under /client/components/auth/user.service.js
Added necessary object handle to existing factory solved this issue.
update: { //<-- I was missing this!
method: 'PATCH'
},
Related
I have a service object that makes a call to the backend to fetch a project by id, it works fine but when I mock the get method during testing the method is not being called, I am not sure what I am doing wrong. I am using a Jasmine2 and do not mock the httpbackend, I use the returnValue to set the response but somehow the returnValue is not waorking
This is the service
(function() {
'use strict';
angular
.module('moduleName')
.factory('Project', Project);
Project.$inject = ['$resource'];
function Project ($resource) {
var resourceUrl = 'api/projects/:id';
return $resource(resourceUrl, {}, {
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET',
transformResponse: function (data) {
if (data) {
data = angular.fromJson(data);
}
return data;
}
},
'update': { method:'PUT' }
});
}
})();
The controller looks like this
(function () {
'use strict';
angular
.module('moduleName')
.controller('ProjectController', ProjectController);
ProjectController.$inject = ['Project'];
function ProjectControllerr(Project) {
var vm = this;
vm.project = Project.get(1);
}
})();
This is the test
'use strict';
describe('Project Controller Tests', function () {
beforeEach(module('moduleName'));
describe('Project Controller', function () {
var $scope, $state, ProjectMock, stateMock, stateParamsMock;
beforeEach(inject(function ($rootScope) {
$scope = $rootScope.$new();
ProjectMock = jasmine.createSpyObj('Project', ['get']);
ProjectMock.get.and.returnValue({id:1,name:'data'});
stateParamsMock = {projectId: 1};
}));
describe('When a project is not empty', function () {
beforeEach(inject(function ($controller,Project) {
$controller('ProjectsController as vm',
{
$scope: $scope,
Project: ProjectMock,
$state: stateMock
});
}));
it('Should fetch current project by id', function () {
expect(ProjectMock.get).toHaveBeenCalled();
expect($scope.vm.project.id).toBe(1);
});
});
});
});
You are not calling ProjectMock.get(); That's why you are not getting response.So,your expectation toHaveBeenCalled is false if you checked it.
Do the following changes.
it('Should fetch current project by id', function () {
ProjectMock.get();
expect(ProjectMock.get).toHaveBeenCalled();
expect($scope.vm.project.id).toBe(1);
});
I have this code in my post.serv.js and in my controller I want to execute the function delete.
"use strict";
app.factory('JnttPost', function ($resource) {
var PostResource = $resource('/api/post/:_id', {
_id: "#id"
}, {
update: {
method: 'PUT',
isArray: false
}
}, {
delete: {
method: 'DELETE',
isArray: false
}
});
return PostResource;
});
I already know how to get and update a post, for example in my createpost.serv.js
"use stric";
app.factory('JnttCreatePost', function ($http, $q, JnttPost) {
return {
createPost: function (newPostData) {
var newPost = new JnttPost(newPostData);
var dfd = $q.defer();
newPost.$save().then(function () {
dfd.resolve();
}, function (response) {
dfd.reject(response.data.reason);
});
return dfd.promise;
}
};
});
and in my newpost.ctrl.js
"use strict";
app.controller('CtrlNewPost',
function ($scope, $location, JnttIdentity, JnttNotifier, JnttCreatePost) {
var email = ...;
$scope.newPost = function () {
var newPostData = {...};
JnttCreatePost.createPost(newPostData).then(function () {
JnttNotifier.notify('success', 'The post has been created');
$location.path('/');
}, function (reason) {
JnttNotifier.notify('error', reason);
});
};
});
I can't realize how to perform the delete request, I can do with a $http
In my new controller for do deletePost() function I have this:
$scope.deletePost = function () {
var pwd = JnttIdentity.currentUser.hashed_pwd;
var postidd = {
password: pwd,
id: $scope.post._id
};
var config = {
method: "DELETE",
url: '/api/post/',
data: postidd,
headers: {
"Content-Type": "application/json;charset=utf-8"
}
};
$http(config);
$location.path('/');
};
This actually already do this stuff but I want to do this without the $http like the create request, How I can do this? How do I can edit this code below for do the request?
createPost: function (newPostData) {
var newPost = new JnttPost(newPostData);
var dfd = $q.defer();
newPost.$save().then(function () {
dfd.resolve();
}, function (response) {
dfd.reject(response.data.reason);
});
return dfd.promise;
}
In my routes.js in express I have this route:
app.delete('/api/post/', posts.deletePost);
You can either call delete on the $resource class you create (JnttPost) or call $delete on a post that's returned from the $resource class.
The $resource class already has get/save/query/remove/delete functions included so you don't need to add the delete (save is create/POST, so you need to include update with PUT).
Here's a sample of using your $resource class to call delete:
angular.module('test', ['ngResource'])
.factory('JnttPost', function ($resource) {
var PostResource = $resource('/api/post/:_id', {
_id: "#id"
}, {
update: {
method: 'PUT',
isArray: false
}
});
return PostResource;
})
.run(function(JnttPost){
JnttPost.delete({id: 123123123});
});
I have tried to build a service that will return a $resource after the service has authenticated.
I have done it like this:
.factory('MoltinApi', ['$q', '$resource', '$http', 'moltin_options', 'moltin_auth', function ($q, $resource, $http, options, authData) {
var api = $resource(options.url + options.version + '/:path', {
path: '#path'
});
var authenticate = function () {
if (!options.publicKey)
return;
var deferred = $q.defer();
var request = {
method: 'POST',
url: options.url + 'oauth/access_token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: "grant_type=implicit&client_id=" + options.publicKey
};
$http(request).success(function (response) {
authData = response;
deferred.resolve(api);
});
return deferred.promise;
};
return authenticate();
}])
But I can not call the resource in my controller:
.controller('HomeController', ['MoltinApi', function (moltin) {
var self = this;
moltin.get({ path: 'categories' }, function (categories) {
console.log(categories);
});
}]);
it just states that 'undefined is not a function'.
Can someone tell me what I am doing wrong?
Update 1
So after playing with the solution that was suggested, this is the outcome.
angular.module('moltin', ['ngCookies'])
// ---
// SERVICES.
// ---
.factory('MoltinApi', ['$cookies', '$q', '$resource', '$http', 'moltin_options', function ($cookies, $q, $resource, $http, options) {
var api = $resource(options.url + options.version + '/:path', {
path: '#path'
});
var authenticate = function () {
if (!options.publicKey)
return;
var deferred = $q.defer();
var authData = angular.fromJson($cookies.authData);
if (!authData) {
console.log('from api');
var request = {
method: 'POST',
url: options.url + 'oauth/access_token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: "grant_type=implicit&client_id=" + options.publicKey
};
deferred.resolve($http(request).success(function (response) {
$cookies.authData = angular.toJson(response);
setHeaders(response.access_token);
}));
} else {
console.log('from cookie');
deferred.resolve(setHeaders(authData.access_token));
}
return deferred.promise;
};
var setHeaders = function (token) {
$http.defaults.headers.common['Authorization'] = 'Bearer ' + token;
}
return authenticate().then(function (response) {
return api;
});
}]);
and to call it I have to do this:
.controller('HomeController', ['MoltinApi', function (moltin) {
var self = this;
moltin.then(function (api) {
api.get({ path: 'categories' }, function (categories) {
console.log(categories);
self.sports = categories.result;
});
});
}]);
but what I would like to do is this:
.controller('HomeController', ['MoltinApi', function (moltin) {
var self = this;
moltin.get({ path: 'categories' }, function (categories) {
console.log(categories);
}, function (error) {
console.log(error);
});
}]);
As you can see, the service is checking to see if we have authenticated before returning the API. Once it has authenticated then the API is returned and the user can then call the api without having to authenticate again.
Can someone help me refactor this service so I can call it without having to moltin.then()?
You are returning the authenticate function call in the MoltinApi factory, so you are returning the promise. And the method get doesn't exist in the promise
I am a Angular noob and having problems with binding a variable from one of my services to one of my controllers. I have read at least a dozen posts on the subject and nothing seems to be working for me.
Here is the controller:
app.controller('TeamController', ['$scope', '$modal', 'teamService', function ($scope, $modal, teamService) {
$scope.teamService = teamService;
$scope.selectedTeam = null;
$scope.selectTeam = function(teamId){
$scope.selectedTeam = teamService.getTeam(teamId, $scope.login.loginId);
};
}]);
Here is the service:
angular.module('teamService', [])
.service('teamService', function($http, $q){
this.selectedTeam = {teamId:-1, teamName:"Select a team", teamLocationName:"", teamDescription:"", teamManaged:false};
this.userTeams = [];
this.getTeam = function(teamId, loginId) {
var postData = {teamId: teamId, loginId: loginId};
var promise = $http({
url: "/url-for-getting-team",
method: "POST",
data: postData
});
promise.success(function (data) {
if (data.status === "success") {
this.selectedTeam = data.response;
return data.response;
}
});
promise.error(function () { //TODO handle getTeam errors
return {};
});
};
this.getSelectedTeam = function(){
return this.selectedTeam;
};
});
And here is the template:
<div class="jumbotron main-jumbo" ng-controller="TeamController">
<h1>{{selectedTeam.teamName}}</h1>
</div>
I have tried binding to the getSelectedTeam function and the service variable itself. Do I need to set up a $watch function in the controller? Any assistance would be greatly appreciated.
EDIT:
I tried turning my service into a factory, which still did not help me, so then I looked at a provider that was properly working that I had already written in the application. I converted my "teamService" into a provider and finally worked like a charm. Thanks for the contributions guys.
Code from my new provider:
angular.module('teamService', [])
.provider('teamService', function () {
var errorState = 'error',
logoutState = 'home';
this.$get = function ($rootScope, $http, $q, $state) {
/**
* Low-level, private functions.
*/
/**
* High level, public methods
*/
var wrappedService = {
/**
* Public properties
*/
selectedTeam: {teamName:"Select a team"},
userTeams : null,
createTeam: function(loginId, name, description, locationName, managed){
var postData = {loginId:loginId, teamName:name, teamDescription:description, teamLocationName:locationName, teamManaged:managed};
var promise = $http({
url: "/create-team-url",
method: "POST",
data: postData
});
return promise;
},
getTeam: function(teamId, loginId) {
var postData = {teamId: teamId, loginId: loginId};
var promise = $http({
url: "/get-team-url",
method: "POST",
data: postData
});
promise.success(function (data) {
if (data.status === "success") {
wrappedService.selectedTeam = data.response;
}
});
promise.error(function () { //TODO handle getTeam errors
wrappedService.selectedTeam = {};
});
},
getUserTeams: function(loginId) {
var postData = {loginId: loginId};
var promise = $http({
url: "/team-list-url",
method: "POST",
data: postData
});
return promise;
},
joinTeam: function(teamId, loginId){
var postData = {teamId:teamId, loginId:loginId};
var promise =$http({
url: "/join-team-url",
method: "POST",
data: postData
});
return promise;
},
getSelectedTeam: function(){
return wrappedService.selectedTeam;
}
};
return wrappedService;
};
});
As seen in my edit. I converted my service into a provider and all the changes seem to propagate to the view with no issues. I need to further analyze the difference between the factory, service, and provider in order to gain a higher understanding of what is going on here.
The main issue with the code is the way that promises are used. You can either correct that within the service, or handle it in the controller. As an example of the latter, you can re-write the above as:
Controller Code:
app.controller('TeamController', ['$scope', '$modal', 'teamService', function ($scope, $modal, teamService) {
$scope.teamService = teamService;
$scope.selectedTeam = null;
$scope.selectTeam = function(teamId){
teamService.getTeam(teamId, $scope.login.loginId).then(
function(result){
$scope.selectedTeam = result.data;
},
function(error){
console.log(error);
}
)
};
}]);
Service code:
angular.module('teamService', [])
.service('teamService', function($http, $q){
this.selectedTeam = {teamId:-1, teamName:"Select a team", teamLocationName:"", teamDescription:"", teamManaged:false};
this.userTeams = [];
this.getTeam = function(teamId, loginId) {
var postData = {teamId: teamId, loginId: loginId};
return $http({
url: "/url-for-getting-team",
method: "POST",
data: postData
});
};
this.getSelectedTeam = function(){
return this.selectedTeam;
};
});
You can also handle this in the service itself, but it requires a little more code. The key thing is that the getTeam call is asynchronous and needs to be handled using proper promise constructs.
I want to create an Angular service that will allow me to access a number of $resources, each that access a given API. For example, I want all of my API calls to be generated from a single service using syntax like
var data = API.Products.query(function() {
$scope.products = data.Products;
});
OR
var data = API.Customers.get({id:123}, function() {
$scope.customer = data;
});
Where Products and Customers are Angular $resources that reside within my API service. Currently I am trying to do this
var app = angular.module('myApp', ['ngRoute', 'ngSanitize', 'ngResource', 'API']);
var APIService = angular.module("API", ["ngResource", function ($resource) {
this.Products = $resource('/WebApi/Products/:type/:id', {id:'all'},
{
systemUpdate: { method: 'GET' },
});
this.Customers = $resource('/WebApi/Customers/:type/:id', {id:'all'},
{
systemUpdate: { method: 'GET' },
});
}]);
But I am getting an error during the compilation of this Service. Uncaught Error and when I look at the same page in Firefox, the error does not give any more detail about what is going wrong. What is the correct way to give this kind of functionality?
Here is my factory now:
app.factory("API", ["ngResource", function ($resource) {
return {
API: {
Alerts: $resource('/WebApi/Alert/:type/:id', { id: 'all' },
{
systemUpdate: { method: 'GET' },
autoArchive: { method: 'POST', url: '/WebApi/Alert/Template/:type' }
})
}
}
}]);
Just make it a factory and return the necessary resources:
var app = angular.module("myApp");
app.factory("API", ["$resource", function($resource) {
return {
API: {
Customers: $resource('/WebApi/Customers/:type/:id', {id:'all'},
{
systemUpdate: { method: 'GET' },
});
}
}
}]);
Inject when needed:
app.controller("myController", ["API", function(API) {
API.Customers.Get({}, function(data) {
//sample get call from Customers
});
}]);