need help using for loop and getText() in protractor - javascript

browser.findElements(protractor.By.repeater('cat in cats')).then(function(rows) {
for (i = 0; i < rows.length; i++) { //which is always 3
var tmp = element(by.repeater('cat in cats').row(i)).element(by.binding('cat.name')).getText();
tmp.then(function(text) {
console.log('text is : ' + text);
console.log('iteration number is: ' + i);
if (text == 'someText') {
element(by.repeater('cat in cats').row(i)).element(by.binding('cat.action')).click();
}
});
}
In this case the value of 'i' inside the function is always returning 3.
I have get text and then check if the text is what I want and click on an element.
The value of 'i' in the 'if' statement is always returned as 3. Something to do with promises, but I am not sure.
Any help with modified code is much appreciated.
Thanks!

Don't call by.repeater() multiple times; instead, use map() and "chain" the promises like this:
element.all(By.repeater('cat in cats')).map(function(elm) {
return {
text: elm.element(by.binding('cat.name')).getText(),
action: elm.element(by.binding('cat.action'))
};
}).then(function(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].text == 'someText') {
return arr[i].action;
}
}
throw new Error('Text not found');
}).then(function(elm) {
elm.click();
});
All of the credits go to #Andres for the solution provided here:
Passing Protractor ElementFinder to deferred.fulfill() results in a promise containing a null value
Also see:
Protractor find element inside a repeater

Related

Get object out of observable array

Why is m "undefined" in this code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for(var i=0;i<currentViewModel.availableReports().length;i++) {
if(currentViewModel.availableReports()[i].id == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
I call getReport() as an onclick event and I want to send the report object to a view (modal) I can do a foreach on the availableReports and it's all there. When I run through the debugger, it loops through the array and finds the right one. But why can't I pull it out of the array? "m" remains undefined the the function returns undefined.
What am I missing here?
EDIT: there is a follow up question here:
Can knockout.js wait to bind until an onClick?
You just need to change if(currentViewModel.availableReports()[i].id ... to if(currentViewModel.availableReports()[i].id() ... because after mapping id will become an observable, i.e. function.
Updated code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id() == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
Demo - Fiddle.
I'll repeat the solution from #NikolayErmakov's answer here, but want to add two things to get a more complete answer. You end with:
...m remains undefined and the function returns undefined.
What am I missing here?
You're missing two things:
The var m bit of the first statement inside the if is hoisted to the top of the current scope (the top of the function). This is why the debugger can tell you what m is, even if you never reach the line of code it's on.
If a function invocation reaches the end of a function (as is the case for you, since you never go inside the if) without seeing an explicit return statement, it will return undefined.
To better understand this, you should interpret your function like this:
currentViewModel.getReport = function(reportId) {
var m;
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id == reportId) {
m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
return undefined;
}
Some people (e.g. Douglas Crockford) do recommend placing var statements at the top of a function, though it's a matter of style to some degree. I don't think many people explicitly return undefined at the end of a function, though in your case I might be explicit about that scenario and return null (or throw an Error even).
As promised, I'll repeat the actual solution, as I concur with the other answer:
you need to invoke id as a function to get its value (because the mapping plugin will map to observable()s.
In addition:
I'd retrieve the array only once
I'd suggest using === instead of ==
Here's my v0.5 version:
currentViewModel.getReport = function(reportId) {
var m = null, reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
m = reports[i];
return m;
}
}
return m;
}
But I'd optimize it to this v1.0:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
return reports[i];
}
}
return null;
}
For completeness, here's another version that utilizes filter on arrays:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports().filter(function(r) { return r.id() === reportId; });
return reports.length >= 1 ? reports[0] : null;
}

Conditionally traverse an Array

I writing code that should mimic the functionality of Underscores's _.each method. However, with the code I have now...
var testArr = ['a','b','c'];
var eachFunc = function(collection, iterator) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.legnth; i++ ) {
iterator(collection[i]);
}
}
else {
for ( var property in collection ) {
iterator(collection[property]);
}
}
};
console.log(eachFunc(testArr, console.log));
I only return undefined. I would expect to log 'a','b' then 'c' to the console. I have verified that Array.isArray(testArr) is returning true and that the if block is entered into. It's the for loop that I'm not entering into properly.
Can someone please tell me what I'm doing wrong with this for loop?
1) Typo: legnth.
2) You can't pass console.log directly - it results in Illegal invocation error. Pass either console.log.bind(console) or anonymous function.
3) That undefined is returned value of your function.

how to check the presence of the element in the array?

please help solve the problem.
live example is here: https://jsfiddle.net/oqc5Lw73/
i generate several tank objects:
var Tank = function(id) {
this.id = id;
Tank.tanks.push(this);
}
Tank.tanks = [];
for (var i = 0; i < 3; i++) {
new Tank(i);
}
Tank.tanks.forEach(function(tank, i, arr) {
console.log(tank);
});
console.log('summary tanks: ' + Tank.tanks.length);
after i delete tank with random index:
var tankDel = Math.floor(Math.random() * (3));
Tank.tanks.splice(tankDel, 1);
Tank.count -= 1;
Tank.tanks.forEach(function(tank, i, arr) {
console.log(tank);
});
console.log('summary tanks: ' + Tank.tanks.length);
i try check tanks massive. if tanks massive contain tank with property 'id' = 0 then i need display alert('tank with id 0 is dead').
but console output follow error message:
Uncaught SyntaxError: Illegal break statement
break is to break out of a loop like for, while, switch etc which you don't have here, you need to use return to break the execution flow of the current function and return to the caller. See similar post here: illegal use of break statement; javascript
Tank.tanks.forEach(function(tank, i, arr) {
if(tank.id == 0) {
tank0Dead = false;
return;
};
});
if(tank0Dead == true) {
alert('tank with id 0 is dead');
};
jsfiddle : https://jsfiddle.net/oqc5Lw73/6/
You can't quit from forEach using break. Just remove break, and it will work.
P.S: honestly, it is better to refactor that code:)
Your only problem is that you can't use the break; statement in a forEach function.
But you can in a for() loop, so here is the equivalent code with a for :
for (var i = 0; i < Tank.tanks.length; i++){
if (Tank.tanks[i].id == 0){
tank0Dead = false;
break;
}
}
https://jsfiddle.net/oqc5Lw73/5/
But I agree with #dimko1 about the idea of refactoring the code
You can not break a forEach callback, simply because it's a function.
Here's updated working jSfiddle
If you really want to break it, you can use exception like code below.
try {
[1,2,3].forEach(function () {
if(conditionMet) {
throw Error("breaking forEach");
}
});
} catch(e) {
}
Otherwise you can use jQuery's each() method. when it's callback returns false it stops.
jQuery.each([1,2,3], function () {
if(conditionMet) {
return false;
}
});

How do I check if a property exists in an object/dictionary?

I'm iterating over an array of words and trying to stuff them in an object literal so I can assign the value of how many times those words occur to each word in the literal/dictionary. The problem is I need to check to make sure that word hasn't already been added into my literal. I tried using in to check if the property exists in the literal but it's throwing an error:
Cannot use 'in' operator to search for 'We' in undefined
Here's problematic function:
I commented the line that's causing the problem
function wordCountDict(filename) {
wordCount = {};
inputFile = fs.readFile( root + filename, 'utf8', function( error, data ) {
if(error) {
console.log('error: ', error)
return false;
}
var words = data.split(" ");
for (i in words) {
if(words[i] in wordCount) { // This is where the problem occurs
wordCount[words[i]]++;
} else {
wordCount[words[i]] = 1;
}
console.log(words[i]);
}
});
}
I'm coming from python and this was always the best/easiest way to achieve this, but javascript doesn't seem to agree.
How would I do this in JavaScript?
Declare wordCount as a local variable to that function. It is probably getting overwritten elsewhere:
function wordCountDict(filename) {
var wordCount = {};
...
}
This is a bad idea
for (i in words) {
do not use a for loop to loop through an array! If something is added to the array prototype it will be checked.
var words = data.split(" ");
for (var i=0; i<words.length; i++) {
if(words[i] in wordCount) {
Next thing, is readFile is asynchronous. If code outide of it resets wordCount to an undefined value, you can get this error. You are better off using a local variable and setting the global value when the looping is done. Also that return false does NOTHING inside the readFile.
function wordCountDict(filename) {
var tempWordCount = {};
var inputFile = fs.readFile( root + filename, 'utf8', function( error, data ) {
if(error) {
console.log('error: ', error)
return false;
}
var words = data.split(" ");
for (var i = 0; i<words.length; i++) {
if(words[i] in wordCount) { // This is where the problem occurs
wordCount[words[i]]++;
} else {
wordCount[words[i]] = 1;
}
console.log(words[i]);
}
wordCount = tempWordCount; //set the global variable equal to the local value
});
}
If all you would like to do is check for existance in the object, you can use this:
if(typeof wordCount[words[i]] === 'undefined'){
...
}
I would not recommend just using if(wordCount[words[i]]) because technically there could be a property of the object that exists but evaluates to false.
Note that in Javascript doing something like myObject.something is equivalent to myObject['something'] on an object, and that when you use myObject['somethingElse'] you are basically just dynamically adding members to the object. In Javascript, objects can be used like Python dictionaries, but they really aren't the same thing.

Need an explanation on simple code

$('#ID').on('click', function() {
if(!CommonUtil.compareDateById('startDt','endDt',false, false, true)) {
return false;
}
var cnt = 0;
if(!CommonUtil.isNullOrEmptyById('startDt')) { cnt++; }
if(cnt == 0) {
CommonUtil.setFocusById('srchWord','<spring:message code="confirm.input" arguments="XXXX"/>');
return false;
So if I click on #ID, following logic occurs.
And my question is what does
var cnt = 0;
if(!CommonUtil.isNullOrEmptyById('startDt')) {
cnt++;
}
mean?
The function of isNullOrEmptyById is following:
isNullOrEmptyById: function(id) {
var value = this.getTrimValueById(id);
return this.isNullOrEmpty(value);
},
But what does
cnt++;
do in here??
This is just an if conditional block:
if(!CommonUtil.isNullOrEmptyById('startDt')) {
cnt++;
}
So if CommonUtil.isNullOrEmptyById('startDt') resolves to false, then the condition resolves to true and the code in the block is executed:
cnt++;
The ++ operator increments the value. So whatever numeric value is in cnt will be incremented by 1.
In the overall context of the code, it seems to be treating cnt as more of a boolean than an integer, though. Unless there's more code outside of this example, this can be simplified by using this condition for the last conditional block instead of using cnt and then checking its value.
It is actually unnecessary. Since the cnt is only incremented once it's value is either 0 or 1. Instead you could just get rid of all that and use isNullOrEmptyById function.
if(!CommonUtil.isNullOrEmptyById('startDt')){
CommonUtil.setFocusById('srchWord','<spring:message code="confirm.input" arguments="XXXX"/>');
return false;
}

Categories

Resources