2

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;
    });
  }
}
Drew13
  • 1,301
  • 5
  • 28
  • 50
  • update your post with **mainpage.component.html** – Aravind Dec 29 '16 at 19:54
  • @Aravind I tried to include minimal code, just enough so the question was understood. What part(s) of **mainpage.component.html** do you find confusing? Or what do you think is missing? – Drew13 Dec 29 '16 at 19:59
  • since your question is all about **button click** and custom pipe. where is the button triggering the click event? – Aravind Dec 29 '16 at 20:00
  • @Aravind in my `button` tag where it says `(click)="submit(secondForm.value)"`. I edited my question by adding the `submit` function, but it has nothing in it since that is what my question is asking. – Drew13 Dec 29 '16 at 20:04

2 Answers2

5

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);
Drew13
  • 1,301
  • 5
  • 28
  • 50
DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • this gives me console error `value.filter is not a function` when I click my submit button which triggers `submit` function. – Drew13 Dec 29 '16 at 20:36
  • I think it's because `value` is the value of my form, which is from the two inputs. I need to filter the table, not the form – Drew13 Dec 29 '16 at 20:44
0

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.

Pezetter
  • 2,783
  • 2
  • 22
  • 40
  • your first solution is more of work around than solution which I'd rather not try, but appreciate. Your solution to toggle a boolean works though! Also, how exactly would I filter the data instead of using pipe? I tried it like @DeborahK 's solution but that didn't work, as you'll see in the comments for her answer. – Drew13 Dec 29 '16 at 20:54
  • 1
    Yeah I am aware. I just love the power of the ngIf. I added the filtering solution to my response. Which is pretty much the same as Deborahs. I also think keeping this functionality in a pipe is better. So you can easily reuse else where in your project. – Pezetter Dec 29 '16 at 21:23