2
<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

lin
  • 17,956
  • 4
  • 59
  • 83
Mike Young
  • 321
  • 2
  • 4
  • 15

8 Answers8

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 {}
lin
  • 17,956
  • 4
  • 59
  • 83
Mike Young
  • 321
  • 2
  • 4
  • 15
  • My css is better than my JS, why not use this method? – Mike Young Dec 14 '16 at 14:52
  • You did not ask for that handling. You said, all other get no class. =) – lin Dec 14 '16 at 15:04
  • @lin Being smug to the person asking the question is not a way to get your answer accepted, he can change his requirements as needed. – KreepN Dec 14 '16 at 15:05
  • @lin But all others do no get a class, they are in number order. As per my example output, that with this css will only apply the styling to the first .row numbers – Mike Young Dec 14 '16 at 15:12
  • Yea @mike, its might work for you. But its not what you asked for. All elements recive a class in your example undepending on its CSS declaration. In my opinion, my answer is the best and its what you asked for. What ever, i just tried to deliver a nice answer. – lin Dec 14 '16 at 15:15
  • Yes that is true however the only intention of giving something a class is to style it. I only asked this question because I knew you can't use last-of-type with classes. 100 percent appreciate your answer, I just stupidly forgot about the adjacent sibling selector. So yeh, kinda my fault and your answer matches this question a lot better for people that find this question for 'get first item in a repeat that has a certain value' and are not just after it to apply a style – Mike Young Dec 14 '16 at 15:20
  • Your solution is good too. If you had asked differently I would do it the same. – lin Dec 14 '16 at 15:26
1

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;
        }
    });
})();
lin
  • 17,956
  • 4
  • 59
  • 83
0

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];
}
Matuszew
  • 841
  • 5
  • 12
  • Don't do it like this! This will force `$digest` error and is pretty inperformant. – lin Dec 14 '16 at 14:55
  • 1
    There won't be digest error. And yup it may load memory up to N or less - as I said - not cleanest one. – Matuszew Dec 14 '16 at 14:58
  • There will be a digest error, read about http://stackoverflow.com/questions/19693410/angularjs-error-10-digest-iterations-reached-aborting and dont do that kind of E2E binding again. Hope that will help you. – lin Dec 14 '16 at 15:30
  • @lin Just for your reference - https://jsfiddle.net/qxaqm689/1/ no $digest error here. – Matuszew Dec 14 '16 at 15:51
  • Yea with 8 items, no problem. You know about the `$digest` max count? – lin Dec 14 '16 at 15:55
  • 10 - especially for you updated fiddle - https://jsfiddle.net/qxaqm689/3/ , you still don't get it why there won't be $digest loop don't you? – Matuszew Dec 14 '16 at 15:59
  • Let's replace `force $digest error` with `it could end with a digest error`. Truely, as you said, its depending on how the function and `$scopes` being setup. To prevent a `$digest` error you should not call E2E binding functions in loops. Sorry, if that was not clear while Iam saying `this will force an error....`. Best practise is not to use functions in iterations. Sorry for that. – lin Dec 14 '16 at 16:03
  • Agree functions in iterations are not the best idea but as I've said - not the cleanest one but will work. Regarding your answer - it won't work if row number is > than array length :) and it won't work also if array is fetched from backend. Anyway thanks for discussion we have agreement :) – Matuszew Dec 14 '16 at 16:06
  • Haha :) cheers m8. You want to discuss with me about: `- it won't work if row number is > than array length :) and it won't work also if array is fetched from backend.`? I can disprove that. – lin Dec 14 '16 at 16:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130623/discussion-between-lin-and-melzar). – lin Dec 14 '16 at 16:19
  • Sorry m8 it's late here so I will skip further discussion. Regarding length issue and row number - honors back I've misread key variable with row property :) cheers! Pleasure talking with you! – Matuszew Dec 14 '16 at 16:33
0

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

salix
  • 846
  • 6
  • 13
  • This would assume that all the widgets are ordered by their row value in `$scope.widgets`, but will bomb if they are random. If you made sure to order them in your code first, it would be an all-inclusive solution. – KreepN Dec 14 '16 at 15:22
-1

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)">
Rafa Romero
  • 2,667
  • 5
  • 25
  • 51
  • Upsss I didn't know... sorry! Which shouldb be the correct way instead @lin? – Rafa Romero Dec 14 '16 at 14:49
  • Prepare the data before its rendered. For example in your controller init. – lin Dec 14 '16 at 14:49
  • You can read about digest error here: http://stackoverflow.com/questions/19693410/angularjs-error-10-digest-iterations-reached-aborting I hope this will help ya in the future. – lin Dec 14 '16 at 15:32
-1

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;
}
Ron Dadon
  • 2,666
  • 1
  • 13
  • 27
-1

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>
B. LORON
  • 11
  • 3
-1

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;
}
digit
  • 4,479
  • 3
  • 24
  • 43
  • There will be a digest error, read about http://stackoverflow.com/questions/19693410/angularjs-error-10-digest-iterations-reached-aborting and dont do that kind of E2E binding again. Hope that will help you. – lin Dec 14 '16 at 15:30