Http request in constructor doesn't change attribute value - angularjs

I've got a problem with this constructor:
function ApiManager() {
this.api= new ApiInterface();
this.apiVersion = -1;
this.api.getVersion()
.then(function(version) {
console.log(version); // "1.0"
this.apiVersion = version;
console.log(this.apiVersion); // "1.0"
}, function(error) {
console.log("Couldn't find API version.")
});
}
ApiManager.prototype = {
getApiVersion: function() {
return this.apiVersion; // "-1"
}
};
I have an object with the attribute apiVersion, which is initialized with the value -1. After that an HTTP request is executed by the ApiInterface and assigns apiVersion the value 1.0. Later I call the function getApiVersion and it returns the old value -1.
I'm new to AngularJS and probably it's a silly rookie mistake, but I don't see what I've got wrong.

This is what the Promises/A+ Spec says about the this keyword inside .then functions:
That is, in strict mode this will be undefined inside of them; in sloppy mode, it will be the global object.
-- Promises/A+ Spec -- Note 3.2
So in your code, you need to explicitly bind instead of using the this keyword.
function ApiManager() {
this.api= new ApiInterface();
this.apiVersion = -1;
//bind 'this' keyword to a var
var vm = this;
this.api.getVersion()
.then(function(version) {
console.log(version); // "1.0"
//use that binding
vm.apiVersion = version;
console.log(vm.apiVersion); // "1.0"
}, function(error) {
console.log("Couldn't find API version.")
});
}

I also tried the version with the promise and this is the result:
function ApiManager() {
this.api= new ApiInterface();
this.apiVersion = api.getVersion();
}
ApiManager.prototype = {
getApiVersion: function() {
return this.apiVersion;
}
};
The ApiInterface returns a promise in the getVersion method.
getVersion: function() {
return $http({
method: 'GET',
url: myUrl;
}).then(function successCallback(response) {
return response.data.jsonapi.version;
}, function errorCallback(response) {
return $q.reject(response.data);
});
},
Later on, I use it this way in my controller:
$scope.printApiVersion = function () {
var promise = manager.getApiVersion();
promise.then(function(version) {
console.log(version);
}, function(error) {
console.log("Couldn't retrieve API version.")
});
}
This works great for me, but I don't know if it is considered good style.

Related

Empty var in second service function

I have a service:
.service('VacanciesService', function($http) {
var vacancies = [];
var usedVacancies = [];
return {
getVacanciesForUniversity: function(university_id) {
return $http.get("http://jobs.app/api/vacancies/" + university_id).then(function(response){
vacancies = response.data.vcancies;
return vacancies;
}, function(error){
return error;
});
},
getRandomVacancy: function() {
console.log(vacancies);
}
}
})
This is the calling controller
.controller('jobsCtrl', function($ionicLoading, locker, UniversitiesService, VacanciesService) {
var vm = this;
user = locker.get('userDetails');
UniversitiesService.getUniversity(user.university.id).then(function(university) {
vm.university = university.university;
});
VacanciesService.getVacanciesForUniversity(user.university.id).then(function(vacancies) {
vm.vacancies = vacancies;
}, function error(error) {
});
vm.addCard = function(name) {
newVacancy = VacanciesService.getRandomVacancy();
};
vm.addCard();
})
And I can't figure out why the vacancies variable in in the console.log is empty in the second function? I assumed as it was set in the initial function (called prior) that it should be populated?
TIA!
if you call getVacanciesForUniversity getRandomVacancy like below, you will get empty array
VacanciesService.getVacanciesForUniversity(uniId)
VacanciesService.getRandomVacancy() //you will get empty
you must getRandomVacancy inside getVacanciesForUniversity returned promise
VacanciesService.getVacanciesForUniversity(uniId).then(function(){
VacanciesService.getRandomVacancy()
})
alo you misstype response.data.vcancies; instead response.data.vacancies;
The answer is READ YOUR CODE PROPERLY.
There was a glaringly obvious typo in my code that I missed from staring at it too long.
Big thanks to Daniel Dawes and aseferov for attempting to help!
In your service :
getVacanciesForUniversity: function(university_id) {
return $http.get("http://jobs.app/api/vacancies/" + university_id);
}
In your controller :
$scope.getVacancies = function () {
your_service.getVacanciesForUniversity().then(
function(response) {
if (response.status = 200) {
$scope.vacancies = response.data.vacancies;
}, function errorCallback (response) {
...
}
);
};
$scope.getVacancies(); // you call one time your function here
$scope.getRandomVacancy : function() {
console.log($scope.vacancies);
};

Typeahead search function

So I am using the typeahead directive that's part of Angular UI project. I have a function that calls a factory (using $resource to call an api). The first function didn't work but the second one did. What's happening differently here? I assumed these would produce the exact same result but apparently I am wrong:
// this didn't work, it doesn't display a list of items in typeahead, no errors.
$scope.getLocation = function(val) {
return LocationService.search({ term: val }, function (res) {
return res.data.map(function (item) {
return item;
});
});
};
// this worked
$scope.getLocation = function(val) {
return LocationService.search({ term: val }).$promise.then(function (res){
return res.data.map(function (item) {
return item;
});
});
};
Do you have $resource wrapped in LocationService? Something like:
function LocationService($resource) {
return {
search : function(query){
var locationResource = $resource('url/',
{},
{
search : {
method: 'POST',
responseType : 'json'
}
});
return locationResource.search({ q : query });
}
};
}
If so, in your first example, you're just passing a callback as second variable to LocationService, which isn't handled in the function definition. The returned function of $resource can take a callback as a second parameter, but not if you've wrapped it. If you wanted, you could pass the callback to the service itself, like:
function LocationService($resource) {
return {
search : function(query, cb){
var locationResource = $resource('url/',
{},
{
search : {
method: 'POST',
responseType : 'json'
}
});
return locationResource.search({ q : query }, cb);
}
};
}

Angularjs $http my second then before first then is done.

I am not sure what I am doing wrong here, but the Report.xls gets downloaded before report.students gets updated.
How can I make it wait for report.students to be updated before Report.xls get downloaded?
Here is my code
`data service function
function getStudentsForExcel() {
var filter = studentFilter;
filter.data.perPage = StudentsModel.data.countTotal;
return $http.post(url + "/summeries", filter.data)
.then(onStudentSummeries)
.catch(onError);
function onStudentSummeries(response) {
return response.data;
}
}`
This function in my controller
`
function tocsv() {
studentData.getStudentsForExcel().then(function(data) {
report.students = data;
}).then(function() {
var blob = new Blob([document.getElementById('tableReport').innerHTML], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
});
saveAs(blob, "Report.xls");
});
}`

AngularJS v1.3 breaks translations filter

In Angular v1.2 I was using the following code for serving up localised strings in the application:
var i18n = angular.module('i18n', []);
i18n.service('i18n', function ($http, $timeout) {
/**
A dictionary of translations keyed on culture
*/
this.translations = {},
/**
The current culture
*/
this.currentCulture = null,
/**
Sets the current culture, loading the associated translations file if not already loaded
*/
this.setCurrentCulture = function (culture) {
var self = this;
if (self.translations[culture]) {
$timeout(function () {
self.currentCulture = culture;
});
} else {
$http({ method: 'GET', url: 'i18n/' + culture + '/translations.json?' + Date.now() })
.success(function (data) {
// $timeout is used here to defer the $scope update to the next $digest cycle
$timeout(function () {
self.translations[culture] = data;
self.currentCulture = culture;
});
});
}
};
this.getTranslation = function (key) {
if (this.currentCulture) {
return this.translations[this.currentCulture][key] || key;
} else {
return key;
}
},
// Initialize the default culture
this.setCurrentCulture(config.defaultCulture);
});
i18n.filter('i18n', function (i18n) {
return function (key) {
return i18n.getTranslation(key);
};
});
In the template it is then used as follows:
<p>{{ 'HelloWorld' | i18n }}</p>
For some reason that I can't fathom, upgrading to v1.3 of AngularJS has broken this functionality. Either the $timeout isn't triggering a digest cycle, or the filter isn't updating. I can see that the $timeout code is running, but the filter code never gets hit.
Any ideas why this might be broken in v1.3?
Thanks!
In angular 1.3 the filtering was changed so that they are no longer "stateful". You can see more info in this question: What is stateful filtering in AngularJS?
The end result is that filter will no longer re-evaluate unless the input changes. To fix this you can add the line:
i18n.filter('i18n', function (i18n) {
var filter = function (key) {
return i18n.getTranslation(key);
};
filter.$stateful = true; ///add this line
return filter;
});
Or else implement your filter some other way.

AngularJS - self referencing services?

I'm building an Angular app that will have a top-level Controller and a second-level controller. There will be n number of second-level controllers, but I want to put global-level functions someplace. I'm doing this in a service.
I'm starting down the path of creating a single service that return an api, really, containing lots of functions (below). The service is returning an object with two property branches that each contain a set of functions. How can I call one of these from the other?
globalModule.factory('global', function($http) {
var squares = MyApp.squares; // this is the *only* link from Global namespace to this app
return {
squareMgr: {
getSquaresEarned: function() {
return squares.earned;
},
getSquaresPlaced: function() {
return squares.placed;
},
setThisSquareEarned: function(value) {
squares.earned.push(value);
},
setThisSquarePlaced: function(value) {
squares.placed.push(value);
}
},
missionMgr: {
missionInfo: {},
setMissionInfo: function(missionInfo) {
this.missionInfo = missionInfo
},
complete: function(missionData) {
log('complete called on video at ' + new Date());
missionData.complete = true;
log(angular.toJson(missionData));
$http({
url: '/show/completeMission',
method: "POST",
data: missionData
})
.then(function(response) {
if (response.data.success === true) {
log('completeMission success');
// increment squares earned counter
this.squareMgr.setThisSquareEarned(missionData.id);
// above is an attempt to run a function contained in this
// same service in a different parent property branch.
// how *should* I do this?
}
});
}
}
}
});
How about something like this:
globalModule.factory('global', function($http) {
var glob = {
squareMgr: {
// ...
},
missionMgr: {
foo: function() {
glob.squareMgr.xyz();
}
}
};
return glob;
});

Resources