Angular function in service not seen as a function - javascript

I have been trying to move a bunch of code into a service instead of having it sit in the controller because other controllers in my application are going to need some of the same functionality. I have the following controller code:
JBenchApp.controller('CaseListCtrl', ['$scope', '$http', 'HoldState',
function ($scope, $http, HoldState) {
//----------------------------------------------------------
// Load the Calendar Data whent he department changes
//----------------------------------------------------------
$scope.getCalendarOnDeptChange = function () {
// Get the dropdown into e
var e = document.getElementById("deptSelect");
// Set $scope.department to the text of the selected dropdown item --- MUST FIND BETTER ANGULAR METHOD
$scope.department = e.options[e.selectedIndex].text;
console.log($scope.department);
$scope.getCalendar();
};
//----------------------------------------------------------
// Load the calendar data
//----------------------------------------------------------
$scope.getCalendar = function () {
// Retrieve calendar data
HoldState.getCalendar($scope.lastDepartment, $scope.date, $scope.lastLawType, $scope.lastLocationID).then(function (data) {
$scope.cases = data;
$scope.$apply();
});
HoldState.setDepartment($scope.department);
};
//----------------------------------------------------------
// Load the user's default settings
//----------------------------------------------------------
$scope.loadDefaults = function () {
HoldState.getUserDefaults($scope.UserID).then(function (data) {
$scope.UserDefaults = data;
});
$scope.defaultDepartment = $scope.UserDefaults.CourtRoom;
$scope.defaultLawType = $scope.UserDefaults.LitigationCode;
$scope.defaultLocationID = $scope.UserDefaults.LocID;
};
$scope.loadPaths = function () {
HoldState.getTypeOfLaw().then(function (data) {
$scope.lastLawType = data;
});
HoldState.getCourthouse().then(function (data) {
$scope.lastLocationID = data;
});
HoldState.getDepartment().then(function (data) {
$scope.lastDepartment = data;
});
};
$scope.doAuthentication = function () {
$scope.UserID = 'dpeng';
};
$scope.saveSequence = function () {
};
//----------------------------------------------------------
// Initial processing
// Located here so that all functions are defined before
// being called.
// 1. Authenticate the user
// 2. Get the default values
// 3. Load the paths
// 4. Get the list of departments
// 5. Show the calendar.
//----------------------------------------------------------
$scope.doAuthentication();
$scope.loadDefaults();
$scope.loadPaths();
HoldState.getDepartmentList($scope.lastLawType, $scope.lastLocationID).then(function (data) {
$scope.departments = data;
});
$scope.getCalendar();
}]);
I also have the following service code:
var StateService = angular.module('StateService', [])
.service('HoldState', function ($http) {
this.setTypeOfLaw = function (a) { localStorage.setItem('LawType', a) };
this.setCourthouse = function (a) { localStorage.setItem('Building', a) };
this.setDepartment = function (a) { localStorage.setItem('Dept', a) };
this.getTypeOfLaw = function () {
var LT = localStorage.getItem('LawType');
return LT;
};
this.getCourthouse = function () {
var BLDG = localStorage.getItem('Building');
return BLDG;
};
this.getDepartment = function () {
var DEPT = localStorage.getItem('Dept');
return DEPT;
};
this.setStatus = function (a) { localStorage.setItem('Status', a) };
this.getStatus = function () {
var STATUS = localStorage.getItem('Status');
return STATUS;
}
//Begin default settings
this.getUserDefaults = function (UserID) {
var userDefaults = [];
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response;
var status = this.getStatus();
// If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
// the same as the default. We do nothing if status is not 0 because it means we already have path values set
if (status == 0) {
this.setTypeOfLaw(response.LitigationCode);
this.setCourthouse(response.LocID);
this.setDepartment(response.CourtRoom);
}
}, function (response) {
console.log(response.status + " -- " + response.data + " -- " + response.statusText);
});
return userDefaults;
};
When I call $scope.loadDefaults(); I get an error that says:
TypeError: HoldState.getUserDefaults(...).then is not a function
at m.$scope.loadDefaults (http://localhost:54365/js/controllers.js:78:52)
at new <anonymous> (http://localhost:54365/js/controllers.js:121:14)
at Object.e [as invoke] (http://localhost:54365/js/angular.min.js:36:315)
at x.instance (http://localhost:54365/js/angular.min.js:76:79)
at http://localhost:54365/js/angular.min.js:59:85
at q (http://localhost:54365/js/angular.min.js:7:428)
at M (http://localhost:54365/js/angular.min.js:59:69)
at g (http://localhost:54365/js/angular.min.js:51:409)
at http://localhost:54365/js/angular.min.js:51:17
at chrome-extension://ighdmehidhipcmcojjgiloacoafjmpfk/dist/hint.js:2071:22 <div ng-view="" class="view-frame ng-scope">(anonymous function) # angular.min.js:102
angular.min.js:102 TypeError: Cannot read property 'getStatus' of undefined
at controllers.js:311
at angular.min.js:112
at m.$eval (angular.min.js:126)
at m.$digest (angular.min.js:123)
at m.scopePrototype.$digest (chrome-extension://ighdmehidhipcmcojjgiloacoafjmpfk/dist/hint.js:1955)
at m.$apply (angular.min.js:127)
at m.scopePrototype.$apply (chrome-extension://ighdmehidhipcmcojjgiloacoafjmpfk/dist/hint.js:2018)
at l (angular.min.js:81)
at P (angular.min.js:85)
at XMLHttpRequest.H.onload (angular.min.js:86)(anonymous function) # angular.min.js:102
What have I done wrong? I am merely trying to cleanly get back the data from a web service through my Angular service.

getUserDefaults is the only method that really needs to be a promise, as you are making an async call to your api. So, inject $q into your service and then have that method return a promise.
this.getUserDefaults = function (UserID) {
var userDefaults = [], deferred = $q.defer();
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
var status = this.getStatus();
// If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
// the same as the default. We do nothing if status is not 0 because it means we already have path values set
if (status == 0) {
this.setTypeOfLaw(response.LitigationCode);
this.setCourthouse(response.LocID);
this.setDepartment(response.CourtRoom);
}
d.resolve(response);
}, function (response) {
console.log(response.status + " -- " + response.data + " -- " + response.statusText);
});
return deferred.promise;
};
you also should just use the getters as getters, and not try to treat them as promises.
i.e.
$scope.loadPaths = function () {
$scope.lastLawType = HoldState.getTypeOfLaw();
$scope.lastLocationID = HoldState.getCourthouse();
$scope.lastDepartment = HoldState.getDepartment();
};

This should fix the problem
$scope.loadDefaults = function () {
HoldState.getUserDefaults($scope.UserID).then(function (data) {
$scope.UserDefaults = data;
}, function(errData) {
//$scope.UserDefaults isn't going to get filled, so do some error handling here.
});
$scope.defaultDepartment = $scope.UserDefaults.CourtRoom;
$scope.defaultLawType = $scope.UserDefaults.LitigationCode;
$scope.defaultLocationID = $scope.UserDefaults.LocID;
};
And
//Begin default settings
this.getUserDefaults = function (UserID) {
//Here we return the promise
return $http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response;
var status = this.getStatus();
// If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
// the same as the default. We do nothing if status is not 0 because it means we already have path values set
if (status == 0) {
this.setTypeOfLaw(response.LitigationCode);
this.setCourthouse(response.LocID);
this.setDepartment(response.CourtRoom);
}
//Here we fill the data variable
return response;
}, function (response) {
console.log(response.status + " -- " + response.data + " -- " + response.statusText);
});
};

Take another look at the error:
HoldState.getUserDefaults(...).then is not a function
^^^^
HoldState.getUserDefaults() doesn't return a promise, that's the problem.
To be able to consume the service like you are at the moment, tweak the method a little:
this.getUserDefaults = function (UserID) {
return $http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
var status = this.getStatus();
// If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
// the same as the default. We do nothing if status is not 0 because it means we already have path values set
if (status == 0) {
this.setTypeOfLaw(response.LitigationCode);
this.setCourthouse(response.LocID);
this.setDepartment(response.CourtRoom);
}
}, function (response) {
console.log(response.status + " -- " + response.data + " -- " + response.statusText);
});
};
Since promises are chainable, you are essentially returning the promise you get from $http.get(), which will be resolved with response. This should make it work.

Related

Getting undefined data when i called a service

So I have a question that i've been trying to solve for hours.
My problem is that I'm getting the data faster than my services to load.
gen-service
function genEmpId() {
settingsService.getSettings().then(function (data) {
var comId = data.data[0].companyId;
console.log(comId);
var test = comId + ' - ';
return test;
});}
controller
function genId() {
var data = genService.genEmpId();
console.log(data); // getting the data too fast how to put a callback ?
}
So when my controller load its calling the service but im getting an undefined return value.
try this, In your code you not returning anything and another thing is it's async call to you have to wait until it finishes.
// gen-service
function genEmpId() {
return settingsService.getSettings().then(function (data) {
var comId = data.data[0].companyId;
console.log(comId);
var test = comId + ' - ';
return test;
});
}
// controller
function genId() {
var data = genService.genEmpId().then(function (data) {
console.log(data);
})
}

Problems with an angular foreach waiting for http calls

I'm building an ionic project where users can play a tour (which the data is from an API)
Every tour has an amount of parts that users can play at a certain point on the map. This app must be able to be a 100% offline app, so when the user enters the code of a tour, the data must be retrieved from the API before the user can proceed (so the app will put all the data of the tour offline). Every part has an image, video, audio which is getting downloaded at start of the app.
The problem is that the function call, who is downloading all the data, is not synchronous. The console.log's are saying that the function already ends before all data is downloaded. Pieces of code below:
function getAndFillFullTour() {
vm.showLoader = true;
// load data
TourFactory.getFullTour(vm.tourData.number, function(data){
if(data.state == 'success'){
vm.tourData = data;
var test = downloadData(function(){
// hide loader and continue tour
});
} else {
console.log('error');
}
});
}
This function calls the factory who is getting the full tour including paths of images of each part which is needed to get downloaded on the users device. The downloadData function is the following function:
function downloadData(callback) {
angular.forEach(vm.tourData.parts, function(value, key){
var part = value;
var i = key;
if(part.image !== "") {
TourFactory.getPartImage(part, tourId, function(data){
vm.tourData.parts[i].partImage = data;
console.log('executed with picture ' + i);
});
}
});
if(callback)
callback();
}
Unfortunately, the forloop itself is performing synchronous but it is not waiting for the factory call to complete. I tried a lot of alternatives with promises, but without luck. Could anyone help? I need to wait for the http call to be finished in order to get a response from the downloadData call.
the getPartImage() is just an example, there are five functions like this each for loop which need to be completed first before I get a response in the downloadData call.
Take a look at $q.all or here- it is a promise helper function that can wait for multiple promises to complete. It's result is a promise as well, so you can chain it with other promises.
// Promise function that knows how to download a single part
function downloadPart(myurl) {
// return http promise
return $http({
method: 'GET',
url: myurl
});
};
// Aggregat epromise that downloads all parts
function downloadAllParts(parts) {
var defer = $q.defer(); // Setup return promise
var partsPromises = []; // Setup array for indivudual part promises
angular.forEach(parts, function(part) { // Iterate through each part
// Schedule download of a single
partsPromises.push(downloadPart(part));
});
// Wait for all parts to resolve
$q.all(partsPromises)
.then(function(data) {
// Returned data will be an array of results from each individual http promise
resData = [];
angular.forEach(data, function(partData) {
//handle each return part
resData.push(partData.data);
})
defer.resolve(resData); // Notify client we downloaded all parts
}, function error(response) { // Handle possible errors
console.log('Error while downloading parts'
response);
defer.reject('Error while downloading parts');
});
return defer.promise;
};
Then, in your client you can simply wait for the downloadAllParts to complete:
downloadAllParts(myParts)
.then(function(data) {
alert('Success!');
}, function(error) {
alert(error);
})
Since $q.all is a promise as well, you can get rid of defers all together:
// Aggregat epromise that downloads all parts
function downloadAllParts(parts) {
var partsPromises = []; // Setup array for indivudual part promises
angular.forEach(parts, function(part) { // Iterate through each part
// Schedule download of a single
partsPromises.push(downloadPart(part));
});
// Wait for all parts to resolve
return $q.all(partsPromises)
.then(function(data) {
// Returned data will be an array of results from each individual http promise
var resData = [];
angular.forEach(data, function(partData) {
//handle each return part
resData.push(partData.data);
})
return resData;
});
};
Here is a working jsfiddle: link
Thanks all! The following code worked for me. I merged the solutions from the comments with some own stuff, and this solution made it to work for me.
// Aggregat epromise that downloads all parts
function downloadAllParts(parts) {
vm.showLoader = true;
var defer = $q.defer(); // Setup return promise
var partsPromises = []; // Setup array for indivudual part promises
angular.forEach(parts, function(part, key) { // Iterate through each part
// Schedule download of a single
if(typeof part.image !== 'undefined' && part.image !== "") {
partsPromises.push(downloadPartImage(part));
}
if(typeof part.audio !== 'undefined' && part.audio !== "") {
partsPromises.push(downloadPartAudio(part));
}
if(typeof part.video !== 'undefined' && part.video !== "") {
partsPromises.push(downloadPartVideo(part));
}
if(key > 0){
vm.tourData.parts[key].available = false;
} else {
vm.tourData.parts[key].available = true;
}
});
// Wait for all parts to resolve
$q.all(partsPromises)
.then(function(data) {
// Returned data will be an array of results from each individual http promise
resData = [];
angular.forEach(data, function(partData) {
//handle each return part
resData.push(partData);
})
defer.resolve(resData); // Notify client we downloaded all parts
}, function error(response) { // Handle possible errors
console.log('Error while downloading parts' + response);
defer.reject('Error while downloading parts');
});
return defer.promise;
}
function downloadPartImage(part) {
var data = {
oid: tourId,
plid: part.image,
func: 'image'
};
return TourFactory.getSynchronousPartImage(part, tourId).then(function(data){
part.partImage = data.data;
return data;
});
};
function downloadPartAudio(part) {
var targetPath = cordova.file.externalDataDirectory + tourId + '/audio/' + part._id.$id + '.mp3';
var url = "https://www.tourtodo.com/gameinfo/" + part.audio;
var trustHosts = true;
var options = {};
return $cordovaFileTransfer.download(url, targetPath, {}, true).then(function (result) {
console.log('Save file on '+targetPath+' success!');
part.audioSrc = targetPath;
return result;
}, function (error) {
console.log('Error Download file');
console.log(JSON.stringify(error));
return error;
}, function (progress) {
console.log((progress.loaded / progress.total) * 100);
});
}
function downloadPartVideo(part) {
var targetPath = cordova.file.externalDataDirectory + tourId + '/video/' + part._id.$id + '.mp4';
var url = "https://www.tourtodo.com/gameinfo/" + part.video;
var trustHosts = true;
var options = {};
return $cordovaFileTransfer.download(url, targetPath, {}, true).then(function (result) {
console.log('Save file on '+targetPath+' success!');
part.videoSrc = targetPath;
return result;
}, function (error) {
console.log('Error Download file');
console.log(JSON.stringify(error));
return error;
}, function (progress) {
console.log((progress.loaded / progress.total) * 100);
});
}
function getAndFillFullTour() {
vm.showLoader = true;
// load data
TourFactory.getFullTour(vm.tourData.number, function(data){
if(data.state == 'success'){
vm.tourData = data;
downloadAllParts(vm.tourData.parts)
.then(function(data) {
vm.showLoader = false;
vm.showStartButton = true;
alertPopup = $ionicPopup.alert({
title: 'Gelukt!',
template: 'De tour is opgehaald. Druk op start om de tour te starten.'
});
localStorage.setItem('tourdata', JSON.stringify(vm.tourData));
console.log(JSON.parse(localStorage.getItem('tourdata')));
}, function(error) {
console.log('error');
console.log(error);
})
} else {
console.log('error');
}
});
}

Function in angular service returning data late?

I have a service that contains a function to set and return default user preferences. I am experiencing a strange problem that I believe has to do with the async nature of returning the data. I will show you the calls and then explain the problem.
The service is defined as follows:
var StateService = angular.module('StateService', [])
.service('HoldState', function ($http, $q) {
In the app.js I have the following injection:
var JBenchApp = angular.module('JBenchApp', [
'ngRoute',
'StateService'
]);
In the controller I also have the following injection:
JBenchApp.controller('CaseListCtrl', ['$scope', '$http', 'HoldState',
function ($scope, $http, HoldState) {
The following is the function I am calling:
this.getUserDefaults = function (UserID) {
var userDefaults = [], deferred = $q.defer();
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response;
//var status = getStatus();
var status = localStorage.getItem('Status');
// If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
// the same as the default. We do nothing if status is not 0 because it means we already have path values set
if (status == 0 || status == null) {
/**setTypeOfLaw(response.LitigationCode);
setCourthouse(response.LocID);
setDepartment(response.CourtRoom);**/
localStorage.setItem('LawType', response.LitigationCode);
localStorage.setItem('Building', response.LocID);
localStorage.setItem('Dept', response.CourtRoom);
localStorage.setItem('Status', 1);
}
alert("value: " + userDefaults.PreferenceID);
deferred.resolve(response);
}, function (response) {
console.log(response.status + " -- " + response.data + " -- " + response.statusText);
});
return deferred.promise;
};
The following is the code where I am seeing the strange issue:
$scope.fillDefaults = function () {
HoldState.getUserDefaults('dpeng').then(function (data) {
$scope.userDefaults = data;
//$scope.$apply();
});
}
$scope.fillDefaults();
The issue that I am seeing is that the function is called and entered. Processing then returns to the line $scope.userDefaults, but it winds up undefined. It seems that the code goes to through the $scope.fillDefaults function before the data is returned. I suspect this because the alert errors saying that it cannot find property PreferenceID of undefined. I have checked the API call through PostMan and it works just fine. What am I doing wrong here?
fillDefaults is a red herring; the problem is in getUserDefaults: you're assigning http.get's full response object to userDefaults, instead of the response data.
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response;
should be
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response.data;

Angular foreach wants to wait until response for single call

I have thousands of records in my array. I wish to perform some function on each element of my array. Though my functions works fine, but I want to delay the call of next item until first is parsed properly. I don't have relevancy of return either it is true or not. I just wish to delay loop until single item is parsed.
I tried.
$scope.getInfo = function (funcName) {
$http.get(Youtube.getDetails(funcName)).success(function (data, status, headers, config) { // this callback will be called asynchronously when the response is available
if (!angular.isUndefinedOrNull(data.nextPageToken)) {
$scope.form.playlistToken = data.nextPageToken;
} else {
$scope.form.playlistToken = "";
$scope.form.playlistUrl = "";
}
if (data.items.length > 0) {
angular.forEach(data.items, function (value, key) {
var youTubeData = Youtube.parseVideoDetail(value);
if (!angular.isUndefinedOrNull(youTubeData.videoId)) {
Video.find({filter: {where: {videoId: youTubeData.videoId}}}).$promise.then(function (data) {
if (data.length == 0) {
Video.create(youTubeData).$promise.then(function (value, responseHeader) {
$scope.items.splice(0, 0, youTubeData);
youTubeData = {};
// $scope.form.url = "";
toastr.success("Record saved successfully", "Success");
}, function (reason) {
toastr.error("Video can't be saved. Please check.", "Error");
});
} else {
duplicate.push(youTubeData.videoId);
toastr.error("Video already exist with id - " + youTubeData.videoId, "Error");
}
});
}
});
} else {
failed.push(funcName.values.id);
toastr.warning("No details found !!", "Warning");
}
}).error(function (data, status, headers, config) { // called asynchronously if an error occurs or server returns response with an error status.
toastr.error("Youtube API failed to fetch data.", "Error");
});
}
$scope.saveVideo = function () {
// var videoId = Youtube.getYoutubeParser($scope.form.url);
var arrVid = []; // this is actually an array of 2K+ items.
angular.forEach(arrVid, function (value, key) {
var videoId = value.replace("?v=", "");
Youtube.params.videoInfo.values.id = videoId;
$scope.getInfo(Youtube.params.videoInfo);
});
console.log("Failed Videos" + JSON.stringify(failed));
console.log("Duplicate Videos" + JSON.stringify(duplicate));
}
don't use foreach for your scenario. you can utilize $timeout or $interval of angularjs. both of them perform start and stop operations with some interval.

Javascript scope error when accessing from callback

Below is part of code from angularjs service. It may not a specific question to angular though.
The $http.get('/api/test/1').then ( ... returns promise and I like to process the data returned by the call back. I am getting error when accessing filter method.
Test.filter(data.Root);
TypeError: Object #<Object> has no method 'filter'
But, I could access the data variable in the same scope (previous line) though.
var testApp = angular.module('testApp.services', []);
testApp.factory('Test', function ($http, $rootScope) {
var Test = {};
var data = [];
Test.filter = function (d) {
ret = data.filter(function (el) {
return el.Pid == d.Id;
});
return ret;
};
Test.data = function () {
return data[1];
};
Test.start = function () {
Test.asyncData = $http.get('/api/test/1')
.then(function (response) {
data = response;
return Test.filter(data.Root);
}, function (response) {
Test.error = 'Can\'t get data';
data = 'Error: ' + response.data;
return data;
});
};
return Test;
});
I think your error is coming from:
ret = data.filter(...
The data variable, which you set to the response, doesn't have a filter method.
It is probably either not of the type you think it is, or you meant to call the filter method on something else.

Categories

Resources