So i have a html layout in which there are blocks (there are no fix number of them, because they can be created dynamically).
In these blocks there are boxes (again, they can be created dynamically)
The boxes contain *html element*s and also have different data attributes
So i need to create an object which looks like this
block1 = {
box1 : {
id : box1.data('id'),
content : box1.html()
},
box2 : {
id : box2.data('id'),
content : box2.html()
}
},
block2 = {
box3 : {
id : box3.data('id'),
content : box3.html()
}
}
Please don't write that the syntax is not correct, i know. I just tried to somehow illustrate what i want.
So my question is how do i do this with the help of jQuery?
Thank you in advanced
You can select all blocks and boxes and iterate over each of them using .each [docs]:
var blocks = {};
$('.block').each(function(index) {
var boxes = {};
$(this).find('.box').each(function(index) {
boxes['box' + index] = {
id: $(this).data('id');
content: $(this).html();
};
});
blocks['block' + index] = boxes;
});
You might not need an object of objects though, maybe an array of array suffices or would be even better, depending on what you intend to do with the data.
To learn more about how objects work, have a look at MDN - Working with Object.
Here is an idea:
1- Iterate over all of the blocks using some CSS selector.
2- Create a generic JS object and set a collection attribute called "boxes" to be an array
3- For each one, iterate over all the boxes inside it, again, using some CSS selector.
4- Create a generic JS object for each box and set the attributes as needed.
Code version
I think something like this would work (not tested):
var blocks = new Array();
$(".blocks").each(function(b) {
var my_block = {boxes: new Array()};
var $block = $(b);
$(".box", $block).each(function(box) {
var $box = $(box);
my_block.boxes.push({id: $box.attr("id"), content: $box.html()});
});
blocks.push(my_block);
});
You should take a look at Knockout.js, it's very comfortable to build an application like yours.
In detail: use Objects. Build an array for yourself, containing Objects with e.g. Block Name and all Child nodes.
<div id="lanesContainer" data-bind="foreach: blocks">
<div id="" class="dropLane laneDefault ui-widget-header ui-dialog ui-widget ui-corner-all ui-front ui-resizable">
<div class="ui-dialog-titlebar ui-helper-clearfix" data-bind="drop: {value: $data.dropTask}">
<p class="laneheader" data-bind="text: $data.title">Lane</p>
</div>
<ul data-bind="foreach: box">
<li class="ui-dialog-content laneItem" data-bind="drag: {value: $data}">
<div class="ui-widget-header laneItemHeader" data-bind="text: $data.Title"></div>
<div class="ui-widget-content laneItemBody" data-bind="text: $data.Description"></div>
<div class="ui-widget-content laneItemFooter">
<div class="ui-corner-all ui-state-default notification-important">
<span class="ui-icon ui-icon-notice" title="sometitle" data-bind="css: {'notification-important-hide': !$root.isElementImportant($data) }"></span>
</div>
</div>
</li>
</ul>
</div>
</div>
Is this useful?
Here is how to get an Object with nested Array of Childs:
function laneObject(title) {
var obj = new Object();
obj.title = title; //Identifier for Lane
obj.childs = []; //Elements of Lane to display
return obj;
}
I am not entirely sure of what your question is, but if you want to create blocks and boxes dynamically, I suggest you first of all use Arrays.
//All dynamically created blocks
blocks = [];
//Create blocks
for(var i = 1; i < 3; i++) {
var block = {
//All dynamically generated bloxes
boxes = [];
};
//Create boxes
for(var j = 1; j < 4; j++) {
block.box[j] = {
id : j,
content : '<span>html for box' + j + '</span>'
}
}
blocks[i] = block;
}
Related
I am making a tournament table on HTML and JS. I have a couple of hundred of « match boxes » to which I want to assign a range of different datas (such as a logo/flag, name of the team, score and more).
How could I write a loop that would execute the following potential thousands of lines :
document.getElementById("matchBox1_team1").innerHTML = teams[1].name;
document.getElementById("matchBox1_flag1").innerHTML = teams[1].flag;
document.getElementById("matchBox1_team2").innerHTML = teams[2].name;
document.getElementById("matchBox1_flag2").innerHTML = teams[2].flag;
document.getElementById("matchBox2_team1").innerHTML = teams[3].name;
document.getElementById("matchBox2_flag1").innerHTML = teams[3].flag;
document.getElementById("matchBox2_team2").innerHTML = teams[4].name;
document.getElementById("matchBox2_flag2").innerHTML = teams[4].flag;
etc…
something like this but I havent got the syntax right :
var j=1;
for (i=0;i<1000;i+=2){
document.getElementById("matchBox("j")_team1").innerHTML = teams[i].name;
document.getElementById("matchBox("j")_flag1").innerHTML = teams[i].flag;
document.getElementById("matchBox("j")_team2").innerHTML = teams[i+1].name;
document.getElementById("matchBox("j")_flag2").innerHTML = teams[i+1].flag;
j++}
Thank you!
It really depends on your method of retrieving data. If you are using ajax then create table rows with js and append those rows to the table so you will only have to get only a single element.
var matchesTable = document.getElementById('matches');
function addMatch(name,flag){
var row = document.createElement('tr');
var nameColumn = document.createElement('th');
nameColumn.innerText = name;
var flagColumn = document.createElement('th');
flagColumn.innerText = flag;
row.append(nameColumn);
row.append(flagColumn);
matchesTable.append(row);
}
Wherever you get that data just call addMatch function and pass all the details as parameters and create th elements for all those details and append to that table.
Else you will need thousands of those rows already created for you to get them and then add those details.
Using id attributes with sequential numbers is a code smell: it is bad practice. Instead you should use classes where their sequence follows from their natural order in the DOM.
For instance:
let teams = [
{ name: "New York Knicks", flag: "flagged" },
{ name: "Toronto Raptors", flag: "cleared" },
{ name: "Sacramento Kings", flag: "cleared" },
{ name: "Miami Hear", flag: "pending" }
];
let teamElems = document.querySelectorAll(".matchBox .team");
let flagElems = document.querySelectorAll(".matchBox .flag");
teams.forEach(({name, flag}, i) => {
teamElems[i].textContent = name;
flagElems[i].textContent = flag;
});
.matchBox { border: 1px solid }
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
You should use appendChild method into the for loop you have created.
Also one hint is you have to create the element (createElement) which you want to insert in the for loop and insert it in the table you have created using appendChild.
You can refer to syntax on w3cschool
$scope.mainArray = [];
$scope.subArray = [];
var myObject = {}
myObject.task = "write";
myObject.execute = true;
for (var i = 0; i < $scope.mainArray.length; i++) {
$scope.subArray[i] = myObject;
$scope.mainArray[i] = $scope.subArray;
}
How can i show the elements inside my subArray using angular js? I'm using the directive ng-repeat to show my mainArray like: ng-repeat="elements in mainArray"
As per your JS code each element inside your mainArray is basically a subArray. In other words, variable elements from your ng-repeat denotes a subArray. You can iterate over it as shown below and use its value to print content in UI
<div ng-repeat="subArray in mainArray">
<div ng-repeat="item in mainArray">
... do some stuff with item here
</div>
</div>
It would be something like this:
<div ng-repeat="subArray in mainArray">
<div ng-repeat="el in subArray">{{el}}</div>
</div>
Working fiddle
It seems like you have the logic set to make the main array properly
I am new to automated testing, Protractor, and angularJS. I have a list that I would like to count, copy to an array maybe, and verify the list text is present. For example The list shows Attractions, Capacity, and Content to the user so they know what privileges they have.
Below is the .html
<div class="home-info">
<div class="home-top home-section">
<h3>User Information</h3>
<div class="home-box">
<div class="property-group wide">
<span>
Change Phillips<br />
</span>
</div>
</div>
<div class="home-box">
<div class="property-group wide">
<div>Editors:</div>
<span>
<ul class="property-stack">
<li><span>Attractions</span>
</li>
<li><span>Capacity</span>
</li>
<li><span>Content</span>
</li>
<li><span>Media</span>
</li>
<li><span>Options</span>
</li>
<li></li>
<li></li>
<li><span>Upload CADs</span>
</li>
</ul>
</span>
</div>
</div>
</div>
Below is the code I have written. I can get the first item on the list however using .all isn't working for me.
var text = "";
browser.driver.findElement.all(By.xpath("//li/span")).count().then(function(count) {
initialCount = count;
console.log(initialCount);
});
browser.driver.findElement(By.xpath("//li/span")).getText().then(function(text) {
console.log(text);
});
I'm trying to avoid using xpath as I was told to try and avoid. To be honest Im lost. Thanks for the help in advance.
Code used for matching:
expect(myLists).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);
I am not sure what version of protractor you're using but you should be able to just call element without the browser or driver prefix. Using element.all should get you the array of of elements you're looking for.
If you want to access specific indexes within that array you can use the .get(index) suffix to the element.all
So below:
1. you get the array of the elements
2. you get the count of the array
3. we call a for loop to iterate through all the indexes of the array
4. each index of the array we call the getText() and print it to the console
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
for(int i = 0; i < count; i++){
myLists.get(i).getText().then(function(text) {
textList[j++] = text;
console.log(text);
});
}
});
EDIT:
In researching I actually found another way to iterate through the array of elements by using the .each() suffix to the element.all.
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
myLists.each(function(element, index) {
element.getText().then(function (text) {
textList[j++] = text;
console.log(index, text);
});
});
});
you should be able to use the textList array to match things.
expect(textList).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);
Once upon a time this was working but somehow it's broken. I want to be able to produce checkboxes using ng-repeat to get as many checkboxes as required based on stored data and use these to filter through a table produced.
Additionally I don't want identical values for the checkboxes to be repeated.
I have made a plnkr with the code.
<div class="row">
<label data-ng-repeat="x in projects">
<input
type="checkbox"
data-ng-true-value="{{x.b}}"
data-ng-false-value=''
ng-model="quer[queryBy]" />
{{x.b}}
</label>
</div>
http://plnkr.co/edit/RBjSNweUskAtLUH3Ss6r?p=preview
So in summary.
Checkboxes to filter Ref.
Checkboxes to be unique.
Checkboxes to be made based off ng-repeat using Ref.
Okay, here's how to do it.
First, let's add a couple of lines of CSS in your to make sure all the checkboxes are visible:
<style>
.row { margin-left: 0px }
input[type=checkbox] { margin-left: 30px; }
</style>
Next, add the following lines to your controller:
app.filter('unique', function() {
return function (arr, field) {
var o = {}, i, l = arr.length, r = [];
for(i=0; i<l;i+=1) {
o[arr[i][field]] = arr[i];
}
for(i in o) {
r.push(o[i]);
}
return r;
};
})
app.controller("maincontroller",function($scope){
$scope.query = {};
$scope.quer = {};
$scope.queryBy = '$';
$scope.isCollapsed = true;
$scope.selectedRefs = [];
$scope.myFilter = function (item) {
var idx = $scope.selectedRefs.indexOf(item.b);
return idx != -1;
};
$scope.toggleSelection = function toggleSelection(id) {
var idx = $scope.selectedRefs.indexOf(id);
if (idx > -1) {
$scope.selectedRefs.splice(idx, 1);
}
else {
$scope.selectedRefs.push(id);
}
};
Phew.
For some reason, your Plunkr's version of AngularJS didn't recognise the unique attribute, so I added one to your controller.
Finally, change your html to this:
<div class="row">
<label data-ng-repeat="x in projects | unique:'b' | orderBy:'b'" >
<input
id="x.b"
type="checkbox"
ng-click="toggleSelection(x.b)"
ng-init="selectedRefs.push(x.b)"
ng-checked="selectedRefs.indexOf(x.b) > -1" />
{{x.b}}
</label>
</div>
... and your ng-repeat to this...
<tr ng-click="isCollapsed = !isCollapsed" ng-repeat-start="x in projects | filter:myFilter | orderBy:orderProp">
If you're interested in knowing how this works, add these lines:
<div style="margin:10px 10px 30px 10px">
<pre>{{ selectedRefs }} </pre>
</div>
I love this trick: you can see the exact contents of our "selectedRefs" array, and see it change as we tick/untick our checkboxes. This really helps when developing/testing our bindings!
As you can see, these changes use the new unique function to get your list of distinct values from your project array, and when the page first loads, we push all of the values into our new "selectedRefs" array.
["123","321","456","654","789","987"]
Then, as you tick/untick the checkboxes, we add/remove that item from this list.
Finally, we use that filter in the ng-repeat.
ng-repeat-start="x in projects | filter:myFilter | orderBy:orderProp"
Job done !
Update
If you wanted to start off with all checkboxes unticked, then it's a simple change. Just remove this line...
ng-init="selectedRefs.push(x.b)"
..and change the myFilter function to show all items initially..
$scope.myFilter = function (item) {
if ($scope.selectedRefs.length == 0)
return true;
var idx = $scope.selectedRefs.indexOf(item.b);
return idx != -1;
};
And to add a "Clear all" button, simply add a button to your form which calls a function in your AngularJS controller like this..
$scope.clearAll = function () {
$scope.selectedRefs = [];
};
(I haven't tested these suggestions though.)
ng-false-value directive needs a value set. Try ng-false-value='false' or ng-false-value='null' (in fact you can skip this one entirely if it has to just be a falsy value and not something concrete, like a string or certain number).
As you've pointed out in the comments, after selecting and then clearing the checkboxes, all rows are filtered out. It happens because unchecking the checkbox will set its value to false, and this does not agree with your entities' values (as you probably know, just stating it for others).
Therefore you do need to set this value to empty string in the end. That'd be the way:
$scope.$watch('quer.$', function () {
if ($scope.quer.$ === false) {
$scope.quer.$ = '';
}
});
I have a div, #containerDiv, which contains elements related to users like first name, last name etc. in separate divs. I need to sort the contents of the container div based on the last name, first name etc. values.
On searching google the examples I got all are appending the sorted results and not changing the entire HTML being displayed. They are also not sorting by specific fields (first name, last name).
So please help me in sorting the entire content of #containerDiv based on specific fields and also displaying it.
The Page looks Like something as mentioned Below:
<div id="containerDiv">
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
</div>
On getting sorted by last name div values, the resulted structure of the container div should look like:
<div id="containerDiv">
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
</div>
Now I think you all can help me in a better way.
this is a sample example:
html:
<div id="containerDiv">
<div>2</div>
<div>3</div>
<div>1</div>
</div>
js
$(function() {
var container, divs;
divs = $("#containerDiv>div").clone();
container = $("#containerDiv");
divs.sort(function(divX, divY) {
return divX.innerHTML > divY.innerHTML;
});
container.empty();
divs.appendTo(container);
});
you may set your divs.sort function param depend on your goal.
jsFiddle.
and a jQuery Plugin is suitable
I suggest you read the div values so you get an array of objects (persons for example) or just names and perform a sort operation on that. Than...output the result to the initial div (overwriting the default values).
I have built a jQuery sort function in which you can affect the sort field.
(it rebuilds the html by moving the row to another location).
function sortTableJquery()
{
var tbl =$("#tbl tr");
var store = [];
var sortElementIndex = parseFloat($.data(document.body, "sortElement"));
for (var i = 0, len = $(tbl).length; i < len; i++)
{
var rowDom = $(tbl).eq(i);
var rowData = $.trim($("td",$(rowDom)).eq(sortElementIndex).text());
store.push([rowData, rowDom]);
}
store.sort(function (x, y)
{
if (x[0].toLowerCase() == y[0].toLowerCase()) return 0;
if (x[0].toLowerCase() < y[0].toLowerCase()) return -1 * parseFloat($.data(document.body, "sortDir"));
else return 1 * parseFloat($.data(document.body, "sortDir"));
});
for (var i = 0, len = store.length; i < len; i++)
{
$("#tbl").append(store[i][1]);
}
store = null;
}
Every time I need to sort lists I use ListJs.
It's well documented, has good performance even for large lists and it's very lightweight (7KB, despite being library agnostic).