0

In my Angular program, I'm trying to filter an array based on another array. I want to only display the name of people (from empInfo array) that have a value of etoEarned for the type property in the ptoData array. The empInfo array is a list of all of the employees with their employee details and the ptoData array is a list of all of the days that anyone has taken off. There is an EmpKey field in both arrays that says which employee has taken which days off. Right now, it's displaying everyone and only filling in values for those who have them and looks like this: enter image description here

How can I eliminate the names that don't have any allocated hours?

Here's functions that my button calls:

    setValues(): void {
        this.datePTO = this.ptoData.filter(pto => pto.date > this.StartDate && pto.date < this.EndDate);
        this.etoEarned = this.datePTO.filter(pto => pto.type === 'etoEarned');
        
        setTimeout(() => { this.printMonthlyReport() }, 2000);
    }

    printMonthlyReport(): void {

        let printContents, printAdjContents, popupWin, popupWinAdj;
        printAdjContents = document.getElementById('print-monthly-eto-report').innerHTML;
        popupWinAdj = window.open('', '_blank', 'top=0,left=0,height=100%,width=auto');
        popupWinAdj.document.open();
        popupWinAdj.document.write(`
          <html>
            <head>
              <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
              <title>Monthly Adjusted Report</title>
            </head>
            <body>
              ${printAdjContents}
            </body>
          </html>`
        );
    } 

and here's my html:

<div id="print-monthly-eto-report" style="border: none; display: none;">

        <div class="panel-body col-sm-12" *ngFor="let emp of empInfo">
         <div *ngIf="empHasEto(emp)>
          <table class="table">
            <thead>
              <tr>
                <td colspan="4" style="font-weight: bold;">Employee: {{emp.FirstName}} {{emp.LastName}}</td>
              </tr>
              <tr>
                <td>Date</td>
                <td>Hours</td>
                <td>Scheduled</td>
                <td>Notes</td>
              </tr>
            </thead>
            <tbody>
              <ng-container *ngFor="let eto of etoEarned">
                    <tr *ngIf="eto.EmpKey === emp.EmpKey">
                      <td>{{eto.date | date: 'MM/dd/yyyy'}}</td>
                      <td>{{eto.hours}}</td>
                      <td>{{eto.scheduled}}</td>
                      <td>{{eto.notes}}</td>
                    </tr>
                  </ng-container>
            </tbody>
            <tfoot>
              <tr>
                <td colspan="4"><span style="font-weight:500;">Total ETO Hours Earned: {{emp.ETOEarned}}</span></td>
              </tr>

            </tfoot>
          </table>
         </div>
        </div>
      </div>

EDIT - I've added a function (thanks to @LarsMontanaro) and it has fixed the problem of displaying everyone's name, but still leaves space for each person. I'm assuming the problem is my html because I have let emp of empInfo before *ngIf="empHasEto(emp) so it will still go through each person but only display the table for those who return true. How can I fix this to eliminate the white space? (for reference here's what it looks like):

enter image description here

Also, here's my new function (I've updated the .html above):

    empHasEto(emp: EmpInfo) {
        this.datePTO = this.ptoData.filter(pto => pto.date > this.StartDate && pto.date < this.EndDate);
        this.etoEarned = this.datePTO.filter(pto => pto.type === 'etoEarned');

        if (this.empInfo && this.etoEarned) {
            for (let eto of this.etoEarned) {
                if (eto.EmpKey == emp.EmpKey) {
                    return true;
                }
            }
        }
    }
rcarcia02
  • 927
  • 4
  • 15
  • 46

1 Answers1

1

EDIT: A better way of doing this is to use a pipe, to filter the array that you are iterating over.

You can create a custom pipe like so:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'filterEmployeesByEto'
})
export class FilterEmployeesByEtoPipe implements PipeTransform {
  transform(empArray : Array<any>, etoArray : Array<any>): Array<any> {
      if (empArray && etoArray) {
          return empArray.filter((emp) => {
              for (let eto of etoArray) {
                  if (eto.EmpKey === emp.EmpKey) {
                      return true;
                  }
              }
          }
      }
  }
}

and then call your pipe in the html line containing the *ngFor like so:

<div class="panel-body col-sm-12" *ngFor="let emp of empInfo | filterEmployeesByEto : etoEarned">

You will have to register your pipe in your app-module.ts.

A SO post which explains this exact process in more detail is here: How to apply filters to *ngFor

And you can read about Angular Custom Pipes here: https://angular.io/guide/pipes#custom-pipes

LarsMonty
  • 727
  • 8
  • 20