Angularjs wait until - angularjs

I have:
$scope.bounds = {}
And later in my code:
$scope.$on('leafletDirectiveMap.load', function(){
console.log('runs');
Dajaxice.async.hello($scope.populate, {
'west' : $scope.bounds.southWest.lng,
'east': $scope.bounds.northEast.lng,
'north' : $scope.bounds.northEast.lat,
'south': $scope.bounds.southWest.lat,
});
});
The bounds as you can see at the begging they are empty but they are loaded later (some milliseconds) with a javascript library (leaflet angular). However the $scope.$on(...) runs before the bounds have been set so the 'west' : $scope.bounds.southWest.lng, returns an error with an undefined variable.
What I want to do is to wait the bounds (southWest and northEast) to have been set and then run the Dajaxice.async.hello(...).
So I need something like "wait until bounds are set".

You can use $watch for this purpose, something like this:
$scope.$on('leafletDirectiveMap.load', function(){
$scope.$watch( "bounds" , function(n,o){
if(n==o) return;
Dajaxice.async.hello($scope.populate, {
'west' : $scope.bounds.southWest.lng,
'east': $scope.bounds.northEast.lng,
'north' : $scope.bounds.northEast.lat,
'south': $scope.bounds.southWest.lat,
});
},true);
});

If you want to do this every time the bounds change, you should just use a $watch expression:
$scope.$watch('bounds',function(newBounds) {
...
});
If you only want to do it the first time the bounds are set, you should stop watching after you've done your thing:
var stopWatching = $scope.$watch('bounds',function(newBounds) {
if(newBounds.southWest) {
...
stopWatching();
}
});
You can see it in action here: http://plnkr.co/edit/nTKx1uwsAEalc7Zgss2r?p=preview

Related

AngularJS strange behavior with $watch

I use broadcast to check in my controller if a directive change a variable.
Here is how my broadcast is launched (it works) :
$rootScope.$broadcast(EVENTS.RELOAD, scope.values);
And here I handle the broadcast in my controller :
$scope.$on(EVENTS.RELOAD, reloadCurves);
function reloadCurves(e, data){
console.log(exp.curves);
/* console.log('reload asked : ', data); */
exp.plate.experiments.forEach(function (element) {
if (data[element.well] === true){
exp.curves[element.well] = element;
}
});
console.log(exp.curves);
}
And after in another directive I check if my curves variable change with the $watch. This watch work too, when I change manually the variable the watch is triggered.
My problem is strange, in my reloadCurves I have two console.log and they always show the same object, but normally the first console.log have to output en empty array (or an array different of the second console.log) and the second one the result of the foreach.
So angular think the curves variable is not changed and don't throw the watch of my second directive.
What to do ?
EDIT :
I change my reloadCurves to this :
function reloadCurves(e, data){
console.log('reload asked : ', data);
console.log(exp.curves);
// Récuperer toute les experiments cochés (celle en true)
var tab = [];
exp.plate.experiments.forEach(function (element) {
if (data[element.well] === true){
tab[element.well] = element;
}
});
exp.curves = tab;
console.log(exp.curves);
}
So now the first and second console.log are different, but the $watch on the curves doesn't throw
After looking at your code, I think you're assuming that your forEach loop is iterating at least one time or more. As you're saying you are getting both console.log output as same so problem is simple. Your forEach loop is not iterating any time. just put console log inside the forEach loop, you won't get that log. Just check what do you have in exp.plate.experiments and you'll be able to fix it.
Edit
Looks like you're updating model outside angular world. Just wrap it in $scope.$apply(fn)
function reloadCurves(e, data){
console.log('reload asked : ', data);
console.log(exp.curves);
// Récuperer toute les experiments cochés (celle en true)
$scope.$apply(function() {
var tab = [];
exp.plate.experiments.forEach(function (element) {
if (data[element.well] === true){
tab[element.well] = element;
}
});
exp.curves = tab;
});
console.log(exp.curves);
}

angular `$broadcast` issue - how to fix this?

In my app, I am boradcasting a event for certain point, with checking some value. it works fine But the issue is, later on whenever i am trigger the broadcast, still my conditions works, that means my condition is working all times after the trigger happend.
here is my code :
scope.$watch('ctrl.data.deviceCity', function(newcity, oldcity) {
if (!newcity) {
scope.preloadMsg = false;
return;
}
scope.$on('cfpLoadingBar:started', function() {
$timeout(function() {
if (newcity && newcity.originalObject.stateId) { //the condition not works after the first time means alwasy appends the text
console.log('each time');
$('#loading-bar-spinner').find('.spinner-icon span')
.text('Finding install sites...');
}
}, 100);
});
});
you can deregister the watcher by storing its reference in a variable and then calling it:
var myWatch = scope.$watch('ctrl.data.deviceCity', function(){
if( someCondition === true ){
myWatch(); //deregister the watcher by calling its reference
}
});
if you want to switch logic, just set some variable somewhere that dictates the control flow of the method:
var myWatch = scope.$watch('ctrl.data.deviceCity', function(){
scope.calledOnce = false;
if(!scope.calledOnce){
//... run this the first time
scope.calledOnce = true;
}
else {
// run this the second time (and every other time if you do not deregister this watch or change the variable)
// if you don't need this $watch anymore afterwards, just deregister it like so:
myWatch();
}
})

AngularJS handle stacked promisses

I have an array of arrays that I want to run a async. function on.
for(var a in As){
this.doSomething(a).then(function(result){
for(var b in Bs){
this.somthingElse(b).then(function(){console.log(result)});
}
})
}
How can I force this to be processed in synchronously? I found Array.prototype.map and Array.prototype.reduce, but they seem not to be available in AngularJS?
Firstly, while AngularJS does not include a angular.map or angular.reduce, Array.prototype.map and Array.prototype.reduce is largely unrelated to AngularJS.
Any modern browser would support the map and reduce array functions. angular.forEach is for compatibility purposes (for IE8 and earlier). If you want to use map/reduce and supporting IE8 or earlier, try underscore.js or lodash.
Back to your question, there is not enough information on what you want to achieve and what actually the variables As and Bs are.
Let's make the following assumptions:
As and Bs are both proper JavaScript Arrays
For every memeber of As, you want to doSomething with it, wait until it's done, then do somthingElse with every member from Bs, when that's done, log the result of doSomething
This means you'll run somthingElse As.length * Bs.length times.
You do not care which As member has to be done first
This means As members would concurrently be done together
Similarly, for every Bs iteration, you do not care who is done first
Below is the code, you will need Angular's $q service:
// We cannot bring 'this' into map function's scope, so assign it to 'self'
var self = this;
var promises = As.map(function(a){
return self.doSomething(a).then(function(resultOfA){
return $q.all(Bs.map(function(b){
return self.somthingElse(b).then(function(resultOfB){
console.log(resultOfA);
return resultOfB; // Not neccessary
});
}))
});
});
// Now if you want to do something after all those iterations, do it here:
$q.all(promises).then(function(){
// do something after all 'As.length * Bs.length' iterations are done
});
You will see in the console log random sequences of resultOfA, implying they are done asynchronously. If you want get the actual results for use later, you can do this instead:
// We cannot bring 'this' into map function's scope, so assign it to 'self'
var self = this;
var promises = As.map(function(a){
return self.doSomething(a).then(function(resultOfA){
return $q.all(Bs.map(self.somthingElse)).then(function(resultsOfB){
return {
resultOfA: resultOfA,
resultsOfB: resultsOfB
};
});
})
});
// Now if you want to do something after all those iterations, do it here:
$q.all(promises).then(function(resultsOfA){
// do something after all 'As.length * Bs.length' iterations are done
console.log(resultsOfA);
// Output would be something like:
// [{
// resultOfA: 'Result of A[0]',
// resultsOfB: ['Result of B[0]', 'Result of B[1]', 'Result of B[2]...']
// }, {
// resultOfA: 'Result of A[1]',
// resultsOfB: ['Result of B[0]', 'Result of B[1]', 'Result of B[2]...']
// }]
});
you can create an array of promises and then use $q.all() to resolve all promises when they are all completed.
var promises = [];
for(var a in As){
promises.push(this.doSomething(a));
}
$q.all(promises).then(function(result){
for(var b in Bs){
this.somthingElse(b).then(function(){console.log(result)});
}
})
I think you're looking for a way to run everything synchronously - so if you have the following:
var As = ['A1', 'A2', 'A3'];
var Bs = ['B1', 'B2', 'B3'];
You'd want the execution to be 'A1', 'B1', 'B2', 'B3', 'A2', 'B1', ...
Is that correct?
If so, the following could be used:
function start(){
$log.log('start');
As.reduce(function(promise, itemA){
return promise.then(function(){
return doSomething(itemA).then(startBs);
});
}, $q.resolve());
}
function startBs(){
$log.log('start Bs');
return Bs.reduce(function(promise, itemB){
return promise.then(function(){
return somethingElse(itemB);
});
}, $q.resolve());
}
Sample plunker: https://plnkr.co/edit/fgiI3J2ylcW4FUXuXjIP?p=preview
if you are ok using bluebird, you can do something like this:
function doSomething(v){
return new Promise(function(fullfil, reject){
setTimeout(function(){
console.log(v);
fullfil(v);
},500);
});
};
var As = [[1,2],[3,4],[5,6]];
(function start(){
return Promise.map(As, function(a) {
return doSomething(a).then(function(){
return Promise.map(a, function(b) {
return doSomething(b);
});
});
});
})();

AngularJS: what is the execution order of $watch?

I have a watch function:
scope.$watch('target', function(){
scope.hasUnsavedChanges = true;
}, true);
Then I change some value:
functiion change(target){
scope.hasUnsavedChanges = false;
scope.target.Xxx = '';
console.log('scope.hasUnsavedChanges = ' + scope.hasUnsavedChanges);
}
It outputs false. So when the watch function executes?
And how to run some code after scope.hasUnsavedChanges becomes true in the above code?
$watch functions executes after every time $scope.$digests() function is called.
And if you want to execute code after a variable like hasUnsavedChanges become true. Wouldn't it make more sense to make it a function instead and execute all code there like:
scope.setUnsavedChanges = function(){
scope.hasUnsavedChanges = true;
//the rest of your code goes here
}
scope.$watch('target', function(){
scope.setUnsavedChanges();
}, true);

How to pass collection inside jeditable function?

I want to edit my collection using jeditable, where modifyCollection is a function associated with the event dblclick. I have the following code:
initialize : function(options) {
view.__super__.initialize.apply(this, arguments);
this.collection = this.options.collection;
this.render();
},
render : function() {
var template = _.template(tpl, {
collectionForTemplate : this.collection ,
});
this.el.html(template);
return this;
},
modifyCollection : function (event){
$('#name').editable(function(value, settings) {
return (value);
}
,
{ onblur: function(value) {
this.modelID=event.target.nameID;
this.collection = this.options.collection;
console.log("This Collection is: " + this.collection); //Shows : undefined
//
this.reset(value);
$(this).html(value);
return (value);
}
});
The idee is to update the model and subsequently, the collection by means of jeditable. The in place editing works fine, but the problem is, I am not able to pass the collection into the function. I want to save all the changes to my collection locally and send them to the server at a later time. What am I doing wrong here?
Moved the comment to a formal answer in case other people find this thread.
The this inside your onblur() function is not pointing to this collection. Try adding var self = this; inside your modifyCollection() function then in your onblur() change this.collection to self.collection like so:
modifyCollection : function (event) {
var self = this; // Added this line
// When working with functions within functions, we need
// to be careful of what this actually points to.
$('#name').editable(function(value, settings) {
return (value);
}, {
onblur: function(value) {
// Since modelID and collection are part of the larger Backbone object,
// we refer to it through the self var we initialized.
self.modelID = event.target.nameID;
self.collection = self.options.collection;
// Self, declared outside of the function refers to the collection
console.log("This Collection is: " + self.collection);
self.reset(value);
// NOTICE: here we use this instead of self...
$(this).html(value); // this correctly refers to the jQuery element $('#name')
return (value);
}
});
});
UPDATE - Foreboding Note on self
#muistooshort makes a good mention that self is actually a property of window so if you don't declare the var self = this; in your code, you'll be referring to a window obj. Can be aggravating if you're not sure why self seems to exist but doesn't seem to work.
Common use of this kind of coding tends to favor using that or _this instead of self. You have been warned. ;-)

Resources