Angularjs factory class object has no method save - angularjs

im having a factory class as defined below
angular.module('App')
.factory('Session', function($resource) {
return {
Sessionlogin : function() {
return $resource('/api/session/');
},
Sessioncheckcredentails : function() {
return $resource('/api/session/forgotusername');
}
}});
now im calling the function in controller as
login: function(user, callback) {
var cb = callback || angular.noop;
return Session.Sessionlogin.save({
email: user.email,
password: user.password,
practicename: user.practicename
}, function(user) {
$rootScope.currentUser = user;
return cb();
}, function(err) {
return cb(err);
}).$promise;
}, with proper injection and all.,
but im gettin error objection function().. has no method save..
I tried to include save:post in $resource but was in vain.

Try this:
.factory('Session', function($resource) {
var resource = $resource(url, { id: '#id' }, defaultOptions);
return {
add: function(param) {
return resource.save(param, function() {
onSuccessfullySaved();
}, function (errorResult) {
console.log(errorResult);
});
}

Related

Angular promise should call other promises

I have a state which resolves as :
resolve: {
accounts: function(utils) {
return utils.getAccounts();
},
data: function(utils) {
return utils.getData();
},
termsData: function(utils) {
return utils.getTerms();
}
}
I need to make sure that data and termsData are called only after accounts is returned in angular resolve.
getAccounts function looke like :
function getAccounts() {
var deferred = $q.defer();
Restangular.setBaseUrl(baseUrl());
var accountsService = Restangular.all(accountsUrl);
accountsService.post({headers}).then(function(data) {
deferred.resolve(data);
}, function(ex) {
deferred.reject(ex);
});
return deferred.promise;
}
You could add accounts as a dependency the same way like I suppose you do in controller:
resolve: {
accounts: function(utils) {
return utils.getAccounts();
},
data: ['accounts', 'utils', function(accounts, utils) {
accounts.then(function(data){
return utils.getData();
}, function(data){
return $q.reject();
});
}]
}
}
You could do call other two once getAccounts function promise get resolved and then do return data with getData & getTerms promise with $q.all.
resolve: {
accounts: function(utils, $q) {
return utils.getAccounts().then(function(accounts){
return $q.all([accounts, utils.getData(), utils.getTerms()])
});
}
}
Controller
app.controller('mainCtrl', function($q, $scope, accounts){
console.log("accounts", accounts[0])
console.log("getData Response", accounts[1])
console.log("getTerms Response", accounts[2])
})
Assuming you need to get all your data asynchronously you can simply restructure your code to take advantage of the promise pattern.
var getAccounts = function(...) {
return new Promise(resolve, reject) {
... do something and resolve or reject
};
};
var getTermsData = funciton= function(...) {
return new Promise(resolve, reject) {
... do something and resolve or reject
};
};
var getData = = function(...) {
return new Promise(resolve, reject) {
... do something and resolve or reject
};
};
getAccounts.then(function(accounts) {
return getTermData();
}.then(function(termData) {
return getData(data);
}.catch(function() {
// something went wrong!
}

Angular and Karma testing a service that uses $resource and returns a promise

I am trying to develop a Unit Test using Jasmine for my AngularJS User service that relies on $resource. My test which is:
'use strict';
describe("User Service Test", function() {
var service;
var mockLoginUser = { email: 'hidden', password: "hidden" };
beforeEach(module('flightlottery.userApi'));
beforeEach(inject(function(User) {
service = User;
// $scope = _$scope_;
//http = $httpBacked;
}));
it('should fetch login a user', function(done) {
var testUser = function(user) {
console.log('callback called');
//expect(user.email).toBe(mockLoginUser.email);
//expect(user.password).toBe(mockUser.password);
};
var failTest = function(error) {
expect(error).toBeUndefined();
};
//http.expectPost('/users/login', mockLoginUser).respond(200,'');
//http.expectGET('/employees/1').respond(200,mockEmployee);
service.login(mockLoginUser)
.$promise.then(testUser)
.catch(failTest)
.finally(done);
// $scope.$apply;
//http.flush();
});
});
When I run my test I get the following error.
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Can anyone let me know how to accomplish this? I find it rather confusing.
Thanks!
***EDIT:
Here is my user service. I would like to test the login method.
angular.module('flightlottery.userApi', ['ngResource']).
factory('User', function($resource, $rootScope) {
var current_user;
var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
query: {method:'GET', params: {method:'index'}, isArray:true },
save: {method:'POST', params: {method:'save'} },
get: {method:'GET', params: {method:'edit'} },
phistory: {method:'GET', params: {method:'history'}, isArray:true },
remove: {method:'DELETE', params: {method:'remove'} },
login: {method:'POST', params: {method:'login'} },
logout: {method:'POST', params: {method:'logout'} },
register: {method:'POST', params: {method:'register'} }
});
User.setCurrentUser = function(user) {
//var self = this;
current_user = user;
$rootScope.$broadcast('user:updated',user);
//console.log(self.current_user_id);
}
User.getCurrentUser = function() {
//var self = this;
return current_user;
//console.log(self.current_user_id);
}
User.registerUser = function(cb) {
//console.log(cb);
return User.register(cb);
}
User.play_history = function(cb) {
//console.log(cb);
return User.phistory(cb);
}
User.loginUser = function(cb) {
return User.login(cb);
}
User.logoutUser = function(cb) {
current_user = null;
return User.logout(cb);
// return User.logout();
///return User.save({id: this.id},
//angular.extend({}, this, {id:undefined}), cb);
};
User.prototype.update = function(cb) {
return User.save({id: this.id},
angular.extend({}, this, {id:undefined}), cb);
};
User.prototype.destroy = function(cb) {
return User.remove({id: this.id}, cb);
};
return User;
});
After trying to guess what you want to accomplish, little refactoring – the results looks like this.
I'm always wonder what really we want test and what we want to prove if test pass or fail.
Version with dependencies stubbed
angular.module('flightlottery.userApi', ['ngResource'])
.factory('User', function($resource) {
var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
login: {
method: 'POST',
params: {
method: 'login'
}
}
});
User.loginUser = function(cb) {
return User.login(cb);
}
return User;
})
describe("User Service Test", function() {
var service;
var queryDeferred;
var mockLoginUser = {
email: 'hidden',
password: "hidden"
};
var scenarios = {
success: function(user) {
expect(user.email).toBe(mockLoginUser.email);
expect(user.password).toBe(mockLoginUser.password);
},
fail: function(error) {
expect(error).toBeDefined();
}
}
beforeEach(module('flightlottery.userApi'));
beforeEach(inject(function(_$rootScope_) {
$rootScope = _$rootScope_;
}));
beforeEach(inject(function($q) {
queryDeferred = $q.defer();
mockUserLogin = {
login: function() {
return {
$promise: queryDeferred.promise
};
}
}
spyOn(mockUserLogin, 'login').and.callThrough();
spyOn(scenarios, 'success').and.callThrough();
spyOn(scenarios, 'fail').and.callThrough();
}))
it('runs `success scenario` if user object is fetched', function() {
queryDeferred.resolve(mockLoginUser)
userLogin(mockLoginUser, scenarios);
expect(scenarios.success).toHaveBeenCalled()
expect(scenarios.fail).not.toHaveBeenCalled()
});
it('runs `fail scenario` if user object is not fetched', function() {
var reason = {
error: 'some error'
}
queryDeferred.reject(reason)
userLogin(mockLoginUser, scenarios)
expect(scenarios.success).not.toHaveBeenCalled()
expect(scenarios.fail).toHaveBeenCalledWith(reason)
});
function userLogin(mockLoginUser, scenarios) {
mockUserLogin.login(mockLoginUser)
.$promise.then(scenarios.success)
.catch(scenarios.fail)
.finally();
$rootScope.$apply();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
Second version - injected service and $httpBackend
When an Angular application needs some data from a server, it calls the $http service, which sends the request to a real server using $httpBackend service. With dependency injection, it is easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify the requests and respond with some testing data without sending a request to a real server.
angular.module('flightlottery.userApi', ['ngResource']).
factory('User', function($resource, $rootScope) {
var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
login: {
method: 'POST',
params: {
method: 'login'
}
}
});
User.loginUser = function(cb) {
return User.login(cb);
}
return User;
});
describe("User Service Test", function() {
var $httpBackend, User;
beforeEach(module('flightlottery.userApi'));
beforeEach(inject(function(_$httpBackend_, _User_) {
$httpBackend = _$httpBackend_;
User = _User_;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('calls `POST` method to interacts with backend', function() {
var stubUser = {
name: 'Some name',
password: 'somePassword'
};
var stubResponse = {
login: 'someName',
lastLogin: Date.now()
}
spyOn(User, 'login').and.callThrough();
User.loginUser(stubUser).$promise.then(function(response) { // response is stubbed by second argument of repond method
expect(response.login).toBe(stubResponse.login)
expect(response.lastLogin).toBe(stubResponse.lastLogin)
});
expect(User.login).toHaveBeenCalledWith(stubUser)
$httpBackend.expectPOST('http://somesite.ca/api/users/login').respond(200, stubResponse)
$httpBackend.flush();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

How do I delete a post using this with angularJS and express?

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});
});

Resolving a promise in a dependent AngularJS service

I have a simple AngularJS app running in a Chrome Extension making use of the Storage API. Having an issue with the async nature of Storage; I've abstracted the storage away into a 'UserService' that sets and gets the data as a factory:
app.factory('UserService',
function($q, AppSettings) {
var defaults = {
api: {
token: AppSettings.environments[1].api.token
},
email: ''
};
var service = {
user: {},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service);
});
return deferred.promise;
}
};
// set the defaults
service.restore().then(function(data) {
console.log(data);
return data;
});
});
The console.log() call above dumps out the data as expected. However, when I am including the UserService in other factories (I have an APIService that makes use of a user-specific API token), the UserService parameter is being flagged as 'undefined' in the code below:
app.factory('APIService',
function($resource, $http, UserService, AppSettings) {
var token = UserService.user.api.token;
...
});
I am sure I am not fully grasping the Angular promise pattern in terms of consuming resolved promises throughout the app.
Updated code:
app.factory('UserService',
function($q, AppSettings) {
var defaults = {
api: {
token: AppSettings.environments[1].api.token
},
email: ''
};
var service = {
user: {},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service.user);
});
return deferred.promise;
}
};
// set the defaults
service.restore().then(function(data) {
console.log(data);
return data;
});
return service;
});
Edit/Additional Info:
Ok, getting close. Have refactored so that I am returning the object properly, but the issue now is that when the APIService gets created and tries to use the properties of the UserService object, they simply don't exist yet as they are only created after the async restore method is resolved. So it's not possible to access the UserService.user.api.token property, as it doesn't exist at that point, so the question is, how do I get that data in APIService when I need it if it is not available at that point? I'm trying to avoid having to put the entire contents of APIService into a callback that fires after a hypothetical new UserService.get() method that calls the callback on resolution of the promise. Any final guidance appreciated.
Your service is wrong. Please look at my fix:
app.factory('UserService',
function($q, AppSettings) {
var defaults = {
api: {
token: AppSettings.environments[1].api.token
},
email: ''
};
var service = {
user: {},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service.user); // <--- return the user in here
});
return deferred.promise;
}
};
// set the defaults
service.restore().then(function(data) {
console.log(data);
return data;
});
return service; // <--- return the service to be used injected when injected
});
[EDIT]
answer to your new question: Dont access user directly. create a new function in your service like getUser() that returns a promise. In that function return the user if it is already retreived otherwise return the restore() function:
var service = {
user: null,
getUser: function() {
if (service.user)
{
var deferred = $q.defer();
deferred.resolve(service.user);
return deferred.promise;
}
else
return service.restore();
},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service.user); // <--- return the user in here
});
return deferred.promise;
}
};
You're not returning an object from your factory. So when you try to inject your UserService parameter, it gives undefined because you haven't returned anything from your UserService function.
If you return your service variable, I think you'll get the behavior you're looking for.

Angular Http Promise Result

I have a little abstraction I wrote around $http that I use to make calls from a security service. The problem I am having is that during a request that results in a 401, the deferred does not seem to propogate correctly.
I am using it like so, and even if it is a 401 it alerts good... I would expect this to alert bad.
security.login('test', 'password').then(function (r) {
alert('good');
}, function (r) {
alert('bad');
});
Http Wrapper
angular.module('app').provider('http', function () {
return {
$get: ['$http', 'url', function ($http, url) {
function http(config) {
config.headers = {
'X-Requested-With': 'XMLHttpRequest'
};
config.url = url.formatUrl(config.url);
return $http(config);
}
return {
delete: function (url, data) {
return http({ url: url, data: data, method: 'DELETE' });
},
get: function (url) {
return http({ url: url, method: 'GET' });
},
post: function (url, data) {
return http({ url: url, data: data, method: 'POST' });
},
put: function (url, data) {
return http({ url: url, data: data, method: 'PUT' });
}
};
}]
};
});
Security Service
function User() {
this.displayName = '';
this.permissions = [];
}
angular.module('app').provider('security', function () {
var _user = new User();
return {
$get: ['$rootScope', '$q', 'cookie', 'http', 'statePermissions', function ($rootScope, $q, cookie, http, statePermissions) {
function login(params) {
return http.post('SS/auth', params).then(function (response) {
cookie.set('authToken', response.data.authToken);
_user = response.data;
$rootScope.$broadcast('event:loginSuccessful', _user);
return response;
}, function (response) {
cookie.expireNow('authToken');
_user = new User();
$rootScope.$broadcast('event:loginUnsuccessful', _user);
$q.reject(response);
});
}
return {
doesAuthTokenExist: function () {
return angular.isDefined(cookie.get('authToken'));
},
getUser: function () {
return _user;
},
isUserAuthorizedForPermission: function (permission) {
var x;
for (x = 0; x < _user.permissions.length; x++) {
if (_user.permissions[x] === permission) {
return true;
}
}
return false;
},
isUserAuthorizedForState: function (stateName) {
if (angular.isDefined(statePermissions[stateName])) {
return this.isUserAuthorizedForPermission(statePermissions[stateName]);
}
return true;
},
login: function (userName, password) {
return login({ username: userName, password: password });
},
loginWithAuthToken: function () {
var authToken = cookie.get('authToken');
return login({ provider: 'token', authToken: authToken });
},
logout: function () {
return http.post('SS/auth/logout').then(function () {
cookie.expireNow('authToken');
_user = new User();
$rootScope.$broadcast('event:logoutSuccessful', _user);
return true;
});
}
};
}]
};
});
Your $http wrapper looks like it's duplicating the functionality of angular's $resource service. Check out the documentation here: http://docs.angularjs.org/api/ngResource.$resource
After a brief look at your code, it seems like the problem could be with how you're using $q. Try rewriting your login method along these lines.
With insight from here:
http://docs.angularjs.org/api/ng.$q
http://docs.angularjs.org/api/ng.$http
function login(params) {
var deferred = $q.defer();
http.post('SS/auth', params).success(function (response) {
cookie.set('authToken', response.data.authToken);
_user = response.data;
$rootScope.$broadcast('event:loginSuccessful', _user);
deferred.resolve(response);
}).error(function (error) {
cookie.expireNow('authToken');
_user = new User();
$rootScope.$broadcast('event:loginUnsuccessful', _user);
deferred.reject(error);
});
return deferred.promise;
}
Also, just curious why you are using providers here as it's not obvious to me?

Resources