Hi i want to remove all running $interval in Angular. in my page there are many $interval and on button click i want to remove all interval.How will i do it .
Any help is appreciated.
According to the documentation $interval returns a promise, and the interval can be cancelled using $interval.cancel(promise).
I can't see any methods for cancelling all intervals, but if you keep the returned promises in an array you can iterate over that to cancel them all.
var intervals = []
intervals.push($interval(function() { /*doStuff*/ }, /*timeout*/));
intervals.push($interval(function() { /*doDifferentStuff*/ }, /*timeout*/));
...
angular.forEach(intervals, function(interval) {
$interval.cancel(interval);
});
intervals.length = 0; //clear the array
If your intervals are spread over different controllers, use a service to keep track of them.
A more performant approach :
var intervals = [];
intervals.push($interval(function() { /*doStuff*/ }, /*timeout*/));
intervals.push($interval(function() { /*doDifferentStuff*/ }, /*timeout*/));
//doing some stuff
while(intervals.length){
$interval.cancel(intervals.pop());
}
Related
I'm manipulating some angular services/functions via Chrome console. (I have to specifically do this for a task I'm working on).
What I want to do is wait for the AddBagIfLimitNotReached() function to execute and finish running. And only then access the variable this.option.Quantity.
angular.element(document.querySelector(".quantity-button")).controller()._registeredControls[1].Scope.AddBagIfLimitNotReached = async function(n) {
console.log("tthis", this)
if (this.HasReachedMaximumBaggageAllowance()) {
angular.element(document.querySelector(".quantity-button")).controller()._registeredControls[1].LuggageDrawersService.OpenLuggageLimitReachedDrawer();
return;
}
this.AddBag(n);
console.log("Quantity", this.option.Quantity);
};
With this function, I'm adding a product to my basket. And this.option.Quantity should console.log 1. But it actually consoles.log 0.
However, if I check the object itself, it shows 1.
So I think what is happening, is I'm console.logging my bag quantity, before the bag has actually finished being added to the basket.
For example, if I added a settimeout of 2 seconds, the correct bag value = 1 is console.logged.
angular.element(document.querySelector(".quantity-button")).controller()._registeredControls[1].Scope.AddBagIfLimitNotReached = async function(n) {
console.log("tthis", this)
if (this.HasReachedMaximumBaggageAllowance()) {
angular.element(document.querySelector(".quantity-button")).controller()._registeredControls[1].LuggageDrawersService.OpenLuggageLimitReachedDrawer();
return;
}
this.AddBag(n);
// Returns 1
setTimeout(function(){ console.log("Quantity", this.option.Quantity); }, 2000);
};
Is there a better way I can achieve this, without using settimeout? I have tried async/await/promises, but I still can't seem to find a way to wait for the function to finish loading.
Async/await returns an error - it doesn't like the function this.HasReachedMaximumBaggageAllowance() and throws an error stating this.HasReachedMaximumBaggageAllowance is not a function.
Any tips/ideas would be much appreciated.
I found a solution, I'm using $watch, to watch a key/value, in the this object. And this seems to work:
angular.element(document.querySelector(".quantity-button.plus-button")).controller()._registeredControls[1].Scope.AddBagIfLimitNotReached = function(n) {
let bagCount = this.option.Quantity;
console.log("bagCount", bagCount);
if (this.HasReachedMaximumBaggageAllowance()) {
angular.element(document.querySelector(".quantity-button.plus-button")).controller()._registeredControls[1].LuggageDrawersService.OpenLuggageLimitReachedDrawer();
return;
};
this.AddBag(n);
this.$watch("this.option.Quantity", function (newValue) {
console.log(`Value of foo changed ${newValue}`);
if (newValue > 0) {
document.querySelector(`.luggage-tile-weight-${n.Weight} .tile-title .tick-box`).classList.add("green-tick");
displayGreenTickNoBagSelected();
};
if (newValue === 0) {
document.querySelector(`.luggage-tile-weight-${n.Weight} .tile-title .tick-box`).classList.remove("green-tick");
displayGreenTickNoBagSelected();
};
});
};
I have the following delay task in the below method:
exportDataToCSV: function () {
var me = this,
grid = this.option.up('grid');
var mask = Ext.widget('processingmessagebox', {
target: grid,
progressMessage: 'exporting',
isProcessing: true,
targetFunctions: {
ConfirmCancelClick: me.stopExportingData
}
});
mask.getViewModel().set({ ShowCancelBox: 'block' });
var grdStore = grid.getStore();
var grdProxy = grdStore.getProxy();
exportData = new Ext.util.DelayedTask(function () {
me.printDataToCSV(grid, mask);
});
exportData.delay(2000);
},
stopExportingData: function () {
exportData.cancel();
}
This is working fine. But, I am trying to cancel the "exportdata" process without using delay task. Any suggestions on this?
You kind of can't. Javascript - and thus ExtJS - is single-threaded. If your code is busy doing something, it can't respond to a cancel button.
In order to make a long-running task cancelable - or even just to allow a progress bar to update - it needs to be broken up into a series of steps. These steps need to then be put on the task queue - something that ExtJS's delayed task does for you.
For something like export data to a CSV, a common approach would be to create a task that exports, say, a dozen records, and then puts another task to read the next dozen.
There are certainly other approaches than using DelayedTask in particular, but they all revolve around the underlying setTimeout or setInterval methods.
Here's an example using a regular ExtJS task, and not DelayedTask:
var readNextDozenRecords = function(indexToReadFrom) { ... }
var writeDataChunk = function(recordsToWrite) { ... }
var taskConfig = {
currentIndex: 0,
interval: 10, // runs every 10ms or so.
run: function() {
var nextDataChunk = readNextDozenRecords(currentIndex);
if (nextDataChunk.length == 0) {
this.stop(); // 'this' scope is the running task.
return;
}
currentIndex += nextDataChunk.length;
writeDatakChunk(nextDataChunk);
}
}
exportData = Ext.TaskManager.newTask(taskConfig);
// The exportData can be manually canceled by calling the `stop` method.
How to get the rows that are actually in the "Showing Items"?
$scope.gridApi.core.getVisibleRows seems to be giving inconsistent values.
http://plnkr.co/edit/FRaCNxKhZ242rFyqNDkm?p=preview
gridApi.core.on.filterChanged($scope, function () {
$timeout(function () {
var allvisiblerows = $scope.gridApi.core.getVisibleRows($scope.gridApi.grid);
$scope.visibleRowsCount = allvisiblerows.length;
}, 0);
});
The problem with filterChanged is that it is raised as soon as the filters change but at that time the data has not necessarily been filtered. To fix this, rather than listening to the filterChanged you can listen to rowsRendered event and that will fix the issue.
gridApi.core.on.rowsRendered($scope, function () {
var allvisiblerows = $scope.gridApi.core.getVisibleRows($scope.gridApi.grid);
$scope.visibleRowsCount = allvisiblerows.length;
});
This way you can also get rid of the $timeout.
I am writing an E2E test with protractor. I had to fetch information from the browser and execute a step multiple times.
I am testing one screen which will start when a
User clicks 'Start'
lands on a new page
The workflow below is invoked with count being passed as argument
id the html id does not change. the value changes when queried again after submitting the current form.
for(i = 0 ; i < count ; i++){
console.log("counter is "+i);
element(by('id')).evaluate('value').then(function(v) {
// do some action on UI based on v
element(by('id1')).sendKeys(v+v);
// submit etc.,
// some angular code runs in the frontend.
}
// need to wait since webdriver jumps to the next one without this completing
}
Many blog posts/documentations suggests you cannot use it in a loop, but does not suggest any alternative way to do this.
Any suggestions appreciated.
Never use protractor element statements inside loop: The simple reason is that the webdriverJS (protractor) API is asynchronous. Element statements returns a promise and that promise is in unresolved state while the code below the statements continues to execute. This leads to unpredictable results. Hence, it is advisable to use recursive functions instead of loops.
source: http://engineering.wingify.com/posts/angularapp-e2e-testing-with-protractor/
Edit: updated question with details of workflow.
It is usually not recommended to use a loop when an iteration has an asynchronous call.
The reason is that the first asynchronous calls is executed after the last iteration of the loop when i is already equal to count.
Thus, it makes it difficult to break the loop and to keep track of the value of i.
On way to tackle the issue is to use a recursive function :
var count = 3;
var results = [];
function iterate(i, n) {
if(i < n) {
console.log(`counter is ${i}`);
browser.refresh();
return element(by.id('h-top-questions')).getText().then(function(text) {
results.push(`${i}:${text}`);
return iterate(i + 1, n);
});
}
}
iterate(0, count).then(function(){
console.log("done!", results);
});
But a better way would be to iterate with promise.map on an array sized to the number of iterations:
var count = 3;
protractor.promise.map(Array(count).fill(0), function(v, i) {
console.log(`counter is ${i}`);
browser.refresh();
return element(by.id('h-top-questions')).getText().then(function(text) {
return `${i}:${text}`;
});
}).then(function(results){
console.log("done!", results);
});
You could also keep using a loop. First you'll have to use the let statement to get the value of i in an asynchronous function (ES6).
Then call all the synchronous code with browser.call to synchronize the execution:
var count = 3;
var results = [];
for(let i = 0 ; i < count ; i++){
browser.call(function(){
console.log(`counter is ${i}`);
browser.refresh();
element(by.id('h-top-questions')).getText().then(function(text) {
results.push(`${i}:${text}`);
});
});
}
browser.call(function() {
console.log("done!", results);
});
Looping in protractor works like this
describe('Describe something', function() {
var testParams = [1,2,3,4,5,6,7,8,9,10];
beforeEach( function() {
// ...
});
for (var i = 0; i < testParams.length; i++) {
(function (testSpec) {
it('should do something', function() {
// inside loop
});
})(testParams[i]);
};
});
Edit : I might be mis-understanding your question, but it seems to me you want to complete all(dynamic count) actions on the page, before going to the next one ?
it('should clear old inspections', function() {
inspectieModuleInspectieFixture.getRemoveInspectionButton().count().then(function (value) {
if(value == 0){
console.log('--- no inspections to remove ---');
}
for(var i = 0; i < value; i++){
//global.waitForClickable(inspectieModuleInspectieFixture.getRemoveInspectionButtonList(i+1));
inspectieModuleInspectieFixture.getRemoveInspectionButtonList(i+1).click();
console.log('iteration '+i + 'count '+value )
};
});
global.wait(5000);
}); */
this counts elements on the page and then it performs an action for the ammount of elements it found
In the above example I use containers to hold my elements, so my code remains readable (i.e. inspectieModuleInspectieFixture.getRemoveInspectionButton() holds $(".elementSelectorExample")
There is also a 'global.waitForClickable' commented, that is reffering to a 'time module' I've created that extends the functionality of 'wait', in this case it waits till the element is vissible/clickable.
This is easily mirrored perhaps something like this :
waitForElementNoDisplay: function(element){
return browser.wait(function() {
return element.isDisplayed().then(function(present) {
return !present;
})
});
},
this will make protractor WAIT untill an element is no longer displayed.(Display:none)
If you need to perform some action on every element, it is true, that better to not use loops. Use .map() or .each() or .filter() instead
Still not quite sure what you what to do, but here is example how i am doing similar tasks, when you need to make number of actions depending on data from the page:
class SomePage {
typeValueForEachElement(elements) {
elements.each((elem, index)=> {
elem.getAttribute('value').then(value=> {
elem.sendKeys(value + value)
elem.submit()
})
})
}
}
new SomePage().typeValueForEachElement($$('your locator here'))
Here is api reference that might help
http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.map
http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.reduce
http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.each
http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.filter
I tried to set up a simple app to show the latest message of each member.
At first, it loads the members array.Then I call a function refreshMsg to loop through the array.
Within the loop, I set an timer on it.
However it did not work at all.Could someone five me an hint?
Many thanks.
demo
//show the message for each member and set an timer to refesh
function refreshMsg(){
//loop through members array and set a timer
for(var i=0;i<$scope.members.length;i++){
var cur_member=$scope.members[i];
var name = cur_member.name;
var url_getMsg = "api/getMsg/".name;
var timer = $timeout( function refresh(){
$http.get(url_getMsg).success(function(data){
cur_member.msg=data;
}
)
timer = $timeout(refresh, 3000);
}, 3000);
}
}
})
One of the reasons it is not working correctly is that due to async nature of the call the cur_member would not match the response message as the loop would have moved ahead. The way to do this would be to create another method specifically for setting the $timeout which would create a closure. Better still use $interval
function refreshMsg(){
//loop through members array and set a timer
for(var i=0;i<$scope.members.length;i++){
var cur_member=$scope.members[i];
var name = cur_member.name;
setupRefresh(curr_member);
}
function setupRefresh(member) {
var url_getMsg = "api/getMsg/"+member.name;
var timer = $interval( function(){
$http.get(url_getMsg).success(function(data){
member.msg=data;
}
);
}, 3000);
}
Also remember to dispose all the interval when scope is destroyed.