How to achieve Recursion in angularJS - angularjs

if(url.toUpperCase().indexOf("SKILL") != -1) {
$timeout(function () {
$("#ABC").click();
}, 500)
}
I am using $timeout to click the skill button and load the grid on U-I . Above code is not loading the U-I, but if i increase the time from 500 to 2500 then code is working properly, but I don't want to increase the timeout. Is it possible to use recursion so my code would work without increasing timeout?

Set this condition inside document ready. Maybe javascript don't found the elements, because them must be loaded first.
$(document).ready(function(){
//here
});

Related

Protractor waiting for element to be in DOM

I have been having some trouble using Protractor. I have a really weird ui-router state where its hard to go off of other elements to start working with the page. Is there any way to tell protractor to wait until an element finally appears in the DOM? Not visible/displayed, but actually created? I keep trying to use wait for the element but it is clearly not available to be selected.
browser.driver.wait(function () {
return elem.isDisplayed();
});
You should be able to use browser.wait together with the presenceOf ExpectedCondition:
var until = protractor.ExpectedConditions;
browser.wait(until.presenceOf(elem), 5000, 'Element taking too long to appear in the DOM');
Protractor has included ExpectedCondition for explicit wait which lets you wait for the element for certain period of time. You should be able to do the following:
var EC = protractor.ExpectedConditions;
browser.driver.wait(function () {
browser.wait(EC.visibilityOf(elem), 10000);
return elem;
});
the first parameter of browser.wait is a function, if we need to wait until an element is present irrespective of time, then we can use the below code, If you need to restrict the wait to a particular time please give time as second parameter of 'browser.wait'
browser.wait(function() {
return element(by.css("#edudrop1")).isPresent()});

Find duration of a REST call in Angular.js

I want to find out the time taken by a REST service to send back the promise object. If a REST service takes more than lets say x seconds, I need to show user a spinner and once the promise object is obtained the normal flow should proceed.
Any ideas?
Thanks
Recording the time of the request seems unnecessary.
Why not just always setup a timeout that will trigger the spinner after x seconds.
In the success callback of the promise you can just destroy the timeout object preventing it from triggering the spinner if it's before x seconds. Then remove the spinner if it exists.
var duration = 1000 * 1; //1 sec
var timeout = setTimeout(releaseTheSpinner, duration);
var releaseTheSpinner = function() {
//Make spinner
}
Something.update(data).
success(function {
clearTimeout(timeout);
//kill spinner
})
Using setTimeout should suffice. For example:
$scope.useHttp = function() {
$http.get('path/to/stuff')
.success(function(data) {
hideSpinner();
//do stuff with data
});
setTimeout(showSpinner,1000); //will show the spinner after a second (1000 milliseconds).
};
Have a look at the ngProgress directive or the angular loading bar directive, which place a progress at the top of the page. This creates a general, uniform method of displaying progress. Even if the service responds quickly (which is when you don't want to show a progress), the bar moves very quickly. This isn't a direct answer to your question, but a suggested alternative to added complexity around timing and showing a spinner.

How to handle Spinners using Protractor

In my AngularJS application, for any page to load, there are two things which are loading
First the content of the page and secondly some back-end resources.
While back-end resources are loading, a spinner comes in the front and user is not able to do anything on the page contents.
Now while I am writing the automation test suites of the application using Protractor, I am not able to find a technique, to wait for the spinner to disappear from the screen before starting the test.
Please help me in this.
IsDisplayed as you mentioned in Andres D's comment should be the right one to use for your situation. However, it returns a promise so you cant just use
return !$('.spinner').isDisplayed()
since that will just always return false.
Try the below and see if it works.
browser.wait(function() {
return $('.spinner').isDisplayed().then(function(result){return !result});
}, 20000);
If you are waiting for something to happen you can use browsser.wait()
For example, if the spinner has the class name "spinner"
browser.wait(function() {
// return a boolean here. Wait for spinner to be gone.
return !browser.isElementPresent(by.css(".spinner"));
}, 20000);
The 20000 is the timeout in milliseconds.
Your test will wait until the condition is met.
For those dealing with non-angular apps:
browser.wait(
EC.invisibilityOf(element(by.css(selector))),
5000,
`Timed out waiting for ${selector} to not be visible.`
)
https://www.protractortest.org/#/api?view=ProtractorExpectedConditions.prototype.invisibilityOf

How to catch memory leaks in an Angular application?

I have a webapp written in AngularJS which basically polls an API to two endpoints. So, every minute it polls to see if there is anything new.
I discovered that it has a small memory leak and I've done my best to find it but I'm not able to do it. In the process I've managed to reduce the memory usage of my app, which is great.
Without doing anything else, every poll you can see a spike in the memory usage (that's normal) and then it should drop, but it's always increasing. I've changed the cleaning of the arrays from [] to array.length = 0 and I think I'm sure that references don't persist so it shouldn't be retaining any of this.
I've also tried this: https://github.com/angular/angular.js/issues/1522
But without any luck...
So, this is a comparison between two heaps:
Most of the leak seems to come from (array) which, if I open, are the arrays returned by the parsing of the API call but I'm sure they're not being stored:
This is basically the structure:
poll: function(service) {
var self = this;
log('Polling for %s', service);
this[service].get().then(function(response) {
if (!response) {
return;
}
var interval = response.headers ? (parseInt(response.headers('X-Poll-Interval'), 10) || 60) : 60;
services[service].timeout = setTimeout(function(){
$rootScope.$apply(function(){
self.poll(service);
});
}, interval * 1000);
services[service].lastRead = new Date();
$rootScope.$broadcast('api.'+service, response.data);
});
}
Basically, let's say I have a sellings service so, that would be the value of the service variable.
Then, in the main view:
$scope.$on('api.sellings', function(event, data) {
$scope.sellings.length = 0;
$scope.sellings = data;
});
And the view does have an ngRepeat which renders this as needed. I spent a lot of time trying to figure this out by myself and I couldn't. I know this is a hard issue but, do anyone have any idea on how to track this down?
Edit 1 - Adding Promise showcase:
This is makeRequest which is the function used by the two services:
return $http(options).then(function(response) {
if (response.data.message) {
log('api.error', response.data);
}
if (response.data.message == 'Server Error') {
return $q.reject();
}
if (response.data.message == 'Bad credentials' || response.data.message == 'Maximum number of login attempts exceeded') {
$rootScope.$broadcast('api.unauthorized');
return $q.reject();
}
return response;
}, function(response) {
if (response.status == 401 || response.status == 403) {
$rootScope.$broadcast('api.unauthorized');
}
});
If I comment out the $scope.$on('api.sellings') part, the leakage still exists but drops to 1%.
PS: I'm using latest Angular version to date
Edit 2 - Opening (array) tree in an image
It's everything like that so it's quite useless imho :(
Also, here are 4 heap reports so you can play yourself:
https://www.dropbox.com/s/ys3fxyewgdanw5c/Heap.zip
Edit 3 - In response to #zeroflagL
Editing the directive, didn't have any impact on the leak although the closure part seems to be better since it's not showing jQuery cache things?
The directive now looks like this
var destroy = function(){
if (cls){
stopObserving();
cls.destroy();
cls = null;
}
};
el.on('$destroy', destroy);
scope.$on('$destroy', destroy);
To me, it seems that what's happening is on the (array) part. There is also 3 new heaps in between pollings.
And the answer is cache.
I don't know what it is, but this thing grows. It seems to be related to jQuery. Maybe it's the jQuery element cache. Do you by any chance apply a jQuery plugin on one or more elements after every service call?
Update
The problem is that HTML elements are added, processed with jQuery (e.g. via the popbox plugin), but either never removed at all or not removed with jQuery. To process in this case means stuff like adding event handlers. The entries in the cache object (whatever it is for) do only get removed if jQuery knows that the elements have been removed. That is the elements have to be removed with jQuery.
Update 2
It's not quite clear why these entries in the cache haven't been removed, as angular is supposed to use jQuery, when it's included. But they have been added through the plugin mentioned in the comments and contained event handlers and data. AFAIK Antonio has changed the plugin code to unbind the event handlers and remove the data in the plugin's destroy() method. That eventually removed the memory leak.
The standard browser way to fix memory leaks is to refresh the page. And JavaScript garbage collection is kind of lazy, likely banking on this. And since Angular is typically a SPA, the browser never gets a chance to refresh.
But we have 1 thing to our advantage: Javascript is primarily a top-down hierarchial language. Instead of searching for memory leaks from the bottom up, we may be able to clear them from the top down.
Therefore I came up with this solution, which works, but may or may not be 100% effective depending on your app.
The Home Page
The typical Angular app home page consists of some Controller and ng-view. Like this:
<div ng-controller="MainController as vm">
<div id="main-content-app" ng-view></div>
</div>
The Controller
Then to "refresh" the app in the controller, which would be MainController from the code above, we redundantly call jQuery's .empty() and Angular's .empty() just to make sure that any cross-library references are cleared.
function refreshApp() {
var host = document.getElementById('main-content-app');
if(host) {
var mainDiv = $("#main-content-app");
mainDiv.empty();
angular.element(host).empty();
}
}
and to call the above before routing begins, simulating a page refresh:
$rootScope.$on('$routeChangeStart',
function (event, next, current) {
refreshApp();
}
);
Result
This is kind of a hacky method for "refreshing the browser type behavior", clearing the DOM and hopefully any leaks. Hope it helps.

Showing AngularJS $timeout progress bar

I have a alert message that I want to hide after a while. I use $timeout and it works well. However, I want to add a small horizontal count-down (aka progress) bar at the bottom of this alert box to let the user know that their time is running out.
AngularJS $timeout doesn't seem to have any way to determine how long it has been running. How should I update my progressbar in this case?
I use the $timeout like this
$timeout(function(){
$scope.m.hideAlertMessage = true;
}, 10000)
You should use $interval to run on every second and update the counter scope variable, and when it gets to 10 you can kill the interval and do whatever you wanted to do.
E.g.
$scope.counter = 0;
$interval(function() {
$scope.counter++;
if($scope.counter == 10) {
// Do whatever you wanted
}
}, 1000, 10);
The third argument of $interval is the # of times to run.
Note that by default this is run in an iteration of $scope.$apply() so the changes to $scope.counter will be immediately visible in your view.

Resources