I'm trying to make a simple login function for my AngularJS application. I'm using Dream Factory for my backend server database and I can't seem to be able to create a session from my login-function.
This is the factory I have set up:
dfdevApp.factory('SessionService', function($resource, $q) {
var sessionResource = $resource('https://dsp-myusername.cloud.dreamfactory.com/rest/user/session', {},
{ update: { method: 'PUT' }, query: {method: 'GET', isArray: false} });
return {
create: function (user) {
var deferred = $q.defer();
sessionResource.save(user, function (result) {
deferred.resolve(result);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
});
And this is the code from my controller:
// $scope.ting = Liste.get()
$scope.user = {'email' : '', 'password': ''};
$scope.login = function() {
console.log(JSON.stringify($scope.user));
$scope.user = SessionService.create(JSON.stringify($scope.user), function(success) {
$rootScope.loggedIn = true;
$location.path('/');
}, function(error) {
$scope.loginError = true;
});
};
});
I get a 400 every time I try to post.
Your post should be like this one:
{"email":"you#youremail.com","password":"yourpassword"}
Also don't forget to include your app_name in the URL or as a header (in this case, call it X-DreamFactory-Application-Name).
You can find more info here:
http://blog.dreamfactory.com/blog/bid/326379/Getting-Started-with-the-DreamFactory-API
I also built an "SDK" which handles all this for you.
https://github.com/dreamfactorysoftware/javascript-sdk
Related
I get a value of "True" in my response. How come my debugger and alert and AccessGranted() in the .then of my $http is not being invoked. Below is my Script:
app.controller("LoginController", function($scope, $http) {
$scope.btnText = "Enter";
$scope.message = "";
$scope.login = function() {
$scope.btnText = "Please wait...";
$scope.message = "We're logging you in.";
$http({
method: 'post',
url: '/Login/Login',
data: $scope.LoginUser
}).then(function (response) {
debugger;
alert(response.data);
if (response.data == "True") {
AccessGranted();
} else {
$scope.message = response.data;
$scope.btnText = "Enter";
}
},
function (error) {
$scope.message = 'Sending error: ' + error;
});
}
$scope.AccessGranted = function() {
window.location.pathname("/Home/HomeIndex");
}
});
This is in my HomeController
public ActionResult HomeIndex()
{
var am = new AuditManager();
var auditModel = new AuditModel()
{
AccountId = 0,
ActionDateTime = DateTime.Now,
ActionName = "Home",
ActionResult = "Redirected to Home"
};
am.InsertAudit(auditModel);
return View("Index");
}
Please see image for the response I get.
seems like your approach is wrong
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Try this,
$http({
method: 'post',
url: '/Login/Login',
data: $scope.LoginUser
})
.then(function (response) {
console.log(response);
},
function (error) {
console.log(error);
});
And check your browser console for logs or any errors
Make sure the response is application/json content type, and content is json.
You can also write own httpProvider for check result from server
module.config(['$httpProvider', function ($httpProvider) {
...
I would suggest you to code like this instead of then so whenever there is success, The success part will be invoked.
$http.get('/path/').success(function (data) {
$scope.yourdata = data.data;
//console.log($scope.yourdata);
}).error(function (error){
//error part
});
I'm attemping to call a RESTful method via $resource as following:
Resource:
angular.module('secure',['ngResource']).factory('Vehicle', function ($resource) {
return $resource('/secure/vehicle/index', { id: '#id' }, {
query: {
method: 'GET',
isArray: true
},
delete: {
method: 'DELETE',
isArray: false,
url: '/secure/vehicle/delete/:id'
}
});
});
Then, from other service I inject the above factory and I call DELETE method in this way:
factory.delete = function (procedureId) {
var vehicle = new Vehicle();
vehicle.$delete({id: procedureId}, function () {
//success
deferred.resolve();
}, function (errResponse) {
// fail
console.log(errResponse);
});
return deferred.promise;
};
(Don't pay attention to deferred things, it doesn't work with or without it)
Unfortunately, I always get the same answer:
Remote Address:127.0.0.1:8080
Request URL:http://localhost:8080/secure/vehicle/delete/21
Request Method:DELETE
Status Code:422 Unprocessable Entity
The call itself is set up properly (secure/vehicle/delete/21). In fact, if I do the same, but instead of using $resource variable, using $http, everything works!
$http({
'method': 'DELETE',
'url': '/secure/vehicle/delete/' + procedureId,
'headers': {
'Content-Type': 'application/json'
},
'data': ""
})
.success(function () {
// success
})
.error(function (data, status) {
console.log(data.errors);
});
So, I guess something is missing in $resource-way, but what? Any help, it will be appreciated!
EDIT:
It seems it's a backend problem when it reads the entire url call. If I call DELETE resource, using $http, adding the data: "" as I show above, the backend initializes itself properly.
But if I try the $resource-way, the required params are pre-configured and that doesn't like to the backend, so I need to find the way to say to $resource how to add something like data: "", any ideas?
Test proof that it works:
angular.module('secure', ['ngResource']).factory('Vehicle', function($resource) {
return $resource('/secure/vehicle/index', {
id: '#id'
}, {
query: {
method: 'GET',
isArray: true
},
delete: {
method: 'DELETE',
isArray: false,
url: '/secure/vehicle/delete/:id'
}
});
});
angular.module('secure').factory('VehicleFactory', function(Vehicle, $q) {
var factory = {}
factory.delete = function(procedureId) {
var deferred = $q.defer();
var vehicle = new Vehicle();
vehicle.$delete({
id: procedureId
}, function(r) {
deferred.resolve(r);
}, function(errResponse) {
console.log(errResponse);
});
return deferred.promise;
};
return factory;
});
describe('VehicleFactory', function() {
var $httpBackend, VehicleFactory
beforeEach(module('secure'));
beforeEach(inject(function(_$httpBackend_, _VehicleFactory_) {
$httpBackend = _$httpBackend_
VehicleFactory = _VehicleFactory_
}))
it('deletes vehicle - vehicle.$delete()', function() {
var r = {
data: 'correct response'
}
$httpBackend.when('DELETE', '/secure/vehicle/delete/123').respond(r)
VehicleFactory.delete(123).then(function(response) {
expect(response.data).toBe(r.data)
})
$httpBackend.flush();
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation()
$httpBackend.verifyNoOutstandingRequest()
})
})
<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>
Some much cleaner way to write delete functionality without $q:
angular.module('secure').factory('VehicleFactory', function(Vehicle) {
var factory = {}
factory.delete = function(procedureId) {
return (new Vehicle()).$delete({
id: procedureId
})
}
return factory;
});
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});
});
Ok, so I have this service that is dependent on another service value that the user can change in the app interface. Something like this:
app.service('Applications', ['$resource', 'URL',
function ($resource, URL) {
var applicationsResource = $resource(URL + '/applications/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.applications;
}
}
});
var applications = applicationsResource.query(function() {
applications.current = applications[0];
});
return applications;
}
]);
app.service('Users', ['$resource', 'URL', 'Applications',
function ($resource, URL, Applications) {
return $resource(URL + '/users/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
headers: {
'User': Applications.current.username,
'Pass': Applications.current.password
},
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.users;
}
}
});
}
]);
Example of working controller code:
app.controller('usersController', ['$scope', '$resource', 'URL', 'Applications',
function ($scope, $resource, URL, Applications) {
$scope.users = [];
$scope.reload = function() {
$scope.loading = true;
var usersResource = $resource(URL + '/users/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
headers: {
'User': Applications.current.username,
'Pass': Applications.current.password
},
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.users;
}
}
});
$scope.users = usersResource.query(function() {
$scope.loading = false;
});
/*
// after injecting Users, this is what I want to do, instead of what's above
$scope.users = Users.query(function() {
$scope.userTable.reload();
$scope.loading = false;
});
*/
};
$scope.$watch('Applications.current', function (newApplication, oldApplication, scope) {
if (newApplication && newApplication !== oldApplication) {
scope.reload();
}
});
}
]);
I want to replace that usersResource with my Users service, but that's where I'm stuck now.
The issue is that no matter what I do, the Applications.current on the Users service is always null. (I only make use of this service after making sure that Applications.current is not null on the controller)
If I move the resource directly to the controller, it works, but I want to move these away from the controllers.
Any tips on how to fix or improve this?
You should know that $resource is async and you call Users service before actually you got response from server and populated applications.current. This a reason why Applications.current is null into Users service.
In your case I would use Uses service into Applications:
app.service('Applications', ['$resource', 'URL', 'Users',
function ($resource, URL, Users) {
var applicationsResource = $resource(URL + '/applications/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.applications;
}
}
});
var applications = applicationsResource.query(function() {
applications.current = applications[0];
// call the Users
Users.query(applications.current) /**/
return /* ... */;
});
return applications;
}
]);
I would like to have service providing a resource as in the following code:
angular.module('myApp.userService', ['ngResource'])
.factory('UserService', function ($resource)
{
var user = $resource('/api/user', {},
{
connect: { method: 'POST', params: {}, isArray:false }
});
return user;
}
Then when using the connect action, I would like to dynamically pass a HTTP header, meaning that it may change for each call. Here is an example, in the controller, please see the comment in the code :
$scope.user = UserService;
$scope.connect = function ( user )
{
var hash = 'Basic ' + Base64Service.encode(user.login + ':' + user.password);
// I would like this header to be computed
// and used by the user resource
// each time I call this function
$scope.user.headers = [{Authorization: hash}];
$scope.user.connect( {},
function()
{
// successful login
$location.path('/connected');
}
,function()
{
console.log('There was an error, please try again');
});
}
Do you know a way to do that, either directly or via a trick?
Final thoughts
Accepted answer does not fully answer the question as the headers are not totally dynamic because the factory returns actually a factory (!) which is not the case in my code.
As $resource is a factory, there is no way to make it dynamic.
I finally destroy the resource object each time the user connects. This way, I have the resource with a header computed when the user connects.
The solution provided by #Stewie is useful for that, so I keep it as accepted.
Here is how I did the connect, which can be used multiple times as the resource is destroyed/recreated when (re)connecting:
this.connect = function (user)
{
self.hash = 'Basic ' + Base64Service.encode(user.login + ':' + user.password);
console.log("CONNECT login:" + user.login + " - pwd:" + user.password + " - hash:" + self.hash);
if (self.userResource)
{
delete self.userResource;
}
self.userResource = $resource('/api/user/login', {}, {
connect: {
method: 'POST',
params: {},
isArray: false,
headers: { Authorization: self.hash }
}
});
var deferred = $q.defer();
self.userResource.connect(user,
function (data)
{
//console.log('--------- user logged in ----- ' + JSON.stringify(data));
// successful login
if (!!self.user)
{
angular.copy(data, self.user);
}
else
{
self.user = data;
}
self.setConnected();
storage.set('user', self);
deferred.resolve(self);
},
function (error)
{
self.user = {};
self.isLogged = false;
storage.set('user', self);
deferred.reject(error);
}
);
return deferred.promise;
};
Starting from angularjs v1.1.1 and ngResource v.1.1.1 this is possible to accomplish using the headers property of the $resource action object.
You may wrap your resource in a function which accepts custom headers as a parameter and returns a $resource object with your custom headers set at the appropriate action definitions:
PLUNKER
var app = angular.module('plunker', ['ngResource']);
app.controller('AppController',
[
'$scope',
'UserService',
function($scope, UserService) {
$scope.user = {login: 'doe#example.com', password: '123'};
$scope.connect = function() {
// dropping out base64 encoding here, for simplicity
var hash = 'Basic ' + $scope.user.login + ':' + $scope.user.password;
$scope.user.headers = [{Authorization: hash}];
UserService({Authorization: hash}).connect(
function () {
$location.url('/connected');
},
function () {
console.log('There was an error, please try again');
}
);
};
}
]
);
app.factory('UserService', function ($resource) {
return function(customHeaders){
return $resource('/api/user', {}, {
connect: {
method: 'POST',
params: {},
isArray: false,
headers: customHeaders || {}
}
});
};
});
I set my Services up a little differently.
angular.module('MyApp').factory('UserService',function($resource, localStorageService) {
var userResource = function(headers) {
return $resource("api/user", {},
{
get: {
method: 'GET',
headers: headers
},
create: {
method: 'POST',
headers: headers
}
}
);
};
return {
api: userResource,
get: function(userid){
var service = this;
return service.api({"token": "SomeToken"}).get({userid: userid}, function (data){
return data;
});
},
create: function(user){
var service = this;
return service.api({"token": localStorageService.get("token")}).create({user: user}, function (data){
return data;
});
}
};
});