<!DOCTYPE html>
<html>

  <head>
    <title>Angular 4 - PrimeNG Datatable Custom filters</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous" />
     <link rel="stylesheet" href="https://unpkg.com/primeng@4.0.0-rc.2/resources/themes/omega/theme.css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
   
    <link rel="stylesheet" href="https://unpkg.com/primeng@4.0.0-rc.2/resources/primeng.min.css" />
    <link rel="stylesheet" href="https://unpkg.com/dragula@3.7.2/dist/dragula.css" crossorigin="anonymous" />
    
  </head>

  <body>
    <my-app>
      Loading...
    </my-app>
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.9/Reflect.js"></script>
    <script src="https://unpkg.com/zone.js@0.8.5/dist/zone.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.41/dist/system.js"></script>
    <script src="https://unpkg.com/typescript@2.2.0/lib/typescript.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </body>

</html>

/* Styles go here */
System.config({
  transpiler: 'typescript',
  typescriptOptions: {
    emitDecoratorMetadata: true,
    experimentalDecorators: true
  },
  map: {
    app: "./src",


    '@angular/core': 'npm:@angular/core@4.0.0/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common@4.0.0/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler@4.0.0/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser@4.0.0/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@4.0.0/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http@4.0.0/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router@4.0.0/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms@4.0.0/bundles/forms.umd.js',
    '@angular/animations': 'npm:@angular/animations@4.0.0/bundles/animations.umd.js',

    '@angular/core/testing': 'npm:@angular/core@4.0.0/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common/bundles@4.0.0/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler@4.0.0/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser@4.0.0/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic@4.0.0/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http@4.0.0/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router@4.0.0/bundles/router-testing.umd.js',

    'rxjs': 'npm:rxjs@5.1.0',
    'lodash': 'npm:lodash@4.17.4/',

    'primeng': 'npm:primeng@4.1.2/primeng.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'

    },
    rxjs: {
      defaultExtension: 'js'
    },
    lodash: {
      defaultExtension: 'js'
    },
    primeng: {
      defaultExtension: 'js'
    }
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  }
});
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';
import { AppComponent } from './app.component';
import { DataTableModule,
         SharedModule,
         MultiSelectModule,
         OverlayPanelModule
} from 'primeng';


@NgModule({
  imports: [ 
    BrowserModule, CommonModule, FormsModule, DataTableModule,
    SharedModule,
    MultiSelectModule,
    OverlayPanelModule
  ],
  declarations: [ 
    AppComponent
  ],
  providers: []
  bootstrap: [ AppComponent ]
})
export class AppModule {}
{
  "compilerOptions": {
    "baseUrl": "",
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "mapRoot": "./",
    "module": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "target": "es5"
  }
}
//our root app component
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import {ROUTER_PROVIDERS, ROUTER_DIRECTIVES, RouteParams, RouteConfig, Router, LocationStrategy, HashLocationStrategy} from '@angular2/router';
import { enableProdMode } from '@angular/core';
import { AppModule }  from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
import { Component, Input, Output, AfterViewInit, ElementRef, EventEmitter, OnChanges } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: 'src/app.component.html',
  styleUrls: ['src/app.css']
})
export class AppComponent implements OnInit {
  public visible: boolean = true;
  
  public filtered: Map<string, boolean> = new Map<string, boolean>();
  public filter = {
    person: [],
    planet: []
  };
  
  public data = [
    {
      planet: 'Tatooine',
      person: 'Luke Skywalker'
    },{
      planet: 'Tatooine',
      person: 'Obi-wan Kenobi'
    },{
      planet: 'Alderaan',
      person: 'Leia Organa'
    },{
      planet: 'Degobah',
      person: 'Master Yoda'
    }
     ];
     
  public people = [
    {
      label: 'Luke Skywalker',
      value: 1
    },{
      label: 'Obi-wan Kenobi',
      value: 2
    },{
      label: 'Leia Organa',
      value: 3
    },{
      label: 'Master Yoda',
      value: 4
    }];
    
    public planets = [
      {
        label: 'Tatooine',
        value: 1
      },{
        label: 'Alderaan',
        value: 2
      },{
        label: 'Degobah',
        value:3
      }];
      
  
  constructor() {
  }

  ngOnInit() {
  }
  
  updateFilters($event) {
    this.filters = [];
    this.filtered = new Map<string, boolean>();
    for (const prop in this.filter) {
      if (this.filter.hasOwnProperty(prop)) {
        this.filtered[prop] = this.filter[prop].length;
        if (this.filter[prop].length) {
          console.log(prop);
        }
      }
    }
  }
  
}
<div class="header">
  <h2>Angular 4 - PrimeNG Datatable Custom Filters</h2>
</div>

<div class="container-fluid" style="padding-top:20px;">
  <div class="row">
    <div class="col-md-12">
      <p-dataTable [value]="data">
        <p-column field="person" header="Person" [filter]="true">
          <ng-template #personFilterTemplate pTemplate="filter" let-col>
            <i class="fa fa-filter" (click)="personMenu.toggle($event)" *ngIf="!filtered[col.field]"></i>
            <span class="fa-stack" (click)="personMenu.toggle($event)" *ngIf="filtered[col.field]">
              <i class="fa fa-square fa-stack-2x"></i>
              <i class="fa fa-filter fa-stack-1x fa-inverse"></i>
            </span>
          </ng-template>
        </p-column>
        <p-column field="planet" header="Planet" [filter]="true">
          <ng-template #planetFilterTemplate pTemplate="filter" let-col>
            <i class="fa fa-filter" (click)="planetMenu.toggle($event)" *ngIf="!filtered[col.field]"></i>
            <span class="fa-stack" (click)="planetMenu.toggle($event)" *ngIf="filtered[col.field]">
              <i class="fa fa-square fa-stack-2x"></i>
              <i class="fa fa-filter fa-stack-1x fa-inverse"></i>
            </span>
          </ng-template>
        </p-column>
      </p-dataTable>
    </div>
  </div>
</div>

<p-overlayPanel #personMenu showCloseIcon="true">
  <p-multiSelect [overlayVisible]="visible" [options]="people" filter="true" defaultLabel="This is a demo" (onChange)="updateFilters($event)" [(ngModel)]="filter.person"></p-multiSelect>
</p-overlayPanel>

<p-overlayPanel #planetMenu showCloseIcon="true">
  <p-multiSelect [overlayVisible]="visible" [options]="planets" filter="true" defaultLabel="           " (onChange)="updateFilters($event)" [(ngModel)]="filter.planet"></p-multiSelect>
</p-overlayPanel>

<HR />
<p-multiSelect [overlayVisible]="visible" [options]="people" filter="true" defaultLabel="This is a demo"></p-multiSelect>
import { Directive, OnChanges, AfterViewInit, OnInit, Input, ElementRef, SimpleChange } from '@angular/core';
import { DragulaService, dragula } from 'ng2-dragula';

@Directive({ selector: '[primeDragula]' })
export class PrimeDragulaDirective implements OnChanges, OnInit, AfterViewInit {
  @Input() public primeDragula: string;
  @Input() public dragulaModel: any;
  @Input() public dragulaOptions: any;
  protected container: any;
  private drake: any;
  private options: any;

  private el: ElementRef;
  private dragulaService: DragulaService;
  public constructor(el: ElementRef, dragulaService: DragulaService) {
    this.el = el;
    this.dragulaService = dragulaService;
  }
  ngOnInit(){
    this.options = Object.assign({}, this.dragulaOptions);
    this.container = this.el.nativeElement;
    
    if(!this.options.initAfterView){
      this.initialize();
    }
  }
  
  ngAfterViewInit() {
    if(this.options.initAfterView){
      this.initialize();
    }
  }
  
  //since we dont have access to the ngprime datatable body or table itself we need to bing laters in the angular event cycle
  //Once this fires we have a tbody tag to attach to and create the drag drop area from.
  //because we need to setup dragula later we needed to create our own version of the directive so we have access to the private property container.
  //If ngdragula ever changes that to protected we can just extend that directive outright and override the container.
  protected initialize(){
    
    if(this.options.childContainerSelector){
        this.container = this.el.nativeElement.querySelector(this.options.childContainerSelector);
        this.options.mirrorContainer = this.container;
      }
      
    let bag = this.dragulaService.find(this.primeDragula);
    let checkModel = () => {
      if (this.dragulaModel) {
        if (this.drake.models) {
          this.drake.models.push(this.dragulaModel);
        } else {
          this.drake.models = [this.dragulaModel];
        }
      }
    };
    if (bag) {
      this.drake = bag.drake;
      checkModel();
      this.drake.containers.push(this.container);
    } else {
      this.drake = dragula([this.container], this.options);
      checkModel();
      this.dragulaService.add(this.primeDragula, this.drake);
    }
  }

  public ngOnChanges(changes: { dragulaModel?: SimpleChange }): void {
    if (changes && changes.dragulaModel) {
      if (this.drake) {
        if (this.drake.models) {
          let modelIndex = this.drake.models.indexOf(changes.dragulaModel.previousValue);
          this.drake.models.splice(modelIndex, 1, changes.dragulaModel.currentValue);
        } else {
          this.drake.models = [changes.dragulaModel.currentValue];
        }
      }
    }
  }
}