【问题标题】:Filter on multiple columns using one pipe angular 2使用一个管道角度 2 过滤多个列
【发布时间】:2017-05-31 01:35:56
【问题描述】:

我正在尝试仅使用一个Pipe 基于多列过滤Array 数据。现在,它过滤第一列值。请检查我下面的代码并帮助我解决这个问题。

我的代码:

@Pipe({ name: "dataFilter", pure: false })
export class DataFilterPipe implements PipeTransform {
    transform(value: Array<any>, filter: any[]) {
        if (!filter) {
            return value;
        } else if (value) {
            return value.filter(item => {
                for (var i = 0; i < filter.length; i++) {
                    if (filter[i][1] == undefined) {
                        return true;
                    } else if ((typeof item[filter[i][0]] === 'string' || item[filter[i][0]] instanceof String) &&
                        (item[filter[i][0]].toLowerCase().indexOf(filter[i][1]) !== -1)) {
                        return true;
                    }
                    return false;
                }
            });
        }
    }
}

我正在传递像dataFilter : [['column1',value1],['column2',value2],['column3',value3]] 这样的数据。

【问题讨论】:

  • 您能提供一个示例值吗?是否在表格上:[{col1:"col1",col2:"col2",col3:"col3"},...,{}]

标签: javascript angular typescript filter pipe


【解决方案1】:

HTML 标记:

*ngFor="let item of _items | filtername:{ column1: searchtext, column2: searchtext } : false"

变换函数:

transform(items: any, filter: any, isAndOperationBwCol: bool): any {
  if (filter && Array.isArray(items)) {
   let filterKeys = Object.keys(filter);
    if (isAndOperationBwCol) {
     return items.filter(item =>
        filterKeys.reduce((memo, keyName) =>
            (memo && new RegExp(filter[keyName], 'gi').test(item[keyName])) || filter[keyName] === "", true));
    } else {
     return items.filter(item => {
      return filterKeys.some((keyName) => {
        console.log(keyName);
        return new RegExp(filter[keyName], 'gi').test(item[keyName]) || filter[keyName] === "";
      });
    });
   }
  } else {
   return items;
  }
}

参考:https://long2know.com/2017/04/angular-pipes-filtering-on-multiple-keys/

【讨论】:

    【解决方案2】:

    这是一个使用作为多列过滤器传递的对象的解决方案。我发现它比传递二维数组更方便:

        @Pipe({
            name: 'filter'
        })
        export class FilterPipe implements PipeTransform {
            transform(items: Array<any>, filter: {[key: string]: any }): Array<any> {
                return items.filter(item => {
                    const notMatchingField = Object.keys(filter)
                                                 .find(key => item[key] !== filter[key]);
    
                    return !notMatchingField; // true if matches all fields
                });
            }
        }
    

    拥有一个包含多列的对象数组:

    this.people = [
      {name: 'John', age: 27, sex: 'male'},
      {name: 'Lara', age: 21, sex: 'female'},
      {name: 'Rick', age: 29, sex: 'male'},
      {name: 'Eva',  age: 27, sex: 'female'},
      {name: 'Mike', age: 27, sex: 'male'}
    ];
    

    还有一个过滤器:

    this.peopleFilter = {age: 27, sex: 'male'};
    

    像这样使用它:

     <div *ngFor="let person of people | filter: peopleFilter;"></div>
    

    因此,有两个人符合我们的条件:JohnMike

    这里是工作插件:Multiple columns filter pipe demo.

    【讨论】:

    • 当我使用它时,它不显示任何数据,即使我输入值时也没有得到任何数据。
    • 谢谢。我将您的帖子标记为答案,但如果没有值(那么它应该返回所有值)或值是任何人输入的数字怎么办?
    • 谢谢!如果没有提供值,假设我们的过滤器是 {},那么所有项目都将被返回,否则它与提供的确切值匹配,这很好,因为例如,可能存在我们想要过滤具有 null 或未定义的字段。 Ofc,这是一个概念,可以根据情况调整解决方案。
    • 谢谢@SeidMehmedovic。调整为过滤同一键的多个值。 Plunker 链接 - plnkr.co/edit/bUeQHzWhDp1qnrvj3tl4
    【解决方案3】:

    我必须有一个可以同时按特定属性值和名为“All”的通用值进行过滤的管道。用户可以通过从下拉列表中选择它们来动态调整这些过滤器(因此使用效率较低的pure: false 配置)。

    在组件中(必须默认为“全部”)

    public filters = { status: "All", condition: "All", other: "All" };
    

    在模板中

    <div *ngFor="let item of items | filter:filters">
    
    @Pipe({
      name: 'filter',
      pure: false
    })
    export class FilterPipe implements PipeTransform {
      transform(items: Array<any>, filter: {[key: string]: any }): Array<any> {
        return items.filter(item => {
          let matches = Object.keys(filter).every(f => {
            return filter[f] === 'All' || item[f] == filter[f];
          })
    
          return matches;
        })
      }
    }
    

    【讨论】:

      【解决方案4】:

      这是我对 Angular 8 所做的:

      目标:从给定关键字的项目列表中搜索多个属性,例如“Property1”和“Property2”:

      app.module.ts:

      ......
      import { MyFilterPipe } from './shared/pipes/my-filter.pipe';
      
      @NgModule({
        declarations: [
          ...
          MyFilterPipe
        ],
        imports: [
          ...
        ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      

      管道:content-filter.pipe.ts

      import { Pipe, PipeTransform } from '@angular/core';
      
      @Pipe({
        name: 'myFilter'
      })
      export class MyFilterPipe implements PipeTransform {
      
        transform(items: any[], keyword: any, properties: string[]): any[] {
          if (!items) return [];
          if (!keyword) return items;
          debugger;
          return items.filter(item => {
            var itemFound: Boolean;
            for (let i = 0; i < properties.length; i++) {
              if (item[properties[i]].toLowerCase().indexOf(keyword.toLowerCase()) !== -1) {
                itemFound = true;
                break;
              }
            }
            return itemFound;
          });
      
        }
      }
      

      组件:

      <input type="search" class="form-control filter-list-input" placeholder="Filter"
                        aria-label="Filter" name="search" [(ngModel)]="searchText" >
      
          <div *ngFor="let itemof myItems | myFilter:searchText:['Property1', 'Property2']; let i = index">...
      </div>
      

      component.ts:

      export class MyListComponent implements OnInit {
      
        ...
        searchText: string;
      

      【讨论】:

        【解决方案5】:

        您可以看到整个工作 ArrayFilterPipe.ts @http://typescript.io/uN29xRdF1Agtypescriptlang.org
        此外,我还附上了打字稿的编译 JS,以便于查看您想要的输出。 如果需要注释代码,请告诉我...

        FYI 选项数组在执行过滤器之前被转换为对象,因为数组感觉很舒服。

        /* yours */
        var option = [['column1',value1],['column2',value2],['column3',value3]];
        /* mine */
        var option = { column1: 'value1', column2: 'value2', column3: 'value3' };
        
        static _arrayToObject(value: Array<Array<any>>): any {
            return (ArrayFilterPipe._isUndefined(value) || value == null) 
            ? value 
            : value.reduce((result, current) => {   
                            result[current[0]] = current[1]; 
                            return result; 
                        } , {});
        }
        

        您还可以通过从https://github.com/angular/angular.js/blob/master/src/ng/filter/filter.js 获得一些灵感来构建类似于 angular 1.x 的数组/对象过滤器

        var ArrayFilterPipe = (function () {
            function ArrayFilterPipe() {
            }
            ArrayFilterPipe._isOfType = function (value, type) {
                return typeof (value) === type;
            };
            ArrayFilterPipe._isUndefined = function (value) {
                return ArrayFilterPipe._isOfType(value, ArrayFilterPipe.TYPES.UNDEFINED);
            };
            ArrayFilterPipe._isObject = function (value) {
                return ArrayFilterPipe._isOfType(value, ArrayFilterPipe.TYPES.OBJECT);
            };
            ArrayFilterPipe._isOrHasMatch = function (value, target) {
                return ArrayFilterPipe._isOfType(value, ArrayFilterPipe.TYPES.STRING) || ArrayFilterPipe._isOfType(target, ArrayFilterPipe.TYPES.STRING)
                    ? value.toString().toLowerCase().indexOf(target.toString().toLowerCase()) >= 0
                    : value == target;
            };
            ArrayFilterPipe._hasOptions = function (value, options) {
                return (ArrayFilterPipe._isUndefined(value) || ArrayFilterPipe._isUndefined(options) ? (ArrayFilterPipe._isUndefined(value) && ArrayFilterPipe._isUndefined(options))
                    : (value === null || options == null) ? (value === null && options == null)
                        : (Array.isArray(value) || Array.isArray(options)) ? false
                            : ArrayFilterPipe._isObject(value) ?
                                (ArrayFilterPipe._isObject(options)
                                    ? Object.keys(options).every(function (key) { return value.hasOwnProperty(key) && ArrayFilterPipe._isOrHasMatch(value[key], options[key]); })
                                    : Object.values(value).some(function (val) { return ArrayFilterPipe._isOrHasMatch(val, options); }))
                                : !ArrayFilterPipe._isObject(value) ?
                                    (!ArrayFilterPipe._isObject(options)
                                        ? ArrayFilterPipe._isOrHasMatch(value, options)
                                        : false)
                                    : false);
            };
            ArrayFilterPipe._arrayToObject = function (value) {
                return (ArrayFilterPipe._isUndefined(value) || value == null) ? value : value.reduce(function (result, current) {
                    result[current[0]] = current[1];
                    return result;
                }, {});
            };
            ArrayFilterPipe.prototype.transform = function (value, options) {
                if (!value || !Array.isArray(value) || ArrayFilterPipe._isUndefined(options) || options === null) {
                    return value;
                }
                options = Array.isArray(options) ? ArrayFilterPipe._arrayToObject(options) : options;
                return value.filter(function (item) { return ArrayFilterPipe._hasOptions(item, options); });
            };
            return ArrayFilterPipe;
        }());
        ArrayFilterPipe.TYPES = {
            OBJECT: 'object',
            STRING: 'string',
            UNDEFINED: 'undefined'
        };
        /*
            TESTING
            --------------------------------------------------
        */
        var pipe = new ArrayFilterPipe();
        var array = [null, undefined, , true, 123123, 'Jeke HeNry', 'joe', 'joe hen', {}, [], { fake: 'hen' }, { name: 'hen' }, { name: 'johenrik', country: 'hen' }, { name: 'joe dick', city: 'hen' }, { name: 'Jeke HeNry', country: 'zxy' }];
        var options = null;
        /* REF:  http://stackoverflow.com/questions/11403107/capturing-javascript-console-log */
        var oldLog = console.log;
        console.log = function (message) {
            var _arguments = arguments;
            var div = Object.keys(arguments).map(function (key) { return Number(key); }).reduce(function (result, key) {
                result = result || document.createElement('div');
                var isJSON = (_arguments[key] != null && (typeof (_arguments[key]) === 'object' || Array.isArray(_arguments[key])));
                var span = document.createElement(isJSON ? 'pre' : 'span');
                span.innerHTML = isJSON ? JSON.stringify(_arguments[key], undefined) : _arguments[key].replace('\n', '</br></br>');
                result.appendChild(span);
                return result;
            }, null);
            document.body.appendChild(div);
            oldLog.apply(console, arguments);
        };
        function test() {
            console.log('options', options);
            console.log('result', pipe.transform(array, options));
        }
        console.log('case : 01');
        console.log('---------------------------------------------------');
        options = 'hen';
        test();
        console.log('\ncase : 02');
        console.log('---------------------------------------------------');
        options = { name: 'hen' };
        test();
        options = [['name', 'hen']];
        test();
        console.log('\ncase : 03');
        console.log('---------------------------------------------------');
        options = { name: 'hen', country: 'hen' };
        test();
        options = [['name', 'hen'], ['country', 'hen']];
        test();
        console.log('\ncase : 04');
        console.log('---------------------------------------------------');
        options = { name: 'hen', city: 'hen', fake: true };
        test();
        options = [['name', 'hen'], ['country', 'hen'], ['fake', true]];
        test();

        【讨论】:

          【解决方案6】:

          我假设你有一个包含这样列的数组:

          [{col1:"col1",col2:"col2",col3:"col3"}]

          我还省略了所有类型检查、空指针和错误处理。当前的解决方案是我尝试使用数组的示例:

          myData:Array<any> = [{col1:"a",col2:"b",col3:"cadd"},
              {col1:"abba",col2:"bobba",col3:"cadd"},
              {col1:"abba",col2:"bobba",col3:"cool"},
              {col1:"a",col2:"bobba",col3:"cool"}];
          

          和管道:

          @Pipe({
            name: 'dataFilter'
          })
          export class DataFilterPipe implements PipeTransform {
          
            transform(value: any, args?: any): any {
              return value.filter(item =>{
                var matchesAll = true;
                for(var i = 0; i<args.length; i++){
                  // check if your data contains the column and the value defined in args.
                  if(item.hasOwnProperty(args[i][0]) && item[args[i][0]]==args[i][1]){
                    continue;
                  }else{ // at least one column did not match,
                    matchesAll = false;
                  }
                }
                return matchesAll;
              });
            }
          
          }
          

          然后你可以调用

          dataFilter.transform(myData,[["col1","abba"],["col2","bobba"],["col3","cool"]]);

          为了得到一个结果,即转换后的第 3 行:[{col1:"abba",col2:"bobba",col3:"cool"}]

          注意:在我的示例中,您可能需要调整列的名称以使其适用于您的代码。

          编辑:使用此解决方案,您还可以传递任意数量的列。

          例如dataFilter.transform(myData,[["col3","cool"]]);

          这将导致转换后的最后两行(来自我的示例):

          [{col1:"abba",col2:"bobba",col3:"cool"},{col1:"a",col2:"bobba",col3:"cool"}]

          编辑:在评论指出代码不起作用后,我提供了上面示例的 plunkr:https://plnkr.co/edit/VdpGJWyzWUVFzYNDSz1g

          【讨论】:

          【解决方案7】:

          如下替换你的代码,

           export class DataFilterPipe implements PipeTransform {
             transform(value: Item[], field: string, args: string): Item[]{
                let filter: string = args ? args.toLocaleLowerCase() : null;
                return filter ? value.filter((item : Item) =>
                    Item[field].toLocaleLowerCase().indexOf(filter) != -1) : value;
             }
          }
          

          在 Html 页面中,

           <tbody *ngFor="let item of items | dataFilter : columnName : value ">
          

          【讨论】:

          • 这只是一个过滤器参数。我需要多个参数。
          • 你到底想做什么?在多列上使用单个过滤器参数进行过滤,还是在单列上使用多个过滤器参数进行过滤?请清除它。
          • 多列上的多个过滤器。我在数组中传递数据,例如[['column1',value1],['column2',value2],['column3',value3]]
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-12-02
          • 2018-03-13
          • 1970-01-01
          • 2021-09-05
          • 2012-05-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多