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;
});
Related
I am trying to implement a controller which has its scope variable being set by service variable like this :
$scope.sidebar= resourceService.sidebar;
The variable sidebar is set by a function called on startup:
var cb = function (api, data) {
for (var key in data) {
var logoArray = data[key];
service.sidebar[key] = logoArray.map(function (logo) {
logo.img = api + "/" + logo.img;
return logo;
});
}
}
service.requestOnStartup = function (api) {
var defer = $q.defer();
$http.get(config.ApiEndpoint.Base + api).success(function (data) {
if (angular.isObject(data)) {
defer.resolve(cb(api, data));
} else {
$log.error("[ResourceService] Unexpected data from resource backend");
defer.reject(data);
}
}).error(function (msg) {
$log.error("Invalid request");
defer.reject(msg);
});
return defer.promise;
};
While the control reaches the scope, the service variable is still not resolved and by the time it is resolved, the control over scope is lost. How do i tackle this problem using promises ?
Your code looks fine if you are displaying $scope.sidebar directly. The variable will be filled asynchronously so it will come after a moment.
If you are doing some action on it while the controller is loading, you'll need to use $watch
$scope.$watch('sidebar', function() {
if ($scope.sidebar) {...} // check for existence here
});
By the way, you can simplify your requestOnStartup like this
service.requestOnStartup = function (api) {
return $http.get(config.ApiEndpoint.Base + api).then(function (data) {
if (angular.isObject(data)) {
return cb(api, data);
} else {
$log.error("[ResourceService] Unexpected data from resource backend");
return $q.reject(data);
}
}, function (msg) {
$log.error("Invalid request");
return $q.reject(msg);
});
};
You can also use the existing $q service of angular js:
service.requestOnStartup = function (api) {
return $q(function(resolve, reject){
$http.get(config.ApiEndpoint.Base + api).then(function (data) {
if (angular.isObject(data)) {
resolve(cb(api, data));
} else {
$log.error("[ResourceService] Unexpected data from resource backend");
reject(data);
}
}, function (msg) {
$log.error("Invalid request");
reject(msg);
});
});
};
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;
}
};
});
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 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.