Finding item of array base on the ng-repeater index in Angular - javascript

In my application i have 2 array of object.layout array is for creating twitter bootstrap layout.this array is like below :
$scope.layout = [
{c:[{size:12}]},
{c:[{size:2},{size:3},{size:4},{size:3}]},
{c:[{size:3},{size:5},{size:4}]}
];
you can see how this array work in this jsbin.the other array is items array and this array is like below:
$scope.items =[
{row:1,column:0,names:['Jack','Daniel']},
{row:3,column:3,names:['Eli','Bill']},
{row:2,column:1,names:['Fred','David']}
];
and this is the repeater that i used :
<div ng-repeat="(ri,r) in layout" class="row">
<div ng-repeat="(ci,c) in r.c" class="col-md-{{c.size}} col-sm-{{c.size}} col-xs-{{c.size}} col-lg-{{c.size}} bi"> Row{{ri}}-Column{{ci}}
//Maybe other repeater come here
</div>
</div>
now i want when i want to display Jack , Daniel in row 1 column 0 and this 1 and 0 is r and c in repeater of first and second repeater.so when the repeater create row 2 column 1 also repeat on $scop.item and find the related names. but i don't know how to find items in $scope.item.and this is my jsbin

You can do something like this:
<div ng-repeat="(ri,r) in layout" class="row">
<div ng-repeat="(ci,c) in r.c" class="col-md-{{c.size}} col-sm-{{c.size}} col-xs-{{c.size}} col-lg-{{c.size}} bi">
{{getNames(ri, ci)}}
</div>
</div>
Where getNames is defined in controller:
$scope.getNames = function(r, c) {
var items = $scope.items;
for (var i = 0; i < items.length; i++) {
if (items[i].row == r && items[i].column == c) {
return items[i].names;
}
}
return '';
};
Demo: http://jsbin.com/sumuwigo/1/edit

Related

AngularJS - how to iterate array on click

I have an array and I want to be able to go to the next index of the array whenever I click a button.
Html code where I call the function:
<div class="flex">
<div class="date-switcher">
<header>
<h1>{{ ctrl.percent}}</h1>
</header>
</div>
<div class="arrows-switch">
<div class="right-arrow" ng-click="nextValue()">
<a href="#"><img src="assets/images/images/images/arrow-sprite_02.png">.
</a>
</div>
</div>
</div>
And the jvs code where I try to iterate :
var ctrl = this;
var percent = [
{ value: 70,},
{ value: 93,},
{ value: 100,},
{ value: 94,},
{ value: 66,}
];
for(var i=0; i < percent.length;i++) {
ctrl.percent = percent[i].value
break
}
$scope.nextValue = function() {
ctrl.percent = percent[i+1].value
}
The output from header h1 {{ctrl.percent}} is 70 which is the ctrl.percent first array item, but when I call the function nextValue, it goes to the second array item and stops.
How can I make it keep going to the next array item till the last?
Expected:
1st click - 2nd array item
2nd click 3rd array item
etc...
indexValue = 0;
$scope.nextValue = function() {
this.percent = percent[indexValue+1].value
}
Declare the index value as a global variable. I think this should do the trick.

How to group all sequently ordered elements with the same class seperated by divider classes?

I have a list which consists of list dividers and items. I want to order items underneath a list divider alphabetically, one by one. Before I can do this, I need those to be grouped. The problem is that those are not ordened within a div, but those are on the same level. I have tried a lot but cannot find out how to group those in an array sothat I am able to loop through the groups.
The html structure I have is the following:
<div class="list">
<div class="list-divider">A list divider</div>
<div class="element">A</div>
<div class="element">B</div>
<div class="list-divider">A list divider</div>
<div class="element">G</div>
<div class="element">A</div>
<div class="element">C</div>
<div class="element">B</div>
<div class="list-divider">A list divider</div>
<div class="element">A</div>
<div class="element">X</div>
<div class="element">X</div>
<div class="element">C</div>
</div>
Is there an easy way to group those into an array?
Heres what I was able to come up with:
var sortable = [];
var newHTML = "";
$(".list > div").each(function(key, value) {
if (value.className == "list-divider"){
sortable.push([]);
}
else {
sortable[sortable.length-1].push(value);
}
})
for (i in sortable)
{
sortable[i].sort(function(a,b) {return (a.innerHTML > b.innerHTML) ? 1 : ((b.innerHTML > a.innerHTML) ? -1 : 0);} );
}
$(".list > .list-divider").each(function(key, value) {
newHTML += value.outerHTML;
for (i in sortable[key])
newHTML += sortable[key][i].outerHTML;
})
$(".list").html(newHTML)
Here is a link to my code

AngularJS get first item in a repeat that has a certain value

<div ng-repeat="widget in widgets"
ng-class="">
<div>{{widget.row}}</div>
</div>
I'm trying to apply a class inside the repeat based on a particular value in the repeat, for example if widget.row = 0 and it is the first widget with that value displayed then give it a class and all the other widgets that have row as 0 do not get the class. This will need to be the case if it equals 1 or 2 and so on so I can't just use $first as there will be multiple row values and multiple widgets for example it may output something like:
0 0 0 0 1 1 2 2 2 2
So the easiest way for me to achieve this was using the Adjacent sibling selector rather than do it with angular as each item is not really aware of the others:
<div ng-repeat="widget in widgets"
class="widget-row-{{widget.row}}">
<div>{{widget}}</div>
</div>
and then use CSS for:
.widget-row-0:first-child {}
.widget-row-0 + .widget-row-1 {}
.widget-row-1 + .widget-row-2 {}
.widget-row-2 + .widget-row-3 {}
Best practise is to prepare your data in a init function in your controller. It's nice and KISS! It's the best way to prepare your data in control function instead of misapply the E2E binding of AngularJS. It solve your problem so no class is written when there is no need for (as you asked for). Its proceeded once instead of calling a function again, again and again by E2E binding like ng-class="shouldIAddAClass()".
View
<div ng-repeat="widget in widgets"
ng-class="{ 'first' : widget.first }">
<div>{{widget.row}}</div>
</div>
Controller
$scope.widgets = [{
row: 0
}, {
row: 2
},{
row: 0
},{
row: 1
},{
row: 1
},{
row: 2
},{
row: 0
}];
//self calling init function
(function init () {
var widgetRowFound = {};
angular.forEach($scope.widgets, function (widget, key) {
if (angular.isDefined(widgetRowFound[widget.row])) {
$scope.widgets[key].first = false;
} else {
$scope.widgets[key].first = true;
widgetRowFound[widget.row] = true;
}
});
})();
Not the cleanest one but will work
<div ng-repeat="widget in widgets">
<div ng-class="{'myClass': applyClass(0, widget.row)}"></div>
</div>
----------
$scope.widgetsRows = {};
function applyClass(number, row){
if(!$scope.widgetsRows[row]){
$scope.widgetsRows[row] = true
}
return row == number && $scope.widgetsRows[row];
}
You can add the class you want to use to the widget objects in the controller first:
var tempRow = "";
for(var i = 0;i < $scope.widgets.length;i++) {
if($scope.widgets[i].row != tempRow) {
$scope.widgets[i].class = "myClass";
tempRow = $scope.widgets[i].row;
}
}
Then you can use that class:
<div id="widgets" ng-repeat="widget in widgets"
class="{{widget.class}}">
<div>{{widget.row}}</div>
</div>
Hope this helps
You can create a method that will be called from ng-class to achieve your goal. The method should return the class to be used.
$scope.firstHitFound = false;
$scope.isFirstZeroValue = function(value){
if($scope.firstHitFound == false && value == 0){
$scope.firstHitFound = true;
return class1;
}else{
return class2;
}
}
The HTML / Angular shoudl look as:
<div ng-class="isFirstZeroValue(widget.row)">
If you want to style it, add the class to all the widget that match your criteria, and use css to perform it only on the first of them.
Html:
<div id="widgets" ng-repeat="widget in widgets"
ng-class="{'widget-first': widget.row == 0}">
<div>{{widget.row}}</div>
</div>
Css:
#widgets.widget-first:first-of-type {
background: #ff0000;
}
You can use ng-class in addition of your ng-repeat:
Example
<div ng-repeat="widget in widgets" ng-class="{'test': widget.value === 0}">
<div>{{widget.row}}</div>
</div>
You need to call a method that will check if the row result is not same with previous value. If it not same , it will return true value and will be assigned ng-class, and if not return false. Filter this out using ng-if.
Html
<div ng-repeat="widget in widgets"
ng-class="">
<div ng-if="calculate(widget.row)">
<div ng-class="test">{{widget.row}}</div>
</div>
<div ng-if="!calculate(widget.row)">
<div>{{widget.row}}</div>
</div>
</div>
Controller
var arr = [];
$scope.calculate = function (row) {
arr.push(row);
var breakLoop = false;
angular.forEach(arr, function (oldVal, newVal) {
breakLoop = false;
if (oldVal != newVal) {
breakLoop = true;
}
)};
return breakLoop;
}

How to group all product of single brand together - angular JS

Please see this JS fiddle link.
http://jsfiddle.net/4Dpzj/174/
This is the logic for group by
app.filter('groupBy', ['$parse', function ($parse) {
return function (list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
// this is a new field which is added to each item where we append "_CHANGED"
// to indicate a field change in the list
//was var new_field = group_by + '_CHANGED'; - JB 12/17/2013
var new_field = 'group_by_CHANGED';
// loop through each item in the list
angular.forEach(list, function (item) {
group_changed = false;
// if not the first item
if (prev_item !== null) {
// check if any of the group by field changed
//force group_by into Array
group_by = angular.isArray(group_by) ? group_by : [group_by];
//check each group by parameter
for (var i = 0, len = group_by.length; i < len; i++) {
if ($parse(group_by[i])(prev_item) !== $parse(group_by[i])(item)) {
group_changed = true;
}
}
}// otherwise we have the first item in the list which is new
else {
group_changed = true;
}
// if the group changed, then add a new field to the item
// to indicate this
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
I want to group all the products together.
what changes i need to do ?
I come up with this in my mind. Without using any custom filters.
I simply use this ng-repeat syntax :
ng-repeat="(key,item) in MyList | orderBy:orderKey"
Thanks to it i can get the key to compare the value with the previous object.
Here is my ng-show attribute. It can be improved by sorting the list somewhere else (like in the controller)
<h2 ng-show="(MyList | orderBy:orderKey)[key-1][orderKey] !== (MyList | orderBy:orderKey)[key][orderKey]"
Thanks to this you can populate your var "orderKey" with any of your attribute name and this will works.
See it working in this JSFiddle
Hope it helped.
EDIT :
I think it would be a bit cleaner to use a temporary list to manage the visual order (see it in this JSFiddle):
JS :
$scope.orderList = function(){
$scope.orderedList = $filter('orderBy')($scope.MyList,$scope.orderKey);
}
HTML :
ng-change="orderList()" To trigger the list sort
The cleaner ng-repeat / ng-show
<div ng-repeat="(key,item) in orderedList">
<h2 ng-show="orderedList[key-1][orderKey] !== orderedList[key][orderKey]">{{item[orderKey]}} </h2>
<ul>
<li>{{item.ProductName}}</li>
</ul>
</div>
Have a look at this:
http://jsfiddle.net/4Dpzj/176/
<div ng-repeat="item in MyList | orderBy:['SubCategoryName','BrandName'] | groupBy:['SubCategoryName']" >
<h2 ng-show="item.group_by_CHANGED">{{item.SubCategoryName}} </h2>
<ul>
<li>{{item.ProductName}} --- {{item.BrandName}}</li>
</ul>
</div>

Sort child div based on data attribute

Trying to sort children div based on data attributes
The html code below is being generated by a CM and the data can be retrieved in any random order.
the html code is
<section class="box explore">
<div id="ProductContainer" class="row">
<div id="1232132" data-name="B" data-category="Category_A" class="explore-cell">
<h>B</h>
<p>Category_A</p>
</div>
<div id="123" data-name="A" data-category="Category_A" class="explore-cell">
<h>A</h>
<p>Category_A</p>
</div>
<div id="1232152351" data-name="C" data-category="Category_A" class="explore-cell">
<h>C</h>
<p>Category_A</p>
</div>
<div id="12342341" data-name="E" data-category="Category_B" class="explore-cell">
<h>E</h>
<p>Category_B</p>
</div>
<div id="1325321" data-name="D" data-category="Category_B" class="explore-cell">
<h>D</h>
<p>Category_B</p>
</div>
</div>
java
$('div').sort(function (a, b) {
var contentA = $(a).attr('data-name');
var contentB = $(b).attr('data-name');
return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
})
Jsfiddle http://jsfiddle.net/w8gkshue/
if someone can point me in the right direct on how to best sort either by Product Name or Category.
Updated hope this gives better explination
EDIT: I missed the jQuery tag... leaving the answer still.
var productCt = document.getElementById('ProductContainer'),
reInsertProductCt = tempRemove(productCt);
[].slice.call(productCt.children)
.sort(function (a, b) {
var aName = a.dataset.name,
bName = b.dataset.name;
return aName < bName? -1 : +(aName > bName);
})
.forEach(productCt.appendChild.bind(productCt));
reInsertProductCt();
function tempRemove(el) {
var parent = el.parentNode,
nextSibling = el.nextSibling;
parent.removeChild(el);
return function () {
if (nextSibling) parent.insertBefore(el, nextSibling);
else parent.appendChild(el);
};
}
<div id="ProductContainer" class="row">
<div id="1232132" data-name="B" data-category="Category_A" class="explore-cell">
<h>TEST NAME B</h>
<p>TEST</p>
</div>
<div id="123" data-name="A" data-category="Category_A" class="explore-cell">
<h>TEST NAME A</h>
<p>TEST</p>
</div>
<div id="1232152351" data-name="C" data-category="Category_A" class="explore-cell">
<h>TEST NAME C</h>
<p>TEST</p>
</div>
<div id="12342341" data-name="E" data-category="Category_B" class="explore-cell">
<h>TEST NAME E</h>
<p>TEST</p>
</div>
<div id="1325321" data-name="D" data-category="Category_B" class="explore-cell">
<h>TEST NAME D</h>
<p>TEST</p>
</div>
</div>
You can use .sort method like this
var $wrapper = $('#ProductContainer');
$wrapper.find('.explore-cell').sort(function (a, b) {
return a.getAttribute('data-name') > b.getAttribute('data-name');
})
.appendTo( $wrapper );
But I don't sure about the cross browsing support
Calling only sort on them won't actually visually change the DOM, it just returns a sorted collection. So basically you just need to get the collection, sort it, then return it. Something like this should work:
$('#ProductContainer > div').detach().sort(function (a, b) {
var contentA = $(a).data('name');
var contentB = $(b).data('name');
return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
}).appendTo('#ProductContainer');
You'll want to make sure that you use the detach() method and not remove(), as detach() will retain all of the data and events associated with the collection items.
Why choose to sort by category or by name when you can sort by both?
I tried to write a generic multisort function generator, which should also work with the native array sort function.
JSFIDDLE HERE
A function that generates the multisort, it takes two parameters.
The column priority list order (first by category or by name? You decide).
I also wanted a way to provide values for columns (since you might not retrieve them the same way for each of them), it is an object that describes for each column a function to retrieve data.
Here it is
function getMultisortFn(columns, provideColumnData) {
return function (a, b) {
for (var i = 0, l = columns.length; i < l; i++) {
var column = columns[i];
var aColumnData = provideColumnData[column.name](a, column.name);
var bColumnData = provideColumnData[column.name](b, column.name);
if (aColumnData !== bColumnData) {
if (column.asc) {
return String.prototype.localeCompare.call(aColumnData, bColumnData);
}
return String.prototype.localeCompare.call(bColumnData, aColumnData);
}
}
};
}
Now this is the part where you actually use the multisort generated
function retrieveDataAttribute(item, attribute) {
return $(item).data(attribute);
}
var $container = $('#ProductContainer');
var $products = $container.find('div');
var multisort = getMultisortFn([{
name: 'category',
asc: false
}, {
name: 'name',
asc: true
}], {
name: retrieveDataAttribute,
category: retrieveDataAttribute
});
$products.sort(multisort);
And finally the DOM manipulation to apply the new order
$products.detach().appendTo($container);
EDIT thanks to plalx:
$container.detach().append($products).appendTo('section.box.explore');

Categories