Search code examples
cssangulartypescriptangular-directiveng-template

*ngFor - How to Override Value in a Template Loop if found


I have an array of objects that look like this:

newItems = [
  { id: 8997, yellow: 'Y', red: 'N', reportId: 1 }, //HewlettPackard 
  { id: 8997, yellow: 'N', red: 'N', reportId: 2 }, //HewlettPackard 
  { id: 6300, yellow: 'N', red: 'Y', reportId: 2 }, //EpsonCenter5000
  { id: 0019, yellow: 'Y', red: 'N', reportId: 1 }  //another report
]

I am looping through the objects using ngFor and it works fine. Basically, if in an object, yellow or red is set to "Y", display the corresponding color circle.

There is approximately 4 reports (reportID's) that can appear for one category id (id).

<div *ngFor="let loop of newItems">
<table>
   <ng-template *ngIf="loop.yellow = 'Y' && loop.red == 'N' && loop.reportId == 1">
      <tr>
         <td class="yellow">Polaris South</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.red = 'Y' && loop.yellow == 'N' && loop.reportId == 1">
      <tr>
         <td class="red">Polaris South</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.reportId !== 1">
      <tr>
         <td class="green">Polaris South</td>
      </tr>
   </ng-template>
   ***********************
   <ng-template *ngIf="loop.yellow = 'Y' && loop.red == 'N' && loop.reportId == 2">
      <tr>
         <td class="yellow">Dandride</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.red = 'Y' && loop.yellow == 'N' && loop.reportId == 2">
      <tr>
         <td class="red">Dandride</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.reportId !== 2">
      <tr>
         <td class="green">Dandride</td>
      </tr>
   </ng-template>

  ********************
   <ng-template *ngIf="loop.yellow = 'Y' && loop.red == 'N' && loop.reportId == 3.1">
      <tr>
         <td class="yellow">Opmanual Internal</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.red = 'Y' && loop.yellow == 'N' && loop.reportId == 3.1">
      <tr>
         <td class="red">Opmanual Internal</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.reportId !== 3.1">
      <tr>
         <td class="green">Opmanual Internal</td>
      </tr>
   </ng-template>

   **************************
   <ng-template *ngIf="loop.yellow = 'Y' && loop.red == 'N' && loop.reportId == 3.2">
      <tr>
         <td class="yellow">Zetaphi Remarks</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.red = 'Y' && loop.yellow == 'N' && loop.reportId == 3.2">
      <tr>
         <td class="red">Zetaphi Remarks</td>
      </tr>
   </ng-template>
   <ng-template *ngIf="loop.reportId !== 3.2">
      <tr>
         <td class="green">Zetaphi Remarks</td>
      </tr>
   </ng-template>
</table>
</div>

The issue is, is for each box where the category (i.e. Hewlett Packard, Epson Center 5000), if there are yellow and red values found for different ReportId's, because the for loop loops through the iterations of objects you get duplicated results if there is more than one report (reportID for a single id). See screenshot.

enter image description here

My goal is that if a yellow is set to "Y" display it on any iteration where the id matches, same thing for red but do not duplicate, instead, overwrite the green default value if a "Y" is found.


Solution

  • Very good question - doing it without TS function made it more challenging... You can check a working demo here

    • I changed the structure of the array (function sortArraybyID) so that we can separate sections for product lines ('HewlettPackard', 'EpsonCenter5000', 'another report').
    • Next, since we must have a row for each report ID, so we put that in an array also and simply print these out
    • We print a green cirlce by default against all reports - so all rows go green (by default)
    • next, we just overwrite the circle if we find a match - the key is to have a greater z-index :)

    updated app.component.ts:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      name = 'Angular';
      newItems = [];
      newArray: any[] = [];
      mustHaveArray: any[] = [];
    
      constructor() {
        this.mustHaveArray = [
          { reportId: 1, descr: 'Polaris South' }, 
          { reportId: 2, descr: 'Dandride' },
          { reportId: 3.1, descr: 'Opmanual Internal' }, 
          { reportId: 3.2, descr: 'Zetaphi Remarks' }
        ];
        this.newItems = [
          { id: '8997', yellow: 'Y', red: 'N', reportId: 1 }, //HewlettPackard 
          { id: '8997', yellow: 'N', red: 'Y', reportId: 2 }, //HewlettPackard 
          { id: '8997', yellow: 'N', red: 'N', reportId: 3.1 }, //HewlettPackard 
          { id: '8997', yellow: 'Y', red: 'N', reportId: 3.2 }, //HewlettPackard 
    
          { id: '6300', yellow: 'N', red: 'Y', reportId: 1 }, //EpsonCenter5000
          { id: '6300', yellow: 'Y', red: 'N', reportId: 2 }, //EpsonCenter5000
    
          { id: '0019', yellow: 'Y', red: 'N', reportId: 1 }, //another report
          { id: '0019', yellow: 'N', red: 'Y', reportId: 2 }, //another report
        ];
        this.sortArraybyID();
      }
    
      sortArraybyID() {
    
        for (var i = 0; i < this.newItems.length; i++) {
          //console.log(i+" off" + this.newItems.length +" for:"+this.newItems[i].id);
    
          let checkForID: boolean = false;
          for (var j = 0; j < this.newArray.length; j++) {
            if (this.newArray[j].id === this.newItems[i].id) { checkForID = true; break; }
          }
          if (checkForID == false) {
            this.newArray.push({ id: this.newItems[i].id, details: [{ yellow: this.newItems[i].yellow, red: this.newItems[i].red, reportId: this.newItems[i].reportId }] });
          } else {
            this.newArray[j].details.push({ yellow: this.newItems[i].yellow, red: this.newItems[i].red, reportId: this.newItems[i].reportId });
          }
        }
        //console.log(this.newArray);
      }
    }
    

    updated app.component.html:

    <div class='tableDiv2' *ngFor="let outer of newArray, let i = index">
        <table>
            <thead>
                <th>
                    <tr> Category [{{i}}] <br/> ID:{{outer.id}} </tr>
                </th>
            </thead>
            <ng-container>
    
                <tbody>
                    <tr *ngFor="let category of mustHaveArray">
                        <td>
                            <ng-container *ngFor="let loop of outer.details; let idx2 = index">
                                <div class='defaultClass'>
                                </div>
                                <div *ngIf="category.reportId == loop.reportId" [ngClass]="(loop.yellow == 'Y' && loop.red == 'N') ? 'yellow' : (loop.yellow == 'N' && loop.red == 'Y') ? 'red' : 'green'  ">
                                </div>
                            </ng-container>
                            <span class='descrClass'>
               {{category.descr}}
               </span>
    
              </td>
                    </tr>
                </tbody>
    
            </ng-container>
        </table>
    </div>
    

    updated app.component.css:

    .yellow , .red , .green
    { border-radius: 50%; height:20px; width: 20px; position: absolute; text-align: center !important; 
    z-index:2;
    }
    
    .defaultClass { background:lightgreen; border-radius: 50%; height:20px; width: 20px; position: absolute; text-align: center !important; z-index:1; }
    
    
    .yellow { background:yellow;   }
    .red { background:lightpink;  }
    .green { background:lightgreen; }
    
    .tableDiv2{float:left; width:24%;}
    .tableDiv{float:left; width:12%;}
    .oldData{ width:100%;  height:10px; float:left;}
    
    .oldData .tableDiv{ background:lightgrey;}
    .descrClass{ margin-left:25px; text-align: left !important;}