<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <title>DevExtreme Demo</title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
    <link rel="stylesheet" type="text/css" href="https://unpkg.com/devextreme@18.1.1-pre-18064/dist/css/dx.spa.css" />
    <link rel="stylesheet" type="text/css" href="https://unpkg.com/devextreme@18.1.1-pre-18064/dist/css/dx.common.css" />
    <link rel="stylesheet" type="text/css" href="https://unpkg.com/devextreme@18.1.1-pre-18064/dist/css/dx.light.css" />
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" />

    <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js@0.6.25/dist/zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>

    <script src="config.js"></script>
    <script>
        System.import('app')
            .then(function(viz) {
                System.import("devextreme/viz/themes").then(function(viz) {
                    viz.currentTheme("generic.light");
                    viz.refreshTheme();
                });
            })
            .catch(console.error.bind(console));
    </script>

</head>

<body class="dx-viewport">
    <div class="demo-container">
        <demo-app>Loading...</demo-app>
    </div>
</body>

</html>
// In real applications, you should not transpile code in the browser. You can see how to create your own application with Angular and DevExtreme here:
// https://github.com/DevExpress/devextreme-angular/blob/master/README.md

System.config({
    transpiler: 'ts',
    typescriptOptions: {
        module: "commonjs",
        emitDecoratorMetadata: true,
        experimentalDecorators: true
    },
    meta: {
        'typescript': {
            "exports": "ts"
        }
    },
    paths: {
        'npm:': 'https://unpkg.com/'
    },
    map: {
        'ts': 'npm:plugin-typescript@7.0.6/lib/plugin.js',
        'typescript': 'npm:typescript@2.2.2/lib/typescript.js',

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

        'rxjs': 'npm:rxjs@5.3.1',

        'devextreme': 'npm:devextreme@18.1.1-pre-18064',
        'jszip': 'npm:jszip@3.1.3/dist/jszip.min.js',
        'devextreme-angular': 'npm:devextreme-angular@18.1.1-beta.1'
    },
    packages: {
        'app': {
            main: './app.component.ts',
            defaultExtension: 'ts'
        },
        'devextreme': {
            defaultExtension: 'js'
        },
        'devextreme-angular': {
            main: 'index.js',
            defaultExtension: 'js'
        }
    }
});
<div class="filter-container">
    <dx-filter-builder 
        [fields]="fields" 
        [(value)]="filter"
        [customOperations]="customOperations"
        (onInitialized)="refreshDataSource()">
        <dx-tag-box *dxTemplate="let condition of 'tagBoxTemplate'"
            [value]="condition.value"
            [items]="categories"
            (onValueChanged)="condition.setValue($event.value)"
            width="auto">
        </dx-tag-box>      
    </dx-filter-builder>
	<dx-button 
        text="Apply Filter"
        type="default"
        (onClick)="refreshDataSource()">
    </dx-button>
</div>
<div class="list-container">
    <dx-list [dataSource]="dataSource">
        <div *dxTemplate="let item of 'item'">
            <div class="product">
                <img src="{{item.ImageSrc}}" />
                <div>{{item.Name}}</div>
                <div class="price">{{item.Price | currency: 'USD':true:'1.0'}}</div>
            </div>
        </div>
    </dx-list>
</div>
import { NgModule, Component, ViewChild, enableProdMode } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { DxListModule,
        DxButtonModule,
        DxTagBoxModule,
        DxFilterBuilderComponent,
        DxFilterBuilderModule } from 'devextreme-angular';
import DataSource from 'devextreme/data/data_source';
import { Service } from './app.service';

if(!/localhost/.test(document.location.host)) {
    enableProdMode();
}

let anyOfOperation = {
        name: "anyof",
        caption: "Any of",
        icon: "check",
        editorTemplate: "tagBoxTemplate",
        calculateFilterExpression(filterValue: any, field: any) {
            if(filterValue && filterValue.length > 0) {
                let result = [],
                    lastIndex = filterValue.length - 1;
                    
                filterValue.forEach((value, index) => {
                    result.push([field.dataField, "=", value]);
                    if(index !== lastIndex) {
                        result.push("or");
                    }
                });
                return result;
            }
        },
        customizeText(fieldInfo: any) {
            return fieldInfo.value && fieldInfo.value.length ? fieldInfo.value.join(", ") : "";
        }
    };

@Component({
    selector: 'demo-app',
    providers: [Service],
    templateUrl: 'app/app.component.html',
    styleUrls: ['app/app.component.css']
})

export class AppComponent {
    @ViewChild(DxFilterBuilderComponent) filterBuilder: DxFilterBuilderComponent;
    dataSource: any;
    fields: Array<any>;
    customOperations: Array<any>;
    filter: any;
    categories: string[];

    constructor(service: Service) {
        this.fields = service.getFields();
        this.filter = service.getFilter();
        this.categories = service.getCategories();
        this.customOperations = [anyOfOperation];
        this.dataSource = new DataSource({
            store: service.getProducts()
        });
    }
    
    refreshDataSource() {        
        this.dataSource.filter(this.filterBuilder.instance.getFilterExpression());
        this.dataSource.load();
    }
}

@NgModule({
    imports: [
        BrowserModule,
        DxListModule,
        DxButtonModule,
        DxTagBoxModule,
        DxFilterBuilderModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})

export class AppModule { }

platformBrowserDynamic().bootstrapModule(AppModule);
import { Injectable } from '@angular/core';


export class Product {
    ID: number;
    Name: string;
    Price: number;
    Current_Inventory: number;
    Backorder: number;
    Manufacturing: number;
    Category: string;
    ImageSrc: string;
}

let filter: Array<any> = [
        ["Category", "anyof", ["Video Players", "Automation"]],
        "or",
        [
            ["Category", "anyof", ["Monitors", "Projectors"]],
            "and",
            ["Price", "between", [165, 700]]
        ],
        "or",
        [
            ["Category", "=", "Televisions"], 
            "and",
            ["Price", "between", [2000, 4000]]
        ]
    ],
    categories: string[] = [
        "Video Players",
        "Televisions",
        "Monitors",
        "Projectors",
        "Automation"
    ],
    fields: Array<any> = [
        {
            dataField: "ID",
            dataType: "number"
        }, {
            dataField: "Name"
        }, {
            dataField: "Price",
            dataType: "number",
            format: "currency"
        }, {
            dataField: "Current_Inventory",
            dataType: "number",
            caption: "Inventory"
        }, {
            dataField: "Category",
            filterOperations: ["=", "anyof"],
            lookup: {
                dataSource: categories
            }
        }
    ],

    products: Product[] = [{
            ID: 1,
            Name: "HD Video Player",
            Price: 330,
            Current_Inventory: 225,
            Backorder: 0,
            Manufacturing: 10,
            Category: "Video Players",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/1.png"
        }, {
            ID: 3,
            Name: "SuperPlasma 50",
            Price: 2400,
            Current_Inventory: 0,
            Backorder: 0,
            Manufacturing: 0,
            Category: "Televisions",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/3.png"
        }, {
            ID: 4,
            Name: "SuperLED 50",
            Price: 1600,
            Current_Inventory: 77,
            Backorder: 0,
            Manufacturing: 55,
            Category: "Televisions",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/4.png"
        }, {
            ID: 5,
            Name: "SuperLED 42",
            Price: 1450,
            Current_Inventory: 445,
            Backorder: 0,
            Manufacturing: 0,
            Category: "Televisions",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/5.png"
        }, {
            ID: 6,
            Name: "SuperLCD 55",
            Price: 1350,
            Current_Inventory: 345,
            Backorder: 0,
            Manufacturing: 5,
            Category: "Televisions",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/6.png"
        }, {
            ID: 7,
            Name: "SuperLCD 42",
            Price: 1200,
            Current_Inventory: 210,
            Backorder: 0,
            Manufacturing: 20,
            Category: "Televisions",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/7.png"
        }, {
            ID: 8,
            Name: "SuperPlasma 65",
            Price: 3500,
            Current_Inventory: 0,
            Backorder: 0,
            Manufacturing: 0,
            Category: "Televisions",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/8.png"
        }, {
            ID: 9,
            Name: "SuperLCD 70",
            Price: 4000,
            Current_Inventory: 95,
            Backorder: 0,
            Manufacturing: 5,
            Category: "Televisions",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/9.png"
        }, {
            ID: 10,
            Name: "DesktopLED 21",
            Price: 175,
            Current_Inventory: null,
            Backorder: 425,
            Manufacturing: 75,
            Category: "Monitors",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/10.png"
        }, {
            ID: 12,
            Name: "DesktopLCD 21",
            Price: 170,
            Current_Inventory: 210,
            Backorder: 0,
            Manufacturing: 60,
            Category: "Monitors",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/12.png"
        }, {
            ID: 13,
            Name: "DesktopLCD 19",
            Price: 160,
            Current_Inventory: 150,
            Backorder: 0,
            Manufacturing: 210,
            Category: "Monitors",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/13.png"
        }, {
            ID: 14,
            Name: "Projector Plus",
            Price: 550,
            Current_Inventory: null,
            Backorder: 55,
            Manufacturing: 10,
            Category: "Projectors",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/14.png"
        }, {
            ID: 15,
            Name: "Projector PlusHD",
            Price: 750,
            Current_Inventory: 110,
            Backorder: 0,
            Manufacturing: 90,
            Category: "Projectors",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/15.png"
        }, {
            ID: 17,
            Name: "ExcelRemote IR",
            Price: 150,
            Current_Inventory: 650,
            Backorder: 0,
            Manufacturing: 190,
            Category: "Automation",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/17.png"
        }, {
            ID: 18,
            Name: "ExcelRemote BT",
            Price: 180,
            Current_Inventory: 310,
            Backorder: 0,
            Manufacturing: 0,
            Category: "Automation",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/18.png"
        }, {
            ID: 19,
            Name: "ExcelRemote IP",
            Price: 200,
            Current_Inventory: 0,
            Backorder: 325,
            Manufacturing: 225,
            Category: "Automation",
            ImageSrc: "http://jsserver.corp.devexpress.com/Demos-18.1/WidgetsGallery/JSDemos/images/products/19.png"
        }];

@Injectable()
export class Service {
    getProducts(): Product[] {
        return products;
    }

    getFields(): Array<any> {
        return fields;
    }

    getCategories(): string[] {
        return categories;
    }

    getFilter(): Array<any> {
        return filter;
    }
}
::ng-deep .filter-container {
    background-color: rgba(191, 191, 191, 0.15);
    width: 50%;
    float: left;
    margin-bottom: 25px;
    height: 430px;
}

::ng-deep .dx-filterbuilder {
    padding: 10px;
    height: 360px;
    margin: 5px;
    overflow: auto;
}

::ng-deep .dx-filterbuilder .dx-texteditor {
    width: 135px;
}

::ng-deep .dx-button {
    margin: 10px 20px;
    float: right;
}

::ng-deep .list-container {
    float: right;
    width: 50%;
}

::ng-deep .list-container .dx-scrollable-container {
    max-height: 430px;
    padding-left: 30px;
}

::ng-deep .product {
    height: 65px;
}

::ng-deep .product > img {
    height: 100%;
    float: left;
}

::ng-deep .product > div {
    padding-left: 10px;
    vertical-align: top;
    line-height: 65px;
    font-size: 15px;
    float: left;
}

::ng-deep .product > div.price {
    float: right;
    font-size: 18px;
}

::ng-deep .dx-tagbox {
    min-width: 150px;
}

::ng-deep .dx-filterbuilder .dx-numberbox {
    width: 80px;
}