This question already has answers here:
Is this a "Deferred Antipattern"?
(3 answers)
Closed 5 years ago.
In my $routeProvider I have a resolve property to get data from an API before I instantiate my controller. Why is it when I use the $q with the $http service, I can pass the data to my controller. Aren't they both doing the same thing?
Here is my original approach where the data wasn't able to get passed to the controller:
AccountService.js
app.factory('AccountService', ['$http', '$q', function ($http, $q) {
return {
GetAccounts: function () {
return $http.get('api/Account/GetAccounts')
.then(function success(response) {
return response;
}, function error(response) {
̶r̶e̶t̶u̶r̶n̶ throw console.log("Oops!");
});
},
};
}]);
To pass the data into the controller I change up the GetAccounts to this:
app.factory('AccountService', ['$http', '$q', function ($http, $q) {
return {
GetAccounts: function () {
var deferred = $q.defer();
$http.get('api/Account/GetAccounts')
.then(function (data) {
deferred.resolve(data.data);
})
.catch(function (response) {
deferred.reject(response);
})
return deferred.promise;
},
};
}]);
route.js
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/users', {
template: '<admin accounts="$resolve.accounts"></admin>',
resolve: {
accounts: function (AccountService) {
return AccountService.GetAccounts()
.then(function (data) {
return data;
})
}
}
})
}]);
$q is Angulars wrapper for promises. $http returns a promise directly so whether you create a new promise with a new $q or $http directly, they are the same
So essentially you dont have to do what you do in AccountService, only this is needed:
app.factory('AccountService', ['$http', function ($http) {
return {
GetAccounts: function () {
return $http.get('api/Account/GetAccounts')
.then(function (data) {
return data.data;
})
},
};
}]);
I would save the .catch() for your resolve to handle and maybe wrap the error in a nice error construct unless your backend already does that.
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/users', {
template: '<admin accounts="$resolve.accounts"></admin>',
resolve: {
accounts: function (AccountService) {
return AccountService.GetAccounts()
.catch( function(err) { ̶r̶e̶t̶u̶r̶n̶ throw new BusinessError( err ) } )
}
}
})
}]);
or let the calling controller/component handle it, its an architecture decision.
Related
I am writing a factory that returns a list of dog breeds from a php file but I can't return the response data to my controller. I have this as a factory
angular.module('myApp')
.factory('DataService, function($http) {
return {
get_breeds: function() {
method: 'GET',
url: 'http://localhost:8080/php/api/getbreeds.php'
}).then(function onSuccess(response) {
console.log(response); // responds with 200
return response.data;
}).catch(function onError(err) {
console.log(err);
})
}
}
});
and this is part of my component
angular.module('myApp')
.component('homeComponent', {
templateUrl: 'home.component.html,
controller: function (DataService) {
DataService.get_breeds()
.then(function(response) {
var dog_breeds = response;
console.log(dog_breeds)
});
...
But I'm not getting anything returned and an error message. Can someone guide me in the right direction?
I figured it out not long after I posted the question!
In the end I made a factory that just returned the data from the URL:
angular.module('myApp')
.factory('DataService', function($http) {
return {
get_dog_breeds: function(urlString) {
return $http.get(urlString)
}
}
})
And then called it in my component's controller like so:
DataService.get_dog_breeds('http://localhost:8080/php/api/getbreeds.php')
.then(function (response) {
console.log(response.data);
$scope.dog_breeds = response.data;
return $scope.dog_breeds;
}).catch(function (error) {
console.log('Error returned: ' + error
+ ' Please make sure the service you\'re trying to use exists');
return false;
});
And it just worked!
Loving AngularJS so far btw guys :)
Just return the http request from the service
angular.module('myApp')
.factory('DataService, function($http) {
var service = this;
service.get_breeds = function() {
return $http.get('http://localhost:8080/php/api/getbreeds.php')
}
}
});
Try doing this it should work
Use this in your factory
angular.module('myApp')
.factory('DataService', function($http) {
return {
get_breeds: function() {
var request = $http({
method: 'GET',
url: 'http://localhost:8080/php/api/getbreeds.php'
});
return request;
}
}
});
and use this in controller to retrieve the data
DataService.get_breeds()
.then(function(response){
console.log(response.data);
});
I want to get info on the currently logged in user so that i can display it for example. I have tried the following code but i get the error AuthService.getAuthMember is not a function can anyone help
var myApp = angular.module('myApp', ['ngResource', 'ngRoute']);
myApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'partials/main.html',
access: {restricted: true}
})
.when('/api/meetups', {
templateUrl: 'partials/main.html',
access: {restricted: true}
})
.when('/login', {
templateUrl: 'partials/login.html',
controller: 'loginController',
access: {restricted: false}
})
.when('/prive', {
templateUrl: 'partials/prive.html',
controller: 'userController',
access: {restricted: true}
})
.when('/logout', {
controller: 'logoutController',
access: {restricted: true}
})
.when('/register', {
templateUrl: 'partials/register.html',
controller: 'registerController',
access: {restricted: false}
})
.when('/one', {
template: '<h1>This is page one!</h1>',
access: {restricted: true}
})
.when('/two', {
template: '<h1>This is page two!</h1>',
access: {restricted: false}
})
.otherwise({
redirectTo: '/'
});
});
myApp.run(function ($rootScope, $location, $route, AuthService) {
$rootScope.$on('$routeChangeStart',
function (event, next, current) {
AuthService.getUserStatus()
.then(function(){
if (next.access.restricted && !AuthService.isLoggedIn()){
$location.path('/login');
$route.reload();
}
});
});
});
angular.module('myApp').factory('AuthService',
['$q', '$timeout', '$http',
function ($q, $timeout, $http, $cookies) {
// create user variable
var user = null;
// we must create authMemberDefer var so we can get promise anywhere in app
var authenticatedMemberDefer = $q.defer();
// return available functions for use in the controllers
return ({
isLoggedIn: isLoggedIn,
getUserStatus: getUserStatus,
login: login,
logout: logout,
register: register,
getAuthMember: getAuthMember,
setAuthMember: setAuthMember
});
function isLoggedIn() {
if(user) {
return true;
} else {
return false;
}
}
//this is function that we will call each time when we need auth member data
function getAuthMember() {
return authenticatedMemberDefer.promise;
}
//this is setter function to set member from coockie that we create on login
function setAuthMember(member) {
authenticatedMemberDefer.resolve(member);
}
function getUserStatus() {
return $http.get('/user/status')
// handle success
.success(function (data) {
if(data.status){
user = true;
} else {
user = false;
}
})
// handle error
.error(function (data) {
user = false;
});
}
function login(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/login',
{username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
user = true;
deferred.resolve();
//**
$cookies.putObject('loginSession', data);
// here create coockie for your logged user that you get from this response, im not sure if its just "data" or data.somethingElse, check you response you should have user object there
} else {
user = false;
deferred.reject();
}
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function logout() {
// create a new instance of deferred
var deferred = $q.defer();
// send a get request to the server
$http.get('/user/logout')
// handle success
.success(function (data) {
user = false;
deferred.resolve();
//on log out remove coockie
$cookies.remove('loginSession');
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function register(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/register',
{username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
deferred.resolve();
} else {
deferred.reject();
}
})
// handle error
.error(function (data) {
deferred.reject();
});
// return promise object
return deferred.promise;
}
}]);
myApp.controller('meetupsController', ['$scope', '$resource', 'AuthService', function ($scope, $resource, AuthService) {
var Meetup = $resource('/api/meetups');
$scope.meetups = []
Meetup.query(function (results) {
$scope.meetups = results;
});
/AuthService.getAuthMember().then(function(member){
console.log(member);
//here your member should be and you can apply any logic or use that data where u want
$scope.username1=member.username;
});
$scope.createMeetup = function () {
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.text = $scope.username;
meetup.$save(function (result) {
$scope.meetups.push(result);
$scope.meetupName = '';
$scope.username = '';
});
}
}]);
myApp.controller('userController', ['$scope', '$resource', function ($scope, $resource) {
/* var Meetup = $resource('/api/user');
$scope.users = []
Meetup.query(function (results) {
$scope.users = results;
});
*/
var Meetup = $resource('/api/user', {},{
query: {method: 'get', isArray: true}
});
$scope.users = []
$scope.text='mikyas';
Meetup.query({text: $scope.text}).$promise.then(function (results) {
$scope.users = results;
}, function(error) {
// console.log(error);
$scope.meetups = [];
});
}]);
Can you change your run to something like this
myApp.run(function($rootScope, $location, $route, AuthService, $cookies) {
$rootScope.$on('$routeChangeStart',
function(event, next, current) {
if ($cookies.get('loginSession')) {
var session = JSON.parse($cookies.get('loginSession'));
AuthService.setAuthMember(session);
} else {
$location.path('/login');
}
});
});
Also try console.log(AuthService) and check if you see that getAuthMember() function.
Edit:
var myApp = angular.module('myApp', ['ngResource', 'ngRoute', 'ngCookies']);
Don't forget to include angular cookies.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I have the following scenario, I need data from a particular url.I also need to save this datas into a variable that I can call everywhere in my controller. To overcome such issue, I have written a service like below :
app.service("userData", function ($http, $q) {
var deferred = $q.defer();
this.getUsers = function () {
return $http.get('/users')
.then(function (response) {
deferred.resolve(response.data);
return deferred.promise;
}, function (response) {
deferred.reject(response);
return deferred.promise;
})
;
};
});
And then my controller looks like :
app.controller('UserCtrl', function($scope, $q, userData) {
var myData = userData.getUsers()
.then(
function (result) {
$scope.users = result;
},
function (error) {
console.log(error.statusText);
}
);
console.log(myData)
});
It prints :
So how can i store data into a variable which is accessible all over the controller ?
Thanks..
I suggest you something like that:
angular.module("app").factory("userService", userService);
userService.$inject = ["$http", "$q"];
function userService($http, $q) {
var deferred = $q.defer();
var promise = null;
var currentUser = null;
return {
getUserInfo: function () {
if (promise) {
deferred.resolve(currentUser);
return (currentUser) ? deferred.promise : promise;
}
else {
promise = $http.get("http://swapi.co/api/people/1/").then(function (response) {
return currentUser = response.data.name;
});
return promise;
}
}
}
}
you can call then any time userService.getUserInfo() to get the cached data.
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl',
controllerAs: 'vc'
});
}])
.controller('View1Ctrl', function (userService) {
var vm = this;
userService.getUserInfo().then(function (result) {
vm.name = result
}, function (err) {
vm.name = err
});
});
I create a git repository to test it out: https://github.com/leader80/angular-cache-service
I hope it helps
I'm trying to use authenticate with a JWT using my api in Laravel, and it works when I just do the authentication in the controller but I'm trying to do this proper and use a service, however I'm getting an error.
The thing is, when I refresh after the error, it redirects to the dashboard (set that up in app.js), which means a user is authenticated.
What am I doing wrong? Why is it giving me this error?:
TypeError: Cannot read property 'then' of undefined
...it should be redirecting to the 'dashboard' route. The error is at .then(function(data) in the controller.
Here is my service:
(function() {
'use strict';
angular
.module('app')
.factory('userService', ['$http', '$auth', userService]);
function userService($http, $auth) {
var service = {
userLogin: userLogin
};
return service;
function userLogin(credentials) {
$auth.login(credentials)
.then(function() {
$http.get('/api/v1/authenticate/user')
.success(function(res) {
return res;
})
.error(function(error) {
return error;
});
});
};
};
})();
And here is my controller:
(function() {
'use strict';
angular
.module('app')
.controller('LoginController', ['$http', '$rootScope', 'userService', LoginController]);
function LoginController($state, $rootScope, userService) {
var vm = this;
vm.login = login;
vm.error;
function login() {
var credentials = {
email: vm.email,
password: vm.password
};
userService.userLogin(credentials)
.then(function(data) {
if (data) {
var user = JSON.stringify(data.user);
localStorage.setItem('user', user);
$rootScope.currentUser = data.user;
$state.go('dashboard');
} else {
vm.error = error;
}
});
}
}
})();
In the service, you should return a promise if you want to "then" the method. In that service, userLogin method is returning nothing.
You have to pass your service a callback, or use $q.deferred.
function userService($auth, $http) {
return {
userLogin: function(url, credentials, callback) {
$auth.login(credentials).then(function(data) {
$http.get(url).then(function(res) {
callback(res);
});
});
}
};
}
And in your controller
userService("/api/v1/authenticate/user", {password: "pwd", email: "me#me.com"}, function(res) {
res.status; //200
res.data; //whatever you got back from the GET request
});
To be more precise, you're getting cannot read property then of undefined because you're not returning anything to the original invoker. By default javascript will return undefined if nothing is returned, so you get undefined when you try to synchronously return an asynchronous function.
In service you can make use of $q service provided by angular like this
(function() {
'use strict';
angular
.module('app')
.factory('userService', ['$http', '$auth', '$q', userService]);
function userService($http, $auth, $q) {
var service = {
userLogin: userLogin
};
return service;
function userLogin(credentials) {
var deferred = $q.defer();
$auth.login(credentials)
.then(function() {
$http.get('/api/v1/authenticate/user')
.success(function(res) {
// return res;
deferred.resolve(res);
})
.error(function(error) {
// return error;
deferred.reject(error);
});
});
return deferred.promise;
};
};
})();
And in controller you can call the service function as you are currently doing..
(function() {
'use strict';
angular
.module('app')
.controller('LoginController', ['$http', '$rootScope', 'userService', LoginController]);
function LoginController($state, $rootScope, userService) {
var vm = this;
vm.login = login;
vm.error;
function login() {
var credentials = {
email: vm.email,
password: vm.password
};
userService.userLogin(credentials)
.then(function(data) {
if (data) {
var user = JSON.stringify(data.user);
localStorage.setItem('user', user);
$rootScope.currentUser = data.user;
$state.go('dashboard');
} else {
vm.error = error;
}
});
}
}
})();
Hope this helps.
I'm new in angular and I'm working on a project that depends on service and factories. my problem is when I'm using a static jason array for response, the variables are filled correctly and are shown in view, but when I change it to a ajax request and get it from a json file, the response comes successful but the controller variables are not successfully loaded with data.
this is my angular project structure:
'use strict';
angular
.module('mytestapp',['ngRoute'])
.config(config)
.controller('HomeCtrl', HomeCtrl)
.controller('AboutCtrl', AboutCtrl)
.factory('GeneralInit', GeneralInit)
.service('UserSrv', UserSrv);
GeneralInit.$inject = ['UserSrv','$q'];
HomeCtrl.$inject = ['GeneralInit','$timeout','UserSrv'];
and here are my config:
function config($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'template/home.html',
controller: 'HomeCtrl',
controllerAs: 'hmc',
resolve: {
GeneralInit: function(GeneralInit){
return GeneralInit();
}}
})
.when('/about', {
templateUrl: 'template/about.html',
controller: 'AboutCtrl',
controllerAs: 'abc',
resolve: {
GeneralInit: function(GeneralInit){
return GeneralInit();
}}
});
}
here is my service:
function UserSrv($http) {
var User={};
var service = {
getUser: Get,
updateUser: Update,
logoutUser: Logout
};
return service;
function Get() {
//return {"FirstName":"StaticName","LastName":'StaticLastName'}
$http.get('/user.json')
.success(function(data, status, headers, config) {
User = data;
console.log(User);
return User;
})
.error(function(data, status, headers, config) {
})
}
function Update() {
}
function Logout() {
}
}
My controller and initialize item:
function GeneralInit(UserSrv,$q)
{
return function() {
var User = UserSrv.getUser(); //{'FirstName':'FstName','LastName':'LstName'};//
var Base='browser';
return $q.all([User, Base]).then(function(results){
return {
User: results[0],
Base: results[1]
};
});
}
}
function HomeCtrl(GeneralInit,$timeout)
{
var hmc= this;
$timeout(function(){
hmc.User=GeneralInit.User;
console.log(hmc.User);
}
,0);
}
The reason why you don't see any data in the console.log(hmc.User); statement is because by the time this statement executes, the promise is not actually resolved (the request fetching users has not yet returned). Though digest cycle is invoked as a result of using $timeout, hmc.User does not have data yet.
Try invoking the digest cycle after the requests actually return in your GeneralInit method, and you should have the data available.
And also you should probably change your Get method to return a promise:
function UserSrv($http) {
var User = {};
var service = {
getUser: Get
};
return service;
function Get() {
return $http.get('/user.json')
.success(function(data, status, headers, config) {
User = data;
console.log(User);
return User;
})
.error(function(data, status, headers, config) {
})
}
}
Your GeneralInit function is expecting getUser to return a promise, so just change it to this:
function UserSrv($http, $q) {
var User={};
var service = {
getUser: Get,
updateUser: Update,
logoutUser: Logout
};
return service;
function Get() {
var deferred = $q.defer();
//return {"FirstName":"StaticName","LastName":'StaticLastName'}
$http.get('/user.json')
.success(function(data, status, headers, config) {
User = data;
console.log(User);
deferred.resolve(user);
return User;
})
.error(function(data, status, headers, config) {
});
return deferred.promise;
}
}