Why do the private variables in a factory not get updated? - angularjs

I have an interval in a factory that calls a function that fetches the latest exchange rates.
There will be some calculations that will need to be updated everytime the exchange rates are updated.
So I was just going to re-calculate the values everytime within $http.get.success.
However, the local variables don't seem to get updated. I created a simple jsfiddle to show you what I mean: http://jsfiddle.net/magician11/37msdsry/2/
app.factory('testFactory', function(){
var number1 = 0, number2 = 0;
var getLatestExchangeRates = function() {
$http.get('https://api.bitcoinaverage.com/ticker/global/IDR/')
.success(function(response) {
blockchainInfoExchangeRates.IDR = response.last;
});
printValues();
};
var printValues = function() {
console.log('number1: ' + number1);
console.log('number2: ' + number2);
};
$interval(getLatestExchangeRates, 60000);
getLatestExchangeRates();
Why doesn't number1 print out the updated value?

After returning from factory method, number1 property holds a value at the time the factory returned.
That is why number1 never get's updated.
To get access to the private variable, you need to return a function, which would capture a reference to original private variables, just like it already works with printValues function:
return {
getValues : function() { return {number1 : number1, number2: number2 }; }
printValues: printValues
};
This is how you can use it now:
console.log($scope.testFactory.getValues().number1);
$scope.currentNumbers = $scope.testFactory.getValues();
...
<p>{{currentNumbers.number1}}</p>

Related

Angular to control printing , no. of times?

I do have a print Service developed using angular js,
Is there any efficient way that I can mention the no.of times.
Currently I can loop through this code and call n number of times
PrintService.printElement("printThisElement");
Print service code
function printElement(elem) {
var printSection = document.getElementById('printSection');
// if there is no printing section, create one
if (!printSection) {
printSection = document.createElement('div');
printSection.id = 'printSection';
document.body.appendChild(printSection);
}
var elemToPrint = document.getElementById(elem);
//clones the element you want to print
var domClone = elemToPrint.cloneNode(true);
printSection.innerHTML = '';
printSection.appendChild(elemToPrint);
$timeout(function(){
window.print();
}, 0);
window.onafterprint = function() {
printSection.innerHTML = '';
}
};
Can I loop thru this ?
$timeout(function(){
window.print();
}, 0);
Not sure of your question but from what I understand, your PrintService can be called from various controllers within your app. And you want to control the number of times this method can be called.
You can use a global variable, and check its value inside the PrintService.printElement("printThisElement"); method. If the value exceeds your limit, return an error.
var myApp = angular.module('myApp', []);
myApp.value('test', 0);
myApp.factory('PrintService ', ['test', function (test) {
this.printElement = function () {
//check 'test' value and then run print code
//test++ if printing
}
}]);
$timeout does not loops the function, $timeout used to execute the code inside it only once after a time out.
Instead use a variable to loop using if condition.
HEre is an example:
var count = 0;
function myFunction() {
count++;
if(count > 5)
{
return; // you can either return or execute any other code.
}
else
{
window.print();
}
}

Angular JS object scope

I have written a simple Angular JS code which greets user depending on the time of the day. It works fine. The code is given below:
var modSvc = angular.module("appName",[]);
modSvc.provider("date",function(){
var greeting;
return {
showGreeting:function(value){
greeting = value;
},
$get:function(){
//it has to return an object which can have variables and functions
return {
showDate:function(){
var date = new Date();
return date.getHours();
},
showGreetingMessage:function(){
//var date = new Date();
return greeting + ", It's "+ this.showDate();
}
}
}
};
});
modSvc.config(function(dateProvider){
var hours = dateProvider.$get().showDate();
if(hours<12)
dateProvider.showGreeting("Good morning!");
else if(hours<17)
dateProvider.showGreeting("Good afternoon!");
else if(hours<22)
dateProvider.showGreeting("Good evening!");
else
dateProvider.showGreeting("Good night!");
});
function serviceController($scope,date){
$scope.greetingMessage = date.showGreetingMessage();
}
modSvc.controller("svcController",serviceController);
If you see showGreetingMessage function it has only one line of code which is:
return greeting + ", It's "+ this.showDate();
However showDate function resides at the same level as that of showGreetingMessage function. That's why this.showDate should not work and it should give error. But it works perfectly fine. How is it so?
It depends on the JavaScript engine that is used but according to the Mozilla reference your "this" actually references the parent object since you are returning an object.
As an object method
When a function is called as a method of an object, its this is set to the object the method is called on.
In the following example, when o.f() is invoked, inside the function this is bound to the o object.
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37

getAttribute return unresolved

I'm just trying to save the attribute of an input value to a variable.
This is the code:
var sliderNumber = element.all(by.model('color.red')).get(1);
var firstNum = sliderNumber.getAttribute('value').then(function(value) {
return value;
});
//Some code that changes the sliderNumber attribute
expect(sliderNumber.getAttribute('value')).toEqual(firstNum + 1);
This gives me an error like this:
Expected '184' to equal Promise::433 ([[PromiseStatus]]: "pending")1.
I've also tried:
var firstNum = function() {
return sliderNumber.getAttribute('value').then(function(value) {
return value;
});
}
That didn't help at all. How do I resolve this promise?
Since .getAttribute() returns a promise, which will invoke your callback asynchronously, you need to put your test logic within the callback:
var sliderNumber = element.all(by.model('color.red')).get(1);
// get the initial value
sliderNumber.getAttribute('value').then(function(value) {
// once we've got the initial value, store it, then proceed with your test
var initialValue = parseInt(value, 10);
//Some code that changes the sliderNumber attribute
expect(sliderNumber.getAttribute('value')).toEqual((initialValue + 1).toString());
});
You can't simply get the return value outside of the callback, because that callback may or may not be called after the rest of your code.

Cannot get iteration count of for loop cycle inside of the service result -> then

i have following method:
var i;
$scope.playAllSelectedSounds = function() {
try {
for( i; i < $scope.selectedSounds.length; i++) {
var fileName = $scope.selectedSounds[i].file;
var volume = $scope.selectedSounds[i].defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
console.log(fileName);
MediaSrv.loadMedia(filePath).then(function(media){
console.log(media);
// !!!!!!!!!!! HERE I CANNOT GET value of the i VARIABLE
$scope.selectedSounds[i].state = 1;
// !!!!!!!!!!! HERE I CANNOT GET value of the i VARIABLE
$scope.selectedSounds[i].mediaInstance = media;
media.play();
media.setVolume(volume);
});
}
} catch(e) {
alert(JSON.stringify(e));
console.log(e);
$scope.showAlert("Error", "Error during the playing item");
}
};
Problem is that inside of the service:
MediaSrv.loadMedia(filePath).then(function(media){
I cannot get number o for cycle loop which i need to set in:
$scope.selectedSounds[i].state = 1;
Variable i is global i still cannot reach them. How can i solve it please?
It is not because i is not accessible, it is because i has run out of its limit because loadMedia is async and the value of i within the callback would become $scope.selectedSounds.length, since the for loop would have run out before the callback is invoked.
You could resolve this by using a closure variable representing the current item: You could just make use angular.forEach itself, and you don't event need to worry about accessing the right index. Instead just modify the object itself which is available as 1st argument of forEach evaluator function.
angular.forEach($scope.selectedSounds, function loadMedia(selectedSound, idx){
var fileName = selectedSound.file;
var volume = selectedSound.defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
MediaSrv.loadMedia(filePath).then(function(media){
selectedSound.state = 1;
selectedSound.mediaInstance = media;
media.play();
media.setVolume(volume);
});
});
Also you forgot to initialize i in your case, which will cause your loop to not run at all.
Does it work if you create a local variable var that=i; just before your call to the promise, and then try to get "that" inside the promise return?
Otherwise try this :
for (var i in superarray){
(function(j) {
MyService.get(superarray[j].externalID).then(function(r) {
console.debug(j);
});
})(i);
}

How to avoid Infinite $digest Loop error while working with ngResource

I am $watching the $scope.randomObjects in my directive. The $watch will throw an error if the function is not stable and I think that my curried function inside getDesiredAmountOfObjects is idempotent. Is here something going on with ngResource -objects that I can not see?
How can I fix this? Also the desiredAmount is fixed for now.
Here is snippet from my controller:
..
var getDesiredAmountOfObjects = function (objects, randomObjects) {
return function (desiredAmount) {
var amount = desiredAmount || 1;
if (amount >= objects.length) {
return objects;
}
var randoms = randomObjects.slice(0, amount);
//logged objects are always the same in each $digest loop.
//including the $$hashKey
console.log(random);
return randoms;
};
};
//this will initialized only once in controller
ObjectRes.query(function(data) {
$scope.objects = data;
var randomObjects = [];
angular.extend(randomObjects, data);
randomObjects.sort(function () {
return 0.5 - Math.random();
});
$scope.randomObjects = getDesiredAmountOfObjects($scope.objects, randomObjects);
});
..
I can't see everything because I haven't seen how the $watch and views are set up, but something jumps out at me.
If randomObjects is being called from within a watcher, and set to a value on the scope which is also watched, it will cause an infinite $digest cycle.
This is because you are constantly returning a new array reference (slice returns a new array). The watcher of the result will recognize that the reference changed, which will require a new loop of the digest, which will ask for new randomObjects which will trigger that something changed, etc.
Usually, you fix this by making sure your function returns references to the same object, and does not create new arrays. In your case, you are calling slice which creates a new array every time. You need to make sure this function returns the same array every time.
Something like this, perhaps?
var getDesiredAmountOfObjects = function (objects, randomObjects) {
var result = [];
return function (desiredAmount) {
var amount = desiredAmount || 1;
if (amount >= objects.length) {
return objects;
}
// clear the result array and put randoms into it
result.length = 0;
result.push.apply(result, randomObjects.slice(0, amount));
//logged objects are always the same in each $digest loop.
//including the $$hashKey
console.log(result);
return result;
};
};

Resources