5

I am using angular 2 with primeng p-dropdown and have a case where I need to filter a child dropdown when the user selects a parent. I did it using a custom pipe like

<p-dropdown [options]="carTypesOptions" [(ngModel)]="CarTypeId" name="CarTypeId"></p-dropdown>
<p-dropdown [options]="carMakeOptions | filterCarMakes: CarTypeId" [(ngModel)]="CarMakeId" name="CarMakeId"></p-dropdown>

So when user selects a car type, I am filtering the second dropdown by using the filterCarMakes pipe which takes the CarTypeId (parent selected id). It all works great. Here's my filterCarMakes pipe.

@Pipe({
    name: 'filterCarMakes',
    pure: false
})
export class FilterMakesPipe implements PipeTransform {
    transform(carMakesOptions: CarMakeSelectItem[], carTypeId: string): CarMakeSelectItem[] {
        if (!carTypeId)
            return carMakesOptions.filter(p => p.carTypeId == null);
        //perform some filtering operation
        return filteredCarMakeOptions;
    }
}

The issue is if I put a console.log in the pipe, it would continue to log that message on console very quickly (like once every 100ms) which means it will continue to call even if the value of parent does not change. The side effect of this is, I can't select any value in child dropdown if there's a scroll because it continues to refresh the options.

Simple screenshot of filtered dropdown is below (it won't let me scroll to select other values and will continue to refresh)

enter image description here

P.S: I don't want to do it in onChange event and calling the pipe from component, so is it possible to do it inside template?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Ali Baig
  • 3,819
  • 4
  • 34
  • 47
  • 5
    Make pipe pure by removing `pure: false` Afther that angular will call transform method only if input values change. See also https://stackoverflow.com/questions/39757603/unraveling-angular-2-book-chapter-1-example-5/39761822#39761822 – yurzui Jul 30 '17 at 04:47
  • 1
    @yurzui, good morning :D. that should be the answer – Max Koretskyi Jul 30 '17 at 04:52
  • Thank you @yurzui it worked! That makes it a stupid question then lol – Ali Baig Jul 30 '17 at 04:57
  • 3
    The Angular team discourages filter pipes, for exactly these kinds of reasons. –  Jul 30 '17 at 04:57
  • @torazaburo What is the alternate to using filter pipes then in that case? – Ali Baig Jul 30 '17 at 05:10
  • 3
    Perform the filtering in the component class. Create a property for the filtered list and bind to that. Then when the filter criteria changes, reset the filtered list. I have an example if you are interested. (But my example is based on an input box, not a drop down) – DeborahK Jul 30 '17 at 05:28
  • Hi DeborahK - could you share the link if possible please? Thanks :) – Jose the hose Sep 05 '17 at 22:11

1 Answers1

1

This is happening because the pipe is unpure

Pure pipes: Angular executes a pure pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (String, Number, Boolean, Symbol) or a changed object reference (Date, Array, Function, Object).

Impure pipes: Angular executes an impure pipe during every component change detection cycle. An impure pipe is called often, as often as every keystroke or mouse-move.

Source: https://angular.io/guide/pipes

but if you really need your pipe to be impure for any reason, for performance concerns you will need to set the component changeDetection strategy to OnPush, and trigger change detection manually when the changes are applied.

import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'awesome-component',
  templateUrl: './pda.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AwesomeComponent implements OnInit {
  constructor(
    private cd: ChangeDetectorRef,
  ) { }

  ...

  fetchData() {
    ...
    // after any data change
    this.cd.markForCheck();
    ...
  }
  ...
}
Community
  • 1
  • 1
Anas Al Hamdan
  • 768
  • 1
  • 5
  • 18