Using Angular 1.0.7, how can I specify a single index for nested ng-repeats, so that each item on the inner arrays get's a consecutive index value? (i.e. 0, 1, 2, 3 and so on for all elements in all inner arrays)
To illustrate:
<ul>
<li ng:repeat="item in arr1">
<ul>
<li ng:repeat="child in item.children">{{consecutiveIndex++}}</li>
</ul>
</li>
</ul>
I tried to achieve it in the following manner:
var cindex= -1;
$scope.cindex= function () {
console.log('cindex', cindex);
return ++cindex;
};
HTML:
<ul>
<li ng:repeat="item in arr1">
<ul>
<li ng:repeat="child in item.children">{{index()}}</li>
</ul>
</li>
</ul>
I am getting quite exotic AngularJS errors using this (believe me, you don't wanna know).
I have also found out (following the console output), that even for an array with a mere 4 elements, ng-repeat hit my cindex() function over 80 times. Meaning instead of 0, 1, 2 and 3 - I got 84, 85, 86 and 87.
Any ideas?
You can't depend on your {{index()}} to be called a fixed amount of times. Whenever angular decides to dirty check a scope it will run all the bindings.
You can generate the value based on other indexes. Demo plunker
HTML
<body ng:controller="MainCtrl">
<ul>
<li ng:repeat="item in arr1">
<ul ng:init="parentIndex = $index">
<li ng:repeat="child in item.children">{{getConsecutiveIndex(parentIndex, $index)}}</li>
</ul>
</li>
</ul>
</body>
JS
app.controller('MainCtrl', function($scope) {
$scope.arr1 = [{children:[0,1,2,3]}, {children:[4,5]}, {children:[6,7,8]}];
$scope.getConsecutiveIndex = function(parentIndex, $index) {
var total = 0;
for(var i = 0; i < parentIndex; i += 1) {
total += $scope.arr1[i].children.length;
}
return total + $index;
}
});
The ngRepeat directive provides a special $index property which should suit your needs. It is zero-based and is exposed on the local scope of each template instance.
Try this:
<ul>
<li ng:repeat="item in arr1">
<ul>
<li ng:repeat="child in item.children">{{$index + 1}}</li>
</ul>
</li>
</ul>
Related
I have an array in javascript called menuElm that has <ul> elements in it:
<ul id="1"></ul>
<ul id="2"></ul>
<ul id="3"></ul>
I have a page in HTML that has the following:
<ul id="menu">
<li class="menu-item"></li>
<li class="menu-item"></li>
<li class="menu-item"></li>
</ul>
I want to add the elements of menuElm to the HTML page so it would look like this:
<ul id="menu">
<li class="menu-item">
<ul id="1"></ul>
</li>
<li class="menu-item">
<ul id="2"></ul>
</li>
<li class="menu-item">
<ul id="3"></ul>
</li>
</ul>
I have tried the following, but the <ul> elements just wont show up in the page nor in the code:
function CreateMenu() {
var menuElm;
var k = 0;
menuElm = createElm("ul");
menuElm.id = ++k;
for (var i = 0; i < menuElm.length; ++i) {
document.getElementsByClassName("menu-item")[i].appendChild(menuElm[i]);
}
}
I am new with JavaScript, what am I doing wrong?
menuElm.length
The ul element doesn't have a length, so you are looping from 0 to 0, which is 0 iterations.
menuElm = createElm("ul");
This function isn't defined. You need document.createElement('ul');
menuElm = createElm("ul");
menuElm.id = ++k;
You appear to be creating one list item, and then changing its ID and appending it multiple times.
You need a new list item each time you go around the loop.
appendChild(menuElm[i]);
You've been treating menuElm as an element previously. It isn't an array, [i] makes no sense here.
$("#menu").find('li').each(function(i){
$(this).append(menuElm[i]);
});
/* if you want to use jquery here is the code to append */
I have a JSON array with various objects and I want to show these objects in the HTML using ng-repeat, but as follows:
<ul>
<li>object 1</li>
<li>object 2</li>
<li>object 3</li>
<li>object 4</li>
</ul>
<ul>
<li>object 5</li>
<li>object 6</li>
<li>object 7</li>
<li>object 8</li>
</ul>
Basically I want to show only 4 items per row (ul)
How can I do that?
Thanks!
You can use the filter limitTo
ng-repeat="item in items | limitTo:4"
UPDATE:This should work
<ul ng-repeat="item in arr" ng-if="$index%4==0">
<li ng-repeat="item in arr|limitTo:4:$index" >
{{item}}
</li>
</ul>
Plnkr
Try this sample out, In your controller:
var list = {'1','2','3','4','5','6','7','8','9','10','11','12','13','14','15'};
// Using lodash
var chunkedList = _.chunk(list, 4);
In your HTML:
<ul ng-repeat="items in chunkedList">
<li ng-repeat="item in items">Object {{item}}</li>
</ul>
Didn't test but give it a try:
<ul ng-repeat="n in getNumber(number)">
<li ng-repeat="item in items | limitTo:4:n*4">object 1</li>
</ul>
JS
$scope.number = Math.round(items.length/4);
scope.getNumber = function(num) {
return new Array(num);
}
The basic idea is to chunk your array to multidimensional array, and then ng-repeat it twice with nested structure.
If you would like to change the chunk number, you may just change it to any integers you like.
Live demo is here.
HTML
<body ng-app="myApp" ng-controller="MainCtrl as ctrl">
<ul ng-repeat-start="chunk in ctrl.chunkList">
<li ng-repeat="item in chunk">{{item}}</li>
</ul>
<hr ng-repeat-end/>
</body>
JS
angular
.module('myApp', [])
.controller('MainCtrl', [function() {
var self = this;
var listLength;
var groupNum;
var i;
self.list = [
'item1', 'item2', 'item3', 'item4', 'item5',
'item6', 'item7', 'item8'
];
listLength = self.list.length;
groupNum = (listLength % 4 === 0)? listLength / 4 : Math.ceil(listLength / 4);
self.chunkList = [];
for (i = 0; i < groupNum; i++) {
self.chunkList[i] = self.list.slice(i * 4, (i + 1) * 4);
}
}]);
Also notice that if you there's no need for other elements in the loop, you can just remove ng-repeat-start and ng-repeat-end and use ng-repeat directly instead.
<body ng-app="myApp" ng-controller="MainCtrl as ctrl">
<ul ng-repeat="chunk in ctrl.chunkList">
<li ng-repeat="item in chunk">{{item}}</li>
</ul>
</body>
Notes
This is approach is similar to #Shivas Jayram's, but without the need for underscore library.
I have a nested list:
<ul>
<li id="1">first</li>
<li id="2">second</li>
<ul>
<li id="2-1">second nested first element</li>
<li id="2-2">second nested secondelement</li>
<li id="2-3">second nested thirdelement</li>
<ul>
<li id="2-3-1">Other</li>
</ul>
</ul>
<li id="3"i>third</li>
<li id="4">fourth</li>
</ul>
Each element has an id that indicates the position within the list. How do I generate it automatically?
Thank you.
UPDATE:
html is generated by Apache velocity without ID. I'm trying to create a method for updating the id if you move elements with jquery sortable. The structure of the id must be "1" for the first item "1-1" for the first element of the first "li". I tried using index () but I can't generate the id in the form that I need
It's not clear what you exactly want to do here, but here's an example of generating dynamic li's with dynamic id's:
HTML:
<ul id="autoGenerated">
</ul>
JS:
for(var i = 1; i < 3 ;i++){
$("#autoGenerated").append("<li id=2-" + i + ">Testing " + i + "</li>")
}
$('#autoGenerated li').click(function(){
alert($(this).attr("id"));
})
Fiddle.
I need to write a function in pure JavaScript witn no framework to get all specific tags, but only from first level under parent.
For example: I need to call some function on first <ul> and get all <li> from first level of it (<li> with text 1.2 and <li> with text 2.1)
<div id="sideNavigation">
<ul>
<li class=" act open ">
1.2
<ul>
<li class=" ">
1.2
<ul>
<li class=" ">
1.3
<ul>
<li class=" ">1.4</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class=" ">
2.1
<ul>
<li class=" ">2.2.1</li>
<li class=" ">2.2.2</li>
<li class=" ">2.2.3</li>
</ul>
</li>
</ul>
</div>
I've been trying to do it like this:
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li");
but it returns all <li> in this div not only first level <li>. Do you have any quick method to solve my problem or do I have to implement a new function to detect depth of nodes
You can use the attribute .children to get those "li"
var firstDepthLi = document.getElementById("sideNavigation").children[0].children;
If you want a generic function you can create something like:
var getElementsByDepth = function(el, tagname, depth) {
var children = el.children;
var res = new Array();
for(var i=0; i<children.length; i++) {
if (children[i].tagName == tagname) {
res.push(children[i]);
if (depth > 0)
res.concat(getElementsByDepth(children[i], tagname, depth-1));
}
}
return res;
}
Try:
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li")[0];
That should return the first li element out of all li's on the page. Change the zero at the end to a different number to get a different element. You could even set a variable for the value:
var liNum = 0;
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li")[liNum];
And in a function:
function getLi(depth) {
var specificLi = document.getElementById("sideNavigation").getElementsByTagName("li")[depth];
return specificLi;
}
var firstLi = getLi(0);
console.log(firstLi);
<div id="sideNavigation">
<ul>
<li>First list tag</li>
<li>Second list tag</li>
<li>Third list tag</li>
</ul>
</div>
And to make the function even shorter, you could just do:
function getLi(depth) {
return document.getElementById("sideNavigation").getElementsByTagName("li")[depth];
}
That should work. :)
Im trying to rearrange a list. I've got for example a list with categories:
<ul class="category">
<li class="cat1 active">cat1</li>
<li class="cat2 active">cat2</li>
<li class="cat3">cat3</li>
<li class="cat4">cat4</li>
</ul>
and a list with subjects
<ul class="subjects">
<li class="sub1">sub1</li>
<li class="sub2">sub2</li>
<li class="sub3">sub3</li>
<li class="sub4">sub4</li>
<li class="sub5">sub5</li>
<li class="sub6">sub7</li>
<li class="sub7">sub7</li>
<li class="sub8">sub8</li>
</ul>
What I want to do is when a certain category has the class active, some subjects will go to the top of the list. for example like this:
<ul class="subjects">
<li class="sub3">sub3</li>
<li class="sub5">sub5</li>
<li class="sub8">sub8</li>
<li class="sub1">sub1</li>
<li class="sub2">sub2</li>
<li class="sub4">sub4</li>
<li class="sub6">sub6</li>
<li class="sub7">sub7</li>
</ul>
I can do this with this code:
if($('li.cat1').hasClass('active')){
$('.sub3, .sub5, .sub8').insertBefore($('.subjects li:first-child'));
}
But then the real problem starts when multiple categories is active. I have done it like so:
if($('li.cat1').hasClass('active')){
$('.sub3, .sub5, .sub8').insertBefore($('.subjects li:first-child'));
}
if($('li.cat2').hasClass('active')){
$('.sub3, .sub4, .sub7').insertBefore($('.subjects li:first-child'));
}
The result is this:
<ul class="subjects">
<li class="sub4">sub4</li>
<li class="sub7">sub7</li>
<li class="sub3">sub3</li>
<li class="sub5">sub5</li>
<li class="sub8">sub8</li>
<li class="sub1">sub1</li>
<li class="sub2">sub2</li>
<li class="sub6">sub6</li>
</ul>
the order is now 4,7,3,5,8. Those are the numbers that have been selected but how do I rearrange them into: 3,4,5,7,8,1,2,6? Could someone help me with this? Here is the JsFiddle http://jsfiddle.net/NTPd4/2/
You could do 2 things to make it ALL good. First, make an sort method to suit your needs (i went by alpha of inner html, pik your poison). Second, make a variable for a jQuery object you can simply add elements too.
Next, use the variable in your if statements to gather the elements you want. Then before inserting, sort them!
Example
function sortAlpha(a,b){
return a.innerHTML.toLowerCase() > b.innerHTML.toLowerCase() ? 1 : -1;
};
var activeSubjects = $();
if ($('li.cat1').hasClass('active')) {
activeSubjects = activeSubjects.add($('.sub5, .sub3, .sub8'))
}
if ($('li.cat2').hasClass('active')) {
activeSubjects = activeSubjects.add($('.sub3, .sub4, .sub7'))
}
activeSubjects.sort(sortAlpha).insertBefore($('.subjects li:first-child'));
I think the .add will sort them automigikally, but keep in mind, you could also use the sort function straightforward, like:
$('.sub5, .sub3, .sub8').sort(sortAlpha);
Also, for your current setup (where all text is same except for number) you could use a sort function like:
function sortNum(a,b) {
return parseInt($(a).text().substring(3)) - parseInt($(b).text().substring(3));
}
However, the sortAlpha will probably better suite your needs in the long run. As I mentioned before, you can arrange your sort function how you like.
If you want all five elements in their original order, you need to move them at the same time. Use .add() to combine them:
$('.sub3, .sub5, .sub8').add('.sub3, .sub4, .sub7').prependTo($('.subjects'));
Or, integrating it into your code:
var $selector = $();
if ($('li.cat1').hasClass('active')) {
$selector = $selector.add('.sub3, .sub5, .sub8');
}
if ($('li.cat2').hasClass('active')) {
$selector = $selector.add('.sub3, .sub4, .sub7');
}
$selector.prependTo('.subjects');
http://jsfiddle.net/mblase75/GYfpv/