Angular filter table using custom pipe upon button click

6.4k Views Asked by At

I have a table which I successfully filter using a custom pipe. The filter is based on two inputs which are together a form. The functionality I want to add is for the filtering not to happen until a submit button is clicked. So it's more like a search button. I've found plenty of information on pipes, but nothing about activating them upon button clicks.

mainpage.component.html:

<div>
  <label>Start Date:</label>
  <input type="text" [(ngModel)]="startDateValue">
</div>
  <label>End Date:</label>
  <input type="text" [(ngModel)]="endDateValue">
</div>
//'let idx=index' and 'let even=even' are used to change color of the rows but I took out that code. The 'onClick' function just takes the row and uses an EventEmitter to output it.
<tr *ngFor="let dPoint of theData | searchDates:startDateValue:endDateValue; let idx=index; let even=even;" (click)="onClick(dPoint, idx)">
  <td>{{dPoint.tDataPoint}}</td>
  <td>{{dPoint.tICCP}}</td>
  <td>{{dPoint.tStartDate}}</td>
  <td>{{dPoint.tEndDate}}</td>
</tr>
//the button that will execute the filtering
<button type="submit" class="btn icon-search" [disabled]="!secondForm.valid" (click)="submit(secondForm.value)"></button>

mainpage.component.ts:

@Component({
  selector: 'main-page',
  styleUrls: ['../app.component.css'],
  templateUrl: 'mainpage.component.html',
  providers: [DataTableService, DatePipe]
})

export class MainPageComponent implements OnInit {
  secondForm : FormGroup;
  theData:DataTable[] = [];

  constructor(fb: FormBuilder, private datePipe: DatePipe, private dataService: DataTableService, private cdRef:ChangeDetectorRef){
    this.secondForm = fb.group({
      'startDate' : [null, Validators.required],
      'endDate' : [null, Validators.required]
    }, {validator: this.endDateAfterOrEqualValidator})
  }

  getTable(): void {
    this.dataService.getTable().then(theData => this.theData = theData);
    this.cdRef.detectChanges();
  }

  submit(value: any){
      //where I'd want to trigger the filtering/pipe
  }
}

search-pipe.ts:

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

@Pipe({
  name: "searchDates"
})

export class SearchPipe implements PipeTransform {
  transform(value, minDate , maxDate){
    return value.filter(row => {
      return row.tStartDate >= minDate && row.tEndDate <= maxDate;
    });
  }
}
2

There are 2 best solutions below

2
On BEST ANSWER

You could consider dropping the pipe and instead just filtering the data yourself when the user clicks the button.

First, define a second property that represented the filtered result

let theFilteredData: DataTable[]

Change your binding to bind to theFilteredData instead:

*ngFor="let dPoint of theFilteredData;" //rest of *ngFor not included

In the submit function:

this.theFilteredData = this.theData.filter(row => 
      return row.tStartDate >= minDate && row.tEndDate <= maxDate);
2
On

You can toggle the filtered for loop with a boolean value that is toggled when you click submit:

*.html:

<div>
    <label>Start Date:</label>
      <input type="text" [(ngModel)]="startDateValue">
    </div>
    <label>End Date:</label>
      <input type="text" [(ngModel)]="endDateValue">
    </div>
    <tr *ngIf="filterToggle" *ngFor="let dPoint of theData | searchDates:startDateValue:endDateValue; let idx=index; let even=even;" (click)="onClick(dPoint, idx)">
      <td>{{dPoint.tDataPoint}}</td>
      <td>{{dPoint.tICCP}}</td>
      <td>{{dPoint.tStartDate}}</td>
      <td>{{dPoint.tEndDate}}</td>
    </tr>
    <tr *ngIf="!filterToggle" *ngFor="let dPoint of theData; let idx=index; let even=even;" (click)="onClick(dPoint, idx)">
      <td>{{dPoint.tDataPoint}}</td>
      <td>{{dPoint.tICCP}}</td>
      <td>{{dPoint.tStartDate}}</td>
      <td>{{dPoint.tEndDate}}</td>
    </tr>
    //the button that will execute the filtering
    <button type="submit" class="btn icon-search" [disabled]="!secondForm.valid" (click)="submit()"></button>
</div>

*.ts:

submit() {
  this.filterToggle = !this.filterToggle;
}

Doesnt keep the template code down, but It should work.

Another idea would be to send a boolean 'filterToggle' through the pipe as well, so the pipe itself will not filter unless 'filterToggle' is true. So if you click submit, the toggle should change to true, and the pipe will filter.

so your *ngFor would look like this:

<tr *ngFor="let dPoint of theData | searchDates:startDateValue:endDate:filterToggle; let idx = index; let even=even">
  ...
</tr>

Or, you could just filter your actual data instead of putting it through a pipe.

submit(){
  this.theData = this.theData.filter(data => 
    return data.tStartDate >= this.startDateValue && data.tEndDate <= this.endDateValue);
}

So when you click submit, change your original theData array by filtering it. You may have to play around with the boolean expression that determines what will be filtered. It should keep only the 'data' that has a start date greater or equal to your 'this.startDateValue' and less or equal to your 'this.endDateValue'. However, this would be overwriting your original theData array. So I would create a temporary array so you can revert back to your unfiltered theData.