How to inject $rootScope into Factory Function in AngularJS - angularjs

I've been given some AngularJS code that has a factory function that is called from a Directive that Posts a file to Web API. I'm having trouble passing the key that is returned to my controller. Can someone tell me how I can do this?
Here is my factory function that posts my file:
return fileManagerClient.save(formData)
.$promise
.then(function(result) {
if (result && result.files) {
result.files.forEach(function(file) {
if (!fileExists(file.name)) {
service.files.push(file);
}
});
}
appInfo.setInfo({
message: "files uploaded successfully"
});
return result.$promise;
},
function(result) {
appInfo.setInfo({
message: "something went wrong: " + result.data.message
});
return $q.reject(result);
})['finally'](
function() {
appInfo.setInfo({
busy: false
});
service.status.uploading = false;
});
}
and I'm trying to get result.value (which is the key of the posted file) passed into my controller but I'm not sure how to do it.
Any help is appreciated.
Thanks,
Pete

If what you say is true, then you just need to have the then callback return result.value:
.then(function(result) {
if (result && result.files) {
result.files.forEach(function(file) {
if (!fileExists(file.name)) {
service.files.push(file);
}
});
}
appInfo.setInfo({
message: "files uploaded successfully"
});
return result.value;
}
In your controller:
myFactory.myFactoryFunction(formData)
.then(function (key) {
console.log(key);
});

Related

Getting Cannot read property 'then' of undefined when using .fromFnPromise in angular-datatables

//the controller that creates the datatable
app.controller('AdminListCtrl', function ($scope, $compile, DTOptionsBuilder, DTColumnBuilder, adminService) {
var vm = this;
function stateChange(iColumn, bVisible) {
console.log('The column', iColumn, ' has changed its status to', bVisible);
}
//vm.dtOptions = DTOptionsBuilder.fromSource('http://localhost/api-v1/admin')
vm.dtOptions = DTOptionsBuilder.fromFnPromise(function() {
return adminService.loadAdmin();
})
.withPaginationType('full_numbers')
.withOption('createdRow', createdRow)
// Add Bootstrap compatibility
.withBootstrap()
// Active ColVis plugin
.withColVis()
// Add a state change function
.withColVisStateChange(stateChange)
// Exclude the last column from the list
.withColVisOption('aiExclude', [2])
// Add Table tools compatibility
.withTableTools('scripts/vendor/datatables/TableTools/swf/copy_csv_xls_pdf.swf')
.withTableToolsButtons([
'copy',
'print', {
'sExtends': 'collection',
'sButtonText': 'Save',
'aButtons': ['csv', 'xls', 'pdf']
}
]);
//adminService to request for all administrators
app.factory('adminService', ['ApiService', function (ApiService) {
return {
loadAdmin: function () {
ApiService.get("admin").then(function (response) {
if (response) {
if (response.success === true) {
return response;
}else{
console.log(response);
}
}else {
console.log('error request ');
}
});
}
};
}]);
//apiservice to interact with api
app.factory('ApiService', function ($http, $q, $localStorage) {
return {
get: function (apiresource) {
var returnData = $q.defer();
$http({
url: api + apiresource,
method: 'GET',
headers: {"Auth-Token": $localStorage.user_data.auth_token}
})
.success(function (data) {
returnData.resolve(data);
})
.error(function (error) {
returnData.resolve();
});
return returnData.promise;
}};
});`enter code here`
When ever I am in that view it throws this errorCannot read property 'then' of undefined. I am following examples from these two sources
http://www.revillweb.com/angularjs-by-example/4-sharing-data-with-angularjs-services/
http://l-lin.github.io/angular-datatables/#/withPromise
You need to return promise object (result of ApiService.get("admin") call) from loadAdmin method.
Also make sure you don't "swallow" rejections inside of the then (in console.log branches) - what happens when you unintentionally handle errors by not passing it further. For this return rejected promise or simply throw error, so that rejection will propagate further down the promise chain:
app.factory('adminService', ['ApiService', function (ApiService) {
return {
loadAdmin: function () {
return ApiService.get("admin").then(function (response) {
if (response) {
if (response.success === true) {
return response;
} else{
console.log(response);
throw response;
// or custom error object: throw {message: 'Error loadAdmin', response}
}
} else {
console.log('error request ');
throw new Error('error request');
}
});
}
};
}]);

Prevent a state change in ui-router based on HTTP query

How do I prevent a state change for a specific "to" state in ui-router (is it using onEnter?) assuming I have this route:
.state('auth.confirm', {
url: '/confirm/:resetToken',
template: '<confirm-page></confirm-page>',
data: { pageTitle: 'Confirm Reset', specialClass: 'gray-bg' }
})
and this service with this promise-based function:
validateResetToken: function(resetToken) {
var self = this;
var deferred = $q.defer();
$http.post(AppConstants.hostRootUrl + '/auth/reset/validate', { resetToken: resetToken })
.then(function(response) {
if(response.data && response.data.success) {
// if we got a 200 return and it indicates success in the response, resolve
self.message = 'Success';
deferred.resolve(self.message);
}
else if (response.data && !response.data.success && response.data.error) {
// if we got a 200 return, but success is falsey and there's an error message, reject with that message
self.message = response.data.error;
deferred.reject(self.message);
}
else {
// error with generic message
self.message = 'Unknown response. Contact administrator.';
deferred.reject(self.message);
}
}, function(errPost) {
if (errPost.data && errPost.data.error) {
self.message = errPost.data.error;
deferred.reject(self.message);
}
else {
self.message = 'Could not connect.';
deferred.reject(self.message);
}
});
return deferred.promise;
},
For posterity (and Googlers) sake, Alon Eitan made me take a second look at my resolve approach, and I realized that my addition of the catch() was causing the rejected promise to not percolate up. This final code works:
.state('auth.confirm', {
url: '/confirm/:resetToken',
template: '<confirm-page></confirm-page>',
data: { pageTitle: 'Confirm Reset', specialClass: 'gray-bg' },
resolve: {
validated: function($q, $stateParams, AuthService, toastr) {
//$log.log('auth.confirm resolve $stateParams',$stateParams);
return AuthService.validateResetToken($stateParams.resetToken).catch(function(validateErr) {
toastr.error(validateErr, 'Blocked', {closeButton: true});
return $q.reject(validateErr);
});
}
}
})
You can create a rule as in https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-create-rules-to-prevent-access-to-a-state
Adapt their example:
app.config(function($stateProvider) {
$stateProvider.state('privatePage', {
data: {
rule: function(token) {
return validateResetToken(token)
}
});
});
app.run(function($rootScope, $state, $stateParams) {
$rootScope.$on('$stateChangeStart', function(e, to) {
if (!angular.isFunction(to.data.rule)) return;
var result = to.data.rule($stateParams.resetToken);
if (result && result.to) {
e.preventDefault();
// Optionally set option.notify to false if you don't want
// to retrigger another $stateChangeStart event
$state.go(result.to, result.params, {notify: false});
}
});
});

Why do I get 'Unable to get property 'then' of undefined or null reference'?

I wrote an angular service:
app.factory('newService', function($http, $q) {
return {
getCustomer: function(id) {
var deferred = $q.defer();
$http.get('/api/customers?id=' + id).success(function(data) {
deferred.resolve({
id: 1,
name: 'Bert'
});
}).error(function(err, msg) {
deferred.reject(msg);
$log(err);
});
return deferred.promise;
}
};
});
My test looks like this:
it('should call service', function() {
$scope.test2();
expect(myMock.getCustomer).toHaveBeenCalledWith(2);
});
However I am getting an error:
TypeError: Unable to get property 'then' of undefined or null reference
plunkr:http://plnkr.co/edit/PHIklcth6uqyYJFcaoLU?p=preview
You're using the defer unnecessarily. When you return it, it hasn't resolved yet because that gets set in the http promise. Change your getCustomer to
getCustomer: function(id) {
return $http.get('/api/customers?id=' + id).success(function(data) {
return {
id: 1,
name: 'Bert'
};
}).error(function(err, msg) {
$log(err);
return msg;
});
}
You have created a mock newService with a method getCustomer but this is just a fake method. It does not have the then()function so an error is throw when JS tries to find it in your call to $scope.test2().
You will have to mock this function too.
Also, you should refactor your service as your use of $q is redundant since $http.get() already returns a promise.
app.factory('newService', function($http) {
return {
getCustomer: function(id) {
return $http
.get('/api/customers?id=' + id)
.then(function(data){
return {
id: 1,
name: 'Bert'
};
})
.catch(function(err) {
$log(err);
});
}
};
});

GET request body parameters undefined

I'm trying to retrieve the parameters of a GET request as follows but anything I try logs out as 'undefined':
GET
// Find a list of Players
$scope.find = function() {
$scope.players = Players.query({limit: 50});
};
Middleware
//Players service
angular.module('players').factory('Players', ['$resource',
function($resource) {
return $resource('players/:playerId', { playerId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
End Point
exports.list = function(req, res) {
Player.find().sort('-created').limit(req.body.limit).populate('user', 'displayName').exec(function(err, players) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(players);
}
});
};
A good discussion on GET with message body - Is this statement correct? HTTP GET method always has no message body
In short, the behavior of servers when using message body with GET would probably not be consistent.
As my request is a GET I need to attain the parameters (within the query string) using .query:
Player.find().sort('-created').limit(req.query.limit).populate('user', 'displayName').exec(function(err, players) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(players);
}
});
};
This answer was greatly helpful More info

Not able to call service from angularJS controller

I've defined 1 service to fetch the category and controller to call the service.
var req = {
method: 'POST',
url: 'http://localhost/cgi-bin/superCategory.pl',
headers: { 'Content-Type': undefined },
data: { action: 'GET' }
};
sampleApp.factory('SuperCategoryService', ['$http', function ($http){
return {
GetSuperCategories: function () {
return $http(req)
.then(
function(response)
{
if (typeof response.data === 'object')
{
alert ('right');
//return response.data;
}
else
{
alert ('wrong');
// invalid response
//return $q.reject(response.data);
}
},
function(response) {
alert ('again worng');
// something went wrong
//return $q.reject(response.data);
});
}
};
}]);
sampleApp.controller('SuperCategoryController', ['$scope', 'SuperCategoryService', function ($scope, SuperCategoryService){
$scope.SuperCategories = function() {
SuperCategoryService.GetSuperCategories()
.then(function(d) {
alert (d);
if (d != undefined) {
alert ('in');
// Your data should be loaded here
console.log(d.data);
alert (d);
$scope.SuperCategories = d.data;
}
else
{
alert ('Here');
}
})
.error(function(data, status) {
// Errors here
});
}
}]);
Even though i am calling the service in controller, but its not hitting to any of the alert in service. (Not even error case)
What can be the problem? Any help will be highly appreciated.
See this $http documentation.Here you did a mistake in calling method of $http.
sampleApp.factory('SuperCategoryService', ['$http', function ($http){
return {
GetSuperCategories: function () {
return $http.post('http://localhost/cgi-bin/superCategory.pl',{ action: 'GET' });
};
}]);
Please see demo here http://jsbin.com/tahigerube/1/edit
you need to call your $scope.SuperCategories to execute it
...
$scope.SuperCategories();
...

Resources