So I started using AngularJS and currently looking at promises. So my old code looks like this:
app.controller('CustomerController', function ($scope Customers, $q) {
init();
function init() {
Customers.getCustomers()
.then(function (response) {
$scope.customers = response.data;
}, function (error) {
console.log(error);
});
}
});
app.factory('Customers', function ($http) {
return {
getCustomers: function () {
return $http.get('/api/customers');
}
};
});
So what I did in my init function to make a promise is like this:
function init() {
var deferred = $q.defer();
Customers.getCustomers()
.then(function (response) {
deferred.resolve(response.data); // how to pass this into my scope?
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
As you can see, I'm not able to pass it to my scope. Am I doing something wrong here?
I'm not sure what are you trying to do here. But it doesn't make sense to use promises like this in controller. You would need to call your init function somewhere else like this:
init().then(response => $scope.data = response);
In your old code in factory the get method of $http service is returning a promise and you correctly handle the response in controller.
Take into account that the $http service from Angular, already returns a promise. The $http API is based on the deferred/promise APIs exposed by the $q service.
So you could have something like this:
app.controller('CustomerController', function ($scope, Customers) {
init();
function init() {
Customers.getCustomers()
.then(function (data) {
$scope.customers = data;
}, function (error) {
console.log(error);
});
}
});
app.factory('Customers', function ($http) {
return {
getCustomers: function () {
var promise = $http.get('/api/customers').
then(function(response){
return response.data;
},
function(error) {
return response.error;
}
)
return promise;
}
};
});
Related
Am using the cordova-file-transfer plugin for uploads in my app, am trying to wrap in the plugin in an ionic factory to make my code more modular.
angular.module('starter.services')
.factory('$uploader', function($q, $cordovaFileTransfer, $user) {
this.uploadLogo = function(filepath, vendorId) {
var q = $q.defer();
var server = encodeURI(logoUploadUrl(vendorId));
$cordovaFileTransfer.upload(server, filepath, this.logoUploadOptions, true)
.then(function (result) {
q.resolve(result);
}, function (error) {
q.reject(error);
}, function (progress) {
q.resolve(progress);
});
return q.promise;
};
return this;
});
When i use the factory in a controller the first "q.resolve" call is not fired, rather the second one is. I just learned how to use JS promises, and i figure i could be doing something wrong. Any help will be appreciated.
Thank you #maurycy for the help. I sorted it out
angular.module('starter.services')
.factory('$uploader', function($q, $cordovaFileTransfer, $user) {
this.uploadLogo = function(filepath, vendorId) {
var q = $q.defer();
var server = encodeURI(logoUploadUrl(vendorId));
$cordovaFileTransfer.upload(server, filepath, this.logoUploadOptions, true)
.then(function (result) {
q.resolve(result);
}, function (error) {
q.reject(error);
}, function (progress) {
q.notify(progress);
});
return q.promise;
};
return this;
});
I need resolve the http call in my service to return the data, not a promise.
Is this possible with AngularJS (with Ionic Framework)?
Service code:
.factory('ClientService', function ($http) {
const url = 'http://myAPIurl';
var self = this;
var result = [];
self.getClients = function () {
return $http.get(url).then(function (response) {
return response.data;
});
}
return self;
})
In my controller, I use this code that actually returns a promise of my request. In this point, the $scope.clients must be filled with data.
Controller code:
ClientService.getClients().then(function (response) {
$scope.clients = response;
}, function (e) {
console.log(e);
});
I have this service which checks if there a new data from the back end. It it working fine.But the problem is I cant get the data from the service to the controller using $watch nor using the promise.
SERVICE
.service('notificationPollService',function($q, $http, $timeout){
var notification={};
notification.poller = function(){
return $http.get('some/routes/')
.then(function(response) {
$timeout(notification.poller, 1000);
if (typeof response.data === 'object') {
return response.data;
} else {
return $q.reject(response.data);
}
}, function(response) {
$timeout(notification.poller, 5000);
return $q.reject(response.data);
});
}
notification.poller();
return notification;
})
WATCH IN THE CONTROLLER
$scope.$watch('notificationPollService.poller()', function(newVal){
console.log('NEW NOT', response) // does nothing either.
}, true);
PROMISE IN THE CONTROLLER
notificationPollService.poller().then(function(response){
console.log("NEW NOTI", response) // not logging every poll success.
});
Is there a way that I missed on how to solve this? Or am I just doing something wrong?
Probably using promise in this case is not the most convenient approach because it is not supposed to be resolved multiple times. You can try to implement poller with old plain callbacks, you can call them repeatedly without need to create new instance of the promise:
.service('notificationPollService', function ($q, $http, $timeout) {
var notification = {};
notification.poller = function (callback, error) {
return $http.get('some/routes/').then(function (response) {
if (typeof response.data === 'object') {
callback(response.data);
} else {
error(response.data);
}
$timeout(function(){
notification.poller(callback, error);
}, 1000);
});
};
return notification;
});
notificationPollService.poller(function(data) {
$scope.data = data; // new data
}, function(error) {
console.log('Error:', error);
});
I am calling the getWeeklyDates which is calling the cachingGlobalConfigurationService which is again calling the globalConfigurationService if the globalConfiguration data could not be found in the localstorage.
The code =>
return cachingGlobalConfigurationService.getGlobalConfiguration()
.then(function(response1){
works fine when the globalConfiguration is not cached yet because then I make the ajax call and return a promise.
But the above line of code with .then(function(response1) is undefined when my globalConfiguration can be found in the localStorage and just this is returned:
else {
return cachedGlobalConfiguration;
}
I guess I can not use .then in this case but I would like.
How can I fix that?
1
this.getWeeklyDates= function (projectId, currentDate) {
return cachingGlobalConfigurationService.getGlobalConfiguration()
.then(function(response1){
// do business logic
});
2
'use strict';
angular.module('test').service('cachingGlobalConfigurationService', function (localStorageService, globalConfigurationService) {
this.getGlobalConfiguration = function () {
var cachedGlobalConfiguration = localStorageService.get('globalConfiguration');
if (!cachedGlobalConfiguration) {
return globalConfigurationService.getGlobalConfiguration().then(
function (globalConfiguration) {
localStorageService.set('globalConfiguration', globalConfiguration);
return globalConfiguration;
},
function (error) {
console.log('error', error);
});
}
else {
return cachedGlobalConfiguration;
}
};
this.saveGlobalConfiguration = function (globalConfiguration) {
// TODO: Only save to local storage when service.save was successfully
localStorageService.set('globalConfiguration', globalConfiguration);
globalConfigurationService.saveGlobalConfiguration(globalConfiguration);
}
});
3
'use strict';
angular.module('test').service('globalConfigurationService', function ($http) {
this.getGlobalConfiguration = function () {
// TODO get from db
var path = 'scripts/model/globalConfiguration.json';
return $http.get(path).then(function (response) {
return response.data.globalConfiguration;
});
};
this.saveGlobalConfiguration = function (globalConfiguration) {
// TODO: save on db
//var path = 'scripts/model/globalConfiguration.json';
//return $http.post(path, globalConfiguration).then(function (response) {
// alert('global configuration was saved succesfully!');
//});
}
});
You can inject $q service and use $q.when to wrap the object while returning, so that way you are always returning a promise from your api (and just removed the redundant else). Also remember to reject the promise from catch callback of the promise (if required).
'use strict';
angular.module('test').service('cachingGlobalConfigurationService', function (localStorageService, globalConfigurationService, $q) {
this.getGlobalConfiguration = function () {
var cachedGlobalConfiguration = localStorageService.get('globalConfiguration');
if (cachedGlobalConfiguration) {
//Return a promise
return $q.when(cachedGlobalConfiguration);
}
return globalConfigurationService.getGlobalConfiguration().then(
function (globalConfiguration) {
localStorageService.set('globalConfiguration', globalConfiguration);
return globalConfiguration;
},
function (error) {
console.log('error', error);
return $q.reject(error); //<-- reject
});
};
//....
});
$q.when - Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.
I got a service in Angular:
App.factory('AuthService', function ($q, $http, CredentialStorage) {
var _endpoint = 'http://localhost:3000';
return {
login: function (credentials) {
return $http
.post('/api/users/login', credentials)
.success(function (apiResult) {
var deferred = $q.defer();
if (apiResult.code == 0) {
$scope.user = apiResult.context;
CredentialStorage.store(apiResult.context);
}
return deferred.promise;
})
.fail(function(apiResult, status, headers) {
var deferred = $q.defer();
return deferred.promise;
});
},
....});
Where I authenticate and store the user in some cookie or whatever (this is not relevant).
Then on my controller I got:
App.controller('LoginController', function ($scope, $rootScope, AuthService) {
var _login = function($event) {
$event.preventDefault();
AuthService.login($scope.l).then(function() {
alert('aaa');
if (!AuthService.authenticatedUser) {
$scope.errors = ['Invalid username and password. Please try again.'];
alert($scope.errors);
} else {
alert('a' + $scope.errors);
}
})
}
$scope.login = _login;
});
For some reason my controller's then doesnt execute. Why is that?
Thanks
Well, the simple answer is: you use .then which chains instead of success which just adds a listener:
App.factory('AuthService', function ($q, $http, CredentialStorage) {
var _endpoint = 'http://localhost:3000';
return {
login: function (credentials) {
return $http
.post('/api/users/login', credentials)
.then(function (response) {
var apiResult = response.data;
if (apiResult.code == 0) {
CredentialStorage.store(apiResult.context);
return apiResult.context; // promises let you return the value
}
throw new Error("Authentication failed"); // return $q.reject
// if you don't want to invoke
// exceptionHandler
});
},
....});
Which would let you do:
AuthService.login($scope.l).then(function(result) {
$scope.user = result;
}).catch(function(err){
// code to handle the case authorization failed or the API call failed
});
From Angular separation of concern's stand point, it is generally bad to modify UI scopes in a service/factory/provider. It's better to return the results.