I'm trying to inject dependencies into functions but it does not seem to be working. Am I doing something wrong or should I do something different instead of trying to inject the dependencies into the function? I'm trying to get the following piece of code working:
angular.module('app').controller('usersController', usersController);
usersController.$inject = ['$http', '$cookies'];
function usersController($http, $cookies) {
var self = this;
self.isLoggedIn = ($cookies.get('token') && $cookies.get('secret'));
register.$inject = ['$http', '$cookies'];
self.register = register;
function register($http, $cookies) {
console.log(self.username);
$http.post('/register', {
username: self.username,
password: self.password,
email: self.email
}).then(function successCallback(response) {
self.isLoggedIn = true;
$cookies.put('token', response.data.token);
$cookies.put('secret', response.data.secret);
}, function errorCallback(response) {
console.log('Something went wrong.');
});
};
};
This looks inappropriate for a couple of reasons; your controller already has those services injected, so it makes little sense to inject them again, and from what I can tell, no state needs to be passed into your function for it to actually work.
This would be cleaner - remove the extraneous $inject and clean up the function params list.
angular.module('app').controller('usersController', usersController);
usersController.$inject = ['$http', '$cookies'];
function usersController($http, $cookies) {
var self = this;
self.isLoggedIn = ($cookies.get('token') && $cookies.get('secret'));
self.register = register;
function register() {
console.log(self.username);
$http.post('/register', {
username: self.username,
password: self.password,
email: self.email
}).then(function successCallback(response) {
self.isLoggedIn = true;
$cookies.put('token', response.data.token);
$cookies.put('secret', response.data.secret);
}, function errorCallback(response) {
console.log('Something went wrong.');
});
}
}
You don't have to. $http and $cookies are already available in that scope
function usersController($http, $cookies) {
// ...
this.register = function() {
$http.post(...)
// and so on
}
}
Related
I made a service that's using $http to post login data and get authentication token, but whenever i inject it into the controller, it breaks (looks like html doesnt see it). When I remove the service injection, or inject one using $resource instead, everything works fine.
Here's the code for the service:
MyApp.service('LoginSrv', ['$http', function User($http) {
var userData = {
isAuthenticated: false,
username: '',
bearerToken: '',
expirationDate: null,
};
function setHttpAuthHeader() {
$http.defaults.headers.common.Authorization = 'Bearer ' + userData.bearerToken;
}
this.getUserData = function(){
return userData;
};
this.authenticate = function(username, password, successCallback, errorCallback) {
var config = {
method: 'POST',
url: '/accounts/login',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
data: 'grant_type=password&username=' + username + '&password=' + password,
};
$http(config)
.success(function(data) {
userData.isAuthenticated = true;
userData.username = data.userName;
userData.bearerToken = data.access_token;
userData.expirationDate = new Date(data['.expires']);
setHttpAuthHeader();
if (typeof successCallback === 'function') {
successCallback();
}
})
.error(function(data) {
if (typeof errorCallback === 'function') {
if (data.error_description) {
errorCallback(data.error_description);
} else {
errorCallback('Unable to contact server; please, try again later.');
}
}
});
};
}]);
And here is the controller code:
MyApp.controller('mainCtrl', function ($scope, LoginSrv)
{
$scope.loginUsername = 'Jan';
$scope.loginPassword = 'Maria';
$scope.userLogin = new LoginSrv();
$scope.loginError = false;
function onSuccesfulLogin () {};
function onFailedLogin(error) {};
$scope.login = function () {
userLogin.authenticate($scope.loginUsername, $scope.loginPassword, onSuccesfulLogin, onFailedLogin);
};
});
Services are singleton so you need not give a "new",
I Made a brief example of the same flow you need and worked well, I hope to help:
The Service
angular.module("yourapp").factory('LoginSrv', function User($http) {
var _authenticate = function(username, password) {
console.log('logged')
};
return {
authenticate: _authenticate
};
});
The Controller
angular.module("yourapp").controller('mainCtrl', function ($scope, $http, LoginSrv)
{
$scope.loginUsername = 'Jan';
$scope.loginPassword = 'Maria';
$scope.userLogin = LoginSrv;
$scope.loginError = false;
$scope.login = function () {
userLogin.authenticate($scope.loginUsername, $scope.loginPassword);
};
});
The other answer does a good job of explaining your LoginSrv related exception and explains how implement a service/factory. However what it fails to note is the differences between the two.
Factory
When injecting a factory you will be provided with the return value as a result of invoking the factory function.
Service
When injecting a service you will be provided with an instance of the service function. That is akin to new serviceFunction();. It is important to note angular will do this the first time the service is injected, all others times it is injected you will receive the same instance.
So Factories are meant for object creation (hence the name) and services are meant, well, for services. So shared logic.
So in my opinion (that's all it is) your existing service is trying to do both. You appear to have a user object that your wanting to create but also methods for authenticating a user. It would be best to put that user object in a factory which returns a create method to create a new user. Then put the authentication logic in a service. Then your authentication is not directly coupled to your user implementation.
Possible Implementation (pseudo code)
.factory('userFactory', function () {
return {
create: function (details) {
return Object.create({}, {
username: {
value: details.username
},
password: {
value: details.password
},
isAuthenticated: {
value: false
}
});
}
}
});
.service('auth', function ($http) {
this.authenticate = function (username, password) {
//Build config
return $http();
}
});
.controller('test', function ($scope, userFactory, auth) {
var user = userFactory.create({
username: 'hiya',
password: 'epic secrets'
});
auth.authenticate(user.username, user.password)
.then(function (d) {
user.isAuthenticated = d.isAuthenticated;
})
.catch(SomeGenericErrorHandler);
});
any questions just ask
So i can get data from a URL if i do it from with in my controller. But if i take that and move it into a factory it doesn't work. So what am i doing wrong?
angular.module('starter.notifications', [])
.factory('Notifications', function($http) {
var link = "http://localhost:8000/notifications";
var notifications = [];
return {
getAll: function()
{
return $http.get(link).success(function(data, status, headers, config) {
notifications = data;
return notifications;
});
},
This code works if i move it into a controller, but why doesn't it work in a factory?
This is how i did it.
In the top of your app.js
angular.module('app', ['ionic', 'app.controllers', 'app.services','ngCordova'])
Let ionic knows you have an services.js by declaring it.
services.js (an http post request example)
angular.module('app.services', ['ngCordova'])
.factory('dataFactory', function($http, $cordovaGeolocation){
var dataFactory = {};
dataFactory.login = function(username, password){
var config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
var data = 'userID=' + username + '&password=' + password +'';
var httpAddressDoLogin = "http://YOURURL";
return $http.post(httpAddressDoLogin, data, config);
};
return dataFactory;
})
In your controller:
dataFactory.login(username, password).then(function(resp) {
Hope that helps.
On services.js $http.get resulting promise not object array. To make it work write like this on your services.js
angular.module('starter.services', [])
.factory('Actor', function($http) {
var actors = $http.get('http://ringkes/slim/snippets/actor').then(function(resp) {
if (resp) {
return = resp['data'];// This will produce promise, not array so can't call directly
} else {
console.error('ERR', err);
}
});
return {
all: function() {
return actors;
}
};
});
then call it on controller like this:
controller('DashCtrl', function($scope,Actor,$http) {
Actor.all().then(function(actors){ $scope.actors = Actor.all();
});
});
In my angular module I wrote a generic http handler for all my ajax requests.'
I was expecting that I could use the service across controllers, but my problem is the promise seems to be global.
Once ControllerOne uses the mapi_loader service, when I load AnotherController (by ng-click="go('/$route_to_load_another_controller')"), AnotherController is loaded a promise that has already returned from ControllerOne even though the URL they fetch are totally different.
So I guess my question is how do I write a service I could use across controllers? Do I really need to write a separate service for each controller where their only difference in code is the URL passed for $http.jsonp?
angular.module('myAppControllers',[])
.service('mapi_loader', ['$http', function($http) {
var promise;
var myService = {
fetch: function(url) {
if ( !promise ) {
promise = $http.jsonp(url)
.then(function (response) {
return response.data.nodes;
});
}
return promise;
}
};
return myService;
}])
.controller('ControllerOne', ['$scope', 'mapi_loader', function ($scope, mapi_loader) {
mapi_loader
.fetch("http://host.com/mapi_data_for_controller_one?callback=JSON_CALLBACK")
.then(function(data) {
$scope.useme = data;
});
}])
.controller('AnotherController', ['$scope', 'mapi_loader', function ($scope, mapi_loader) {
mapi_loader
.fetch("http://host.com/mapi_data_for_another_controller?callback=JSON_CALLBACK")
.then(function(data) {
$scope.useme = data;
});
}])
;
try something like this
angular.module('myAppControllers',[])
.service('mapi_loader', function($http) {
var alreadyLoading = {};
return {
fetch: function(url) {
if ( url in alreadyLoading ) {
return alreadyLoading[url];
}
return alreadyLoading[url] = $http.jsonp(url)
.then(function (response) {
delete alreadyLoading[url];
return response.data.nodes;
});
}
};
})
.controller('ControllerOne', function ($scope, mapi_loader) {
...
})
.controller('AnotherController', function ($scope, mapi_loader) {
...
});
Is it possible within the AuthService to handle the response and set the session with the SessionService?
I doing it in the controller right now with the success callback but I'm still new to Angularjs and trying to understand how to customize a resource.
I'm using Angularjs 1.1.5
app.factory('AuthService', ['$resource', 'SessionService',
function($resource, SessionService) {
return $resource(
'/api/v1/auth/:action',
{action:'#action'},
{
login: {
method:'POST',
params: {
action: 'login'
}
},
logout: {
method:'GET',
params: {
action: 'logout'
}
}
}
);
}
]);
app.controller('LoginCtrl', ['$scope', '$location', 'AuthService', 'SessionService' function LoginCtrl($scope, $location, AuthService, SessionService) {
$scope.credentials = { email: "", password: ""};
$scope.login = function() {
AuthService.login($scope.credentials).success(function() {
SessionService.set('authenticated', true);
$location.path('/home');
});
}
}]);
app.factory("SessionService", function() {
return {
get: function(key) {
return sessionStorage.getItem(key);
},
set: function(key, val) {
return sessionStorage.setItem(key, val);
},
unset: function(key) {
return sessionStorage.removeItem(key);
}
}
});
I'm not sure that $resource is the appropriate abstraction to use here. I think it would be much simpler to implement your AuthService using plain $http. Just implement login and logout as normal function, then you can feel free to do whatever you want there. You should also make sure you return the promise, that way whoever calls login() or logout() can still do .then() on it if they need to do additional things after login. Here's an example:
app.factory('AuthService', ['$http', '$location' 'SessionService',
function($http, $location, SessionService) {
var baseUrl = '/api/v1/auth/';
function onLoginSuccess(data){
SessionService.set('authenticated', true);
$location.path('/home');
}
function onLoginFailure(error){
SessionService.unset('authenticated');
$location.path('/login');
}
return {
login: function(credentials){
return $http.post(baseUrl+'login', credential).then(onLoginSuccess, onLoginFailure);
}
logout: function(){
return $http.get(baseUrl+'logout');
}
};
app.controller('LoginCtrl', ['$scope', 'AuthService', function LoginCtrl($scope, AuthService) {
$scope.credentials = { email: "", password: ""};
$scope.login = function() {
AuthService.login($scope.credentials);
}
}]);
app.factory("SessionService", function() {
return {
get: function(key) {
return sessionStorage.getItem(key);
},
set: function(key, val) {
return sessionStorage.setItem(key, val);
},
unset: function(key) {
return sessionStorage.removeItem(key);
}
}
});
Your server side script on path /api/v1/auth/login should return a result to indicate that the login is successfully granted or not.
For example, If the log in is granted, then /api/v1/auth/login returns the success response 200.
If the login is denied, then /api/v1/auth/login returns the bad request response (failure) 400.
If with this condition, your code should be written as followed,
app.controller('LoginCtrl', ['$scope', '$location', 'AuthService', 'SessionService'
function LoginCtrl($scope, $location, AuthService, SessionService) {
$scope.credentials = { email: "", password: ""};
$scope.login = function() {
AuthService.login($scope.credentials, function(data) { //Success callback
SessionService.set('authenticated', true);
$location.path('/home');
}, function(error){ //Failure callback
SessionService.unset('authenticated');
$location.path('/login');
});
}
}]);
I have build a simple service that makes multiple requests. The service has two methods. I cannot call one method from another inside the service.
Plunkr: http://plnkr.co/edit/2fERik4uTxbxlVOhncMd?p=preview
app.factory('Report', ['$http', function($http){
var Authors = {
reports : [],
requests :[{'url':'data/data.cfm','response':'first'},
{'url':'data.json','response':'second'},
{'url':'data.json','response':'third'},
{'url':'data.json','response':'forth'}],
getReport : function(target, source, response, callback) {
return $http({ url:source,
method:"POST",
params:{url : target}
}).success(function(result) {
$scope.progress = response;
angular.extend($scope.user, result)
console.log($scope.user)
}
).error(function(error){
$scope.progress = response
})
},
startQueue : function (target) {
var promises = [];
this.requests.forEach(function (obj, i) {
console.log(obj.url)
promises.push(getReport(target, obj.url, obj.response, function(value){
reports.push(value);
console.log(value)
}));
});
$q.all(promises).then(function () {
console.log("Finito");
},function(error){
console.log("errori")
});
}
};
return Authors;
}])
When I try to call getReport from inside startQueue I get error: getReport is not defined.
Change your factory to:
app.factory('Report', ['$http', function($http){
var Authors = {
reports : [],
requests :[{'url':'data/data.cfm','response':'first'},
{'url':'data.json','response':'second'},
{'url':'data.json','response':'third'},
{'url':'data.json','response':'forth'}],
};
Authors.getReport = function(target, source, response, callback) {
};
Authors.startQueue = function (target) {
};
return Authors;
}])
I know this is 'really late', but it looks like it's because you don't have $q injected.
Change:
app.factory('Report', ['$http', function($http){
to
app.factory('Report', ['$http','$q', function($http,$q){