2

I am using *ngFor and it has me perplexed. I am trying to use UIkit but it would be applicable to Bootstrap also.

<div *ngFor="let device of devices" >
    <div class="uk-child-width-expand@s uk-text-center" uk-grid>
      <div class="uk-card uk-card-secondary uk-card-hover uk-card-body uk-transform-origin-bottom-right uk-animation-scale-up">
        <h2 class="uk-h2">{{ device.name }}</h2>
          <button class="uk-button uk-button-default" (click)="toggleDevice(device)"  ng-disabled="device.onStatus == true">On</button> <!-- In final app add: ng-disabled="!device.onStatus" -->
          <button class="uk-button uk-button-default" (click)="toggleDevice(device)"  ng-disabled="device.onStatus == false">Off</button> <!-- In final app add: ng-disabled="device.onStatus" -->
      </div>
    </div>
  </div>

The element div class="uk-child-width-expand@s uk-text-center" uk-grid needs be added at the start and after every 3rd element something like i % 3 === 0 is what I was thinking, just nothing I try will make it render correctly. Any ideas would be greatly received.

EDIT - I need to have the view rendered like the lower image on here rather than the top image

EDIT 2 - Added the demo to LINK As it loads is how it should look, working as only 3 elements. If you click sensors, this is how it is displayed using *ngIf tatements.

Cœur
  • 37,241
  • 25
  • 195
  • 267

5 Answers5

4

Here's an aproach that utilises nested *ngFor to force wrapping 3 items in a div

First you will need a getter to split your array into threes.

get device_triples(){
    let arr = [];
    let triple= [];
    for (let i = 1; i <= this.devices.length; i++) {
        triple.push(this.devices[i - 1]);
        if (i % 3 === 0) {
            arr.push(triple);
            triple= [];
        }
    }
    if(triple.length > 0){
        arr.push(triple);
    }
    return arr;
}

This function will transform:

  • [1, 2, 3, 4, 5, 6] into [[1, 2, 3],[4, 5, 6]]
  • [1, 2, 3, 4, 5] into [[1, 2, 3],[4, 5]]

You could also use _.chunk for this

Then you will need to iterate over the new array in your template, add your "div of threes" and then iterate over each sub array, each with at most 3 items in:

<div *ngFor="let triple of device_triples">
    <div class="uk-child-width-expand@s uk-text-center" uk-grid>    
        <div *ngFor="let device of triple">
            <h2 class="uk-h2">{{ device.name }}</h2>
            ...
       <div>
    </div>
</div>
Christopher Moore
  • 3,071
  • 4
  • 30
  • 46
  • Thanks for the response. This looks to be exactly what I need. I will select it as best answer and the bounty is yours. Would I be able to impletement this inside a .ts file. Link to file I am referring too (https://github.com/Sacki2013/homeAutomation/blob/master/client/app/components/devices/devices.component.ts) Thanks again. – Adam 'Sacki' Sackfield Feb 20 '17 at 17:20
2

Just use index that directive provides

like

*ngFor="let device of devices ; let i = index"

then in the html just check with *ngIf and add your block

like <div ... *ngIf="i % 3 === 0"></div>

Probaly you need this approach Ng-repeat-start in angular2 - aka repeat multiple elements using NgFor

(in AngularJS 1.x it was pair of directives ng-repeat-start ng-repeat-end, now it's another approach for the same)

Community
  • 1
  • 1
BotanMan
  • 1,357
  • 12
  • 25
  • Thanks for the quick response. I have tried this already but since the (lets call it row) element I want conditionally repeating has the other elements nested inside it, then it only adds the 1st, 4th etc to the DOM. I feel I need a way to add a wrapper element around the actual content. I can post some screenshots to imgur for clarity as i'm not sure I am wording it in the correct way. – Adam 'Sacki' Sackfield Feb 16 '17 at 08:57
  • Thanks again. Still lost, not sure what you are referring to in that link. Also torazaburo is correct i % 3 === 0 would apply to the first element making i === 0 redundant. – Adam 'Sacki' Sackfield Feb 16 '17 at 09:10
  • I feel since the element I need to add wraps around the other content and when I use conditionals it includes all the nested elements I need a way to remove the (uk-grid) element while still showing what is nested inside it. – Adam 'Sacki' Sackfield Feb 16 '17 at 09:12
1

As the other answer proposed, storing the index with something like *ngFor="let device of devices ; let i = index" will let you keep track of the index in the collection you're iterating over.

Then, as far as I understand you need correctly, you want to conditionally apply a class to elements, namely the first and every third. If you actually want a series like T,F,F,T,F,F,T,F,F,T,... then you can use i % 3 === 0 to produce it and apply a class conditionally with something like

<div (class)="i % 3 === 0 ? 'class-to-apply' : ''">

This will let you conditionally add a class to the first and every third element afterwards.

If instead you want to have different html based on the index, then you have to use ngIf branching and have two html snippets to handle the two cases. Something in the lines of:

<div *ngIf="i % 3 === 0">first case</div>
<div *ngIf="i % 3 !== 0">second case</div>
acidghost
  • 484
  • 4
  • 14
  • Thanks for the response. These methods won't suffice. Take adding the class, it will add it to the first element - say wrapping with – Adam 'Sacki' Sackfield Feb 19 '17 at 15:13
  • What I mean and don't think I was clear is after it open and closes the row on iteration 1, iteration 2 and 3 will be outside of this row and then it will open and close another row on iteration 4 and again the proceeding two will again be outside the row. – Adam 'Sacki' Sackfield Feb 19 '17 at 15:25
  • Added the demo to http://adamsackfield.tech/ As it loads is how it should look, working as only 3 elements. If you click sensors, this is how it is displayed using *ngIf tatements. – Adam 'Sacki' Sackfield Feb 19 '17 at 15:43
0

@ChristopherMoore have great answer but you need to modifythe devices_triple function to add a statement to check

if (i == this.devices.length){
   arr.push(triple);
}

this will help where cases like [[1, 2, 3],[4, 5]] and [[1, 2, 3],[4, 5, 6],[7]]

Fadi Abo Msalam
  • 6,739
  • 2
  • 20
  • 25
0

If you are using Bootstrap V4.0, below link is useful.

How to create a new row of cards using ngFor and bootstrap 4

In my case, it works for me as I am using Bootstrap V4.

Gautam
  • 238
  • 6
  • 17