I want to have an array with values, one 'true' for each object in my model.
As you can see in my JSFiddle - Hardcoded working, I have currently hard coded the values, and then it works, i.e. the "level 2" tables being collapsed from start.
$scope.dayDataCollapse = [true, true, true, true, true, true];
$scope.dayDataCollapseFn = function () {
for (var i = 0; $scope.storeDataModel.storedata.length - 1; i += 1) {
$scope.dayDataCollapse.append('true');
}
};
But when I replace the hardcoded with an empty array and a function (shown above) to populate it for me, meaning appending 'true' for each store in the storeDataModel, it fails. All level 2 tables are expanded from start, but can collapse them by clicking two times (one for adding value to array and one for collapsing).
Have also tried with a "real" function...:
function dayDataCollapseFn() {
for (var i = 0; $scope.storeDataModel.storedata.length - 1; i += 1) {
$scope.dayDataCollapse.append('true');
}
};
...but I can't get the $scope.dayDataCollapse to populate initally.
How can I solve this?
Your for loop is incorrect. The middle expression is evaluated for true/false, but you've just coded it to be a constant value (well, constant for any invocation of the function anyway). Try this:
function dayDataCollapseFn() {
for (var i = 0; i < $scope.storeDataModel.storedata.length; i += 1) {
$scope.dayDataCollapse.push(true);
}
};
Your function would have done nothing at all if the model had one element, and locked up the browser with a "slow script" warning if the model had zero or more than one elements.
Also note that you should use true, the boolean constant, and not the string 'true'.
edit — also note that it's .push(), not .append()
#Pointy got me in right direction...thanks! =)
...and then I solved the last thing.
I forgot that I had used a negation, i.e. data-ng-show="!dayDataCollapse[$index]" since I was using collapse="dayDataCollapse[$index]" first. Then I removed the collapse since it didn't work well together.
Anyhow...since I removed the bang (!) I could also use false instead of true and then of course switch the booleans in the $scope.selectTableRow() function as well.
The last thing was that I had if-else, where the if statement checked if dayDataCollapse was undefined and then an else for the logic. Of course the logic did not trigger first time as it was undefined.
Functions that made it work...:
$scope.dayDataCollapseFn = function () {
$scope.dayDataCollapse = [];
for (var i = 0; i < $scope.storeDataModel.storedata.length; i += 1) {
$scope.dayDataCollapse.push(false);
}
};
$scope.selectTableRow = function (index, storeId) {
if ($scope.dayDataCollapse === undefined) {
$scope.dayDataCollapseFn();
}
if ($scope.tableRowExpanded === false && $scope.tableRowIndexCurrExpanded === "" && $scope.storeIdExpanded === "") {
$scope.tableRowIndexPrevExpanded = "";
$scope.tableRowExpanded = true;
$scope.tableRowIndexCurrExpanded = index;
$scope.storeIdExpanded = storeId;
$scope.dayDataCollapse[index] = true;
} else if ($scope.tableRowExpanded === true) {
if ($scope.tableRowIndexCurrExpanded === index && $scope.storeIdExpanded === storeId) {
$scope.tableRowExpanded = false;
$scope.tableRowIndexCurrExpanded = "";
$scope.storeIdExpanded = "";
$scope.dayDataCollapse[index] = false;
} else {
$scope.tableRowIndexPrevExpanded = $scope.tableRowIndexCurrExpanded;
$scope.tableRowIndexCurrExpanded = index;
$scope.storeIdExpanded = storeId;
$scope.dayDataCollapse[$scope.tableRowIndexPrevExpanded] = false;
$scope.dayDataCollapse[$scope.tableRowIndexCurrExpanded] = true;
}
}
Updated JSFiddle
Related
It is one of the beginner challenges for javascript, where you need to check whether the passed parameter(string of symbols, namely =, +, any letter) to a function, includes a random letter surrounded by +. If there is one, return true, else - false.
function simple(str) {
let alph = 'abcdefghijklmnopqrstuvwxyz';
let alphArray = alph.split('');
for (let i = 0; i <= alphArray.length; i++) {
if (str.includes(`+${alph[i]}+`)) {
return true;
} else {
return false;
}
}
}
console.log(simple('+d+=3=+s+'));
It should return true, but I am certainly missing something, most likely it's the condition.
Also, tried doing it without a function, with a predefined variable with the given symbols and it worked, but a in a sloppy way.
You need to move the false return statement outside of the loop, because this would end the loop immediately without checking the following possible true values and need to have the index smaller as the length of the string.
BTW, no need to use an array.
function simple(str) {
let alph = 'abcdefghijklmnopqrstuvwxyz';
for (let i = 0; i < alph.length; i++) {
if (str.includes(`+${alph[i]}+`)) {
return true;
}
}
return false;
}
console.log(simple('+d+=3=+s+'));
I basically try to check if all input fields are valid by using the following code:
addData (input){
var isValid = false;
for (var i = 0, ii = input.length; i < ii; ++i) {
if ($('#' + input[i].id).hasClass('valid')) {
isValid = true;
}
}
if (isValid) {
//do stuff
}
}
In total the input.length is 3 and I want to check if all 3 input fields are valid. In my example, it just checks the last input element. In related posts, I couldn`t find a way to solve it with a for-loop. Can anybody help?
Thanks for all the helpful answers: Finally, I adjusted Máté Safranka's answer:
var isValid = input.toArray().every(element => $('#' + element.id).hasClass('valid'))
with .toArray() to convert jQuery object to an Array >> see
here for more information. Defining true or false is not
needed anylonger.
You never set isValid to false, so as long as at least one of the inputs is valid the result will be valid.
Better to swap the true/false and check for any invalid ones, eg:
addData (input){
var isValid = true;
for (var i = 0, ii = input.length; i < ii; ++i) {
if (!$('#' + input[i].id).hasClass('valid')) {
isValid = false;
}
}
if (isValid) {
//do stuff
}
}
Note: there are alternatives to using a loop, but this is to answer the explicit question of fixing the loop in OPs question.
You got it wrong. You're assuming invalid until an element is valid. And you should assume valid until one element is invalid:
addData (input){
var isValid = true;
for (var i = 0, ii = input.length; i < ii; ++i) {
if (!($('#' + input[i].id).hasClass('valid'))) {
isValid = false;
}
}
if (isValid) {
//do stuff
}
}
As the others have said, you need to start off with isValid set to true and then as of the first invalid field, set it to false and break the loop.
But if input is really an array, I'd use input.every instead:
addData (input){
if (input.every(function(element) { return $(element).hasClass('valid'); })) {
// Do stuff
}
}
Array#every calls the callback for the items in the array, returning true if the callback returns a truthy value for all of them, or returning false (and stopping immediately) if the callback ever returns a falsy value.
Or you could use jQuery's filter:
addData (input){
if ($(input).filter(':not(.valid)').length === 0) {
// Do stuff
}
}
Array#every is more concise if you can use an ES2015+ arrow function:
addData (input){
if (input.every(element => $(element).hasClass('valid'))) {
// Do stuff
}
}
If input is an array, you can use .every():
var isValid = input.every(it => $('#' + it.id).hasClass('valid'));
If input isn't an array, you can convert it to an array with Array.from().
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
Note: As pointed out in the comments, this works in IE10 and later. This shouldn't be a problem most of the time (Windows 10 ships with IE11 and Edge, and theoretically every commercial end-user should have upgraded by now), but worth noting in case you have to support legacy platforms.
anding true with isValid (as opposed to assigning true directly) will make it so the first false value will make the whole condition false. Just be sure to start with isValid as true before the loop.
isValid = true;
// inside the loop
isValid = isValid && true;
I have the below javascript 'underscore' code which checks if the given USER_ROLES has at least one VALID_ROLES. If so returns true else false.
It works fine.
But I want to refactor it such that I want to remove the hard coded roles VALID_ROLES and want to check if there is at least one role that starts with ROLE_. How can it be done ?
// Function to check if least one valid role is present
var USER_ROLES = ['ROLE_5'];
function hasAnyRole(USER_ROLES) {
var VALID_ROLES = [ 'ROLE_1', 'ROLE_2', 'ROLE_3', 'ROLE_4' ];
for (var i = 0; i < USER_ROLES.length; i++) {
if (_.contains(VALID_ROLES, USER_ROLES[i])) {
console.log("Found a valid role, returning true.");
return true;
}
}
console.log("No valid role found, returning false.");
return false;
}
You're pretty close, but for what you want there's no need to use underscore:
for (var i = 0; i < USER_ROLES.length; i++) {
if (typeof USER_ROLES[i].indexOf == "function" && USER_ROLES[i].indexOf("ROLE_") > -1) {
console.log("Found a valid role, returning true.");
//return true;
}
}
Use this. no need of underscore you can use .some array
USER_ROLES.some(function(value){
return value.substring(0, 5) === "ROLE_";
});
var index, value, result;
for (index = 0; index < USER_ROLES.length; ++index) {
value = USER_ROLES[index];
if (value.substring(0, 5) === "ROLE_") {
// You've found it, the full text is in `value`.
// So you might grab it and break the loop, although
// really what you do having found it depends on
// what you need.
result = value;
break;
}
}
// Use `result` here, it will be `undefined` if not found
i've working code(below) to read xml in google docs. It works greate if xml looks like this(part i'm interested in):
<row orderID="4452813795" charID="96255569" stationID="60011752" volEntered="1" volRemaining="1" minVolume="1" orderState="0" typeID="11134" range="32767" accountKey="1002" duration="90" escrow="0.00" price="20000.00" bid="0" issued="2016-02-28 02:05:29"/>
What i want is volRemaining value and my code returns 1. But if xml looks like this:
<row orderID="4452813795" charID="96255569" stationID="60011752" volEntered="1" volRemaining="1" minVolume="1" orderState="0" typeID="11134" range="32767" accountKey="1002" duration="90" escrow="0.00" price="20000.00" bid="0" issued="2016-02-28 02:05:29"/>
<row orderID="4452814032" charID="96255569" stationID="60011752" volEntered="1" volRemaining="1" minVolume="1" orderState="0" typeID="11134" range="32767" accountKey="1002" duration="90" escrow="0.00" price="20000.00" bid="0" issued="2016-02-28 02:05:47"/>
Code still returns 1. What i need is to add these values from both rows so that code returns 2 in this case(there may be more rows like these and code need to check if orderState="0").
This is my code:
function getLevelByTypeFromRowset(rowset, id)
{
var rows = rowset.getChildren("row");
var level=null;
var level2=0;
for (var i=0;level==null && i<rows.length;i++)
{
var row=rows[i];
var typeIdAttr=row.getAttribute("typeID");
if (typeIdAttr && typeIdAttr.getValue()==id && row.getAttribute("orderState").getValue()==0)
{
level2=level2 + row.getAttribute("volRemaining").getValue();
if (i = rows.length){
level=level2;
}
}
}
return level;
}
function TradeVolume(id) {
//id=3389;
var idd="orders";
var url = "http://some.url.com";
var document = readXml(url);
var level = null;
var rowsets = document.getRootElement().getChild("result").getChildren("rowset");
for (var i=0;level==null && i<rowsets.length;i++)
{
var rowset=rowsets[i];
var typeIdAttr=rowset.getAttribute("name");
if (typeIdAttr && typeIdAttr.getValue()==idd)
{
level=getLevelByTypeFromRowset(rowset, id);
}
}
if (level==null){
level = 0
}
return parseFloat(level);
}
I've been trying to do it 3 hours and can't came up with any idea...
Edit:
Working code:
function getLevelByTypeFromRowset(rowset, id)
{
var rows = rowset.getChildren("row");
var level=0;
for (var i=0;i<rows.length;i++)
{
var row=rows[i];
var typeIdAttr=row.getAttribute("typeID");
if (typeIdAttr && typeIdAttr.getValue()==id && row.getAttribute("orderState").getValue()==0)
{
level=parseInt(level) + parseInt(row.getAttribute("volRemaining").getValue());
}
}
return level;
}
One problem is if (i = rows.length). You are making an assignment there, where you apparently meant to test for equality. The assignment causes the loop condition to be false, so the loop terminates early.
So you could change the if condition to if (i == rows.length). However, that test will never be true, since the loop condition includes && i<rows.length. Maybe you meant to say if (i == rows.length - 1), so that the body of the if would be executed on the last pass through the loop? But it would only happen if the last row satisfies
(typeIdAttr && typeIdAttr.getValue()==id && row.getAttribute("orderState").getValue()==0)
which I don't think is what you want.
Really what I expect you meant was to move the
if (i == rows.length - 1) {
level = level2;
}
outside of the block of the preceding if. But a simpler way of doing the same thing would be to remove this if block completely, and change
return level;
to
return level2;
I've got an in page text search using JS, which is here:
$.fn.eoTextSearch = function(pat) {
var out = []
var textNodes = function(n) {
if (!window['Node']) {
window.Node = new Object();
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
}
if (n.nodeType == Node.TEXT_NODE) {
var t = typeof pat == 'string' ?
n.nodeValue.indexOf(pat) != -1 :
pat.test(n.nodeValue);
if (t) {
out.push(n.parentNode)
}
}
else {
$.each(n.childNodes, function(a, b) {
textNodes(b)
})
}
}
this.each(function() {
textNodes(this)
})
return out
};
And I've got the ability to hide columns and rows in a table. When I submit a search and get the highlighted results, there would be in this case, the array length of the text nodes found would be 6, but there would only be 3 highlighted on the page. When you output the array to the console you get this:
So you get the 3 tags which I was expecting, but you see that the array is actually consisting of a [span,undefined,span,undefined,undefined,span]. Thus giving me the length of 6.
<span>
<span>
<span>
[span, undefined, span, undefined, undefined, span]
I don't know why it's not stripping out all of the undefined text nodes when I do the check for them. Here's what I've got for the function.
performTextSearch = function(currentObj){
if($.trim(currentObj.val()).length > 0){
var n = $("body").eoTextSearch($.trim(currentObj.val())),
recordTitle = "matches",
arrayRecheck = new Array(),
genericElemArray = new Array()
if(n.length == 1){
recordTitle = "match"
}
//check to see if we need to do a recount on the array length.
//if it's more than 0, then they're doing a compare and we need to strip out all of the text nodes that don't have a visible parent.
if($(".rows:checked").length > 0){
$.each(n,function(i,currElem){
if($(currElem).length != 0 && typeof currElem != 'undefined'){
if($(currElem).closest("tr").is(":visible") || $(currElem).is(":visible")){
//remove the element from the array
console.log(currElem)
arrayRecheck[i] = currElem
}
}
})
}
if(arrayRecheck.length > 0){
genericElemArray.push(arrayRecheck)
console.log(arrayRecheck)
}
else{
genericElemArray.push(n)
}
genericElemArray = genericElemArray[0]
$("#recordCount").text(genericElemArray.length + " " +recordTitle)
$(".searchResults").show()
for(var i = 0; i < genericElemArray.length; ++i){
void($(genericElemArray[i]).addClass("yellowBkgd").addClass("highLighted"))
}
}
else{
$(".highLighted").css("background","none")
}
}
If you look at the code below "//check to see if we need to do a recount on the array length. ", you'll see where I'm stripping out the text nodes based off of the display and whether or not the object is defined. I'm checking the length instead of undefined because the typeof == undefined wasn't working at all for some reason. Apparently, things are still slipping by though.
Any idea why I'm still getting undefined objects in the array?
My apologies for such a big post!
Thanks in advance
I've modified your eoTextSearch() function to remove dependencies on global variables in exchange for closures:
$.fn.extend({
// helper function
// recurses into a DOM object and calls a custom function for every descendant
eachDescendant: function (callback) {
for (var i=0, j=this.length; i<j; i++) {
callback.call(this[i]);
$.fn.eachDescendant.call(this[i].childNodes, callback);
}
return this;
},
// your text search function, revised
eoTextSearch: function () {
var text = document.createTextNode("test").textContent
? "textContent" : "innerText";
// the "matches" function uses an out param instead of a return value
var matches = function (pat, outArray) {
var isRe = typeof pat.test == "function";
return function() {
if (this.nodeType != 3) return; // ...text nodes only
if (isRe && pat.test(this[text]) || this[text].indexOf(pat) > -1) {
outArray.push(this.parentNode);
}
}
};
// this is the function that will *actually* become eoTextSearch()
return function (stringOrPattern) {
var result = $(); // start with an empty jQuery object
this.eachDescendant( matches(stringOrPattern, result) );
return result;
}
}() // <- instant calling is important here
});
And then you can do something like this:
$("body").eoTextSearch("foo").filter(function () {
return $(this).closest("tr").is(":visible");
});
To remove unwanted elements from the search result. No "recounting the array length" necessary. Or you use each() directly and decide within what to do.
I cannot entirely get my head around your code, but the most likely issue is that you are removing items from the array, but not shrinking the array afterwards. Simply removing items will return you "undefined", and will not collapse the array.
I would suggest that you do one of the following:
Copy the array to a new array, but only copying those items that are not undefined
Only use those array items that are not undefined.
I hope this is something of a help.
Found the answer in another post.
Remove empty elements from an array in Javascript
Ended up using the answer's second option and it worked alright.