Пользовательский фильтр с AngularJS ngTable

Я пытаюсь построить таблицу с помощью ngTable, но с другой настраиваемой фильтрацией, чем описано в примере с страницы ngTable.

Я хочу, чтобы фильтрация была на месте, но я не хочу, чтобы ngTable отображал селектора фильтров. Я хочу сделать их самостоятельно (над таблицей), а затем ссылаться на них в методе getData().

Пример, упомянутый ранее, не объясняет, как работает любое из этих механизмов. Я не знаю, что именно должно быть указано в свойстве "filter" в каждом элементе "td". Я понимаю базовый синтаксис функции фильтра AngularJS $, но я не понимаю, что делает ngTable с этим. Из одного примера, похоже, что я могу выполнять только проверку "равно", которая будет выбирать только строки, где соответствующее значение столбца равно значению фильтра. Это не совсем то, что мне нужно.

В моей таблице несколько столбцов. Два из них называются "ключ" и "неудачны", являясь строкой и булевыми соответственно. Когда я создаю эти поля фильтра над таблицей, мне нужен специальный ярлык для фильтра с ошибкой. Фильтрация для столбца "ключ" должна соответствовать значению фильтра с любой подстрокой значений "ключ" . Например, если у меня есть ключевые значения "abc", "abac" и "def", значение фильтра "a" приведет к тому, что первые две записи будут показаны и не будут отображаться "def".

Update:

В связи с этим мне хотелось бы выяснить, как это сделать:

Скажем, у меня есть выражение ngRepeat в моем элементе таблицы, как это, используя "стандартные" фильтры angularjs:

"item in $data | customfilter:param | anothercustomfilter:param"

Мы знаем, что это не совсем работает, поскольку эти фильтры применимы только к одному фрагменту страницы, полученному из метода getData(). То, что я действительно хотел бы сделать в моем методе getData(), - это просто получить доступ ко всей цепочке фильтров, включая выражения параметров, и просто передать в нее другой массив, являющийся полным списком исходных данных, а не только срез страницы.

В то же время мне нужно было бы "выключить", чтобы угловое действие фильтрации выполнялось само по себе, выполняя эту цепочку фильтров в своей нормальной обработке.

Это звучит сложно, но я считаю, что текущий API требует большой связи между html и javascript. Было бы неплохо, если бы html мог указать желаемую фильтрацию, и javascript просто использовал бы всю целую цепочку фильтров, но использовал ее во всем списке данных, а не только для фрагмента страницы.

Update:

Вот соответствующий отрывок из моего HTML:

<label for="keysFilter">Filter Keys:</label>
<input id="keysFilter" type="text" ng-model="keysFilter"/>
<label for="showOnlyFailed">Show only queries that failed?</label>
<input id="showOnlyFailed" type="checkbox" ng-model="showOnlyFailed"/>
<table ng-table="tableParams" table-pagination="custom/pages" class="table">
<tr ng-repeat="queryInfo in $data"> <!--  | filterFailed:showOnlyFailed | filterMatchingKeys:keysFilter -->

Здесь мой код tableParams:

$scope.tableParams  = new ngTableParams({
    page: 1,
    count: 10,
    sorting: {
        lastRun: 'desc'
    }
},
{
    debugMode: true,
    total:  $scope.completedQueries.length,
    getData:    function($defer, params) {
        var orderedData = params.sorting() ?
                $filter('orderBy')($scope.completedQueries, params.orderBy()) :
                data;
        orderedData = $filter('filterFailed')(orderedData, $scope.showOnlyFailed);
        orderedData = $filter('filterMatchingKeys')(orderedData, $scope.keysFilter);

        params.total(orderedData.length);
        $defer.resolve(orderedData.slice((params.page() - 1) * params.count(),
                                                     params.page() * params.count()));
    }
});

Обратите внимание, что я использовал этот ngTable, не используя список "$ data", и просто повторяю его через список "completedQueries". Когда это сработает, список сразу изменится, когда я щелкнул флажок "Показывать только запросы, которые не удалось" или ввел текст в поле ввода "keysFilter".

Однако теперь, когда я использую список "$ data", ничего не происходит, когда я меняю одно из этих полей. Фактически, я даже добавил $watch-es для обоих этих полей, и ни один из них не стрелял. Однако, когда я вношу изменения в любое из этих полей, я знаю, что данные таблицы переоцениваются, потому что два из столбцов имеют данные, которые, как ожидается, будут миллис-значением, и у меня есть настраиваемый фильтр для этих столбцов, которые переводят значение в "временное" английское выражение, например "30 секунд назад" или "2 минуты назад", и каждый раз, когда я меняю одно из этих полей ввода, я вижу, что эти выражения в таблице меняются, но он все еще не выполняет правильная фильтрация.

Если это имеет значение, вот $watch-es, которые я добавил в свою область. Они никогда не срабатывают:

    $scope.$watch("showOnlyFailed", function() {
    $scope.tableParams.reload();
});

$scope.$watch("keysFilter", function() {
    $scope.tableParams.reload();
});

Обратите внимание, что когда я прокомментировал это, я вижу следующую ошибку после того, как ударил мой метод getData():

Error: settings.$scope is null
@http://localhost:8000/js/diag/libs/ng-table.src.js:411
qFactory/defer/deferred.promise.then/[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:11046
qFactory/ref/<.then/<@http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:11132
[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:12075
[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:11903
[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:12179
bootstrap/doBootstrap/<@http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:1341
[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:3762
bootstrap/[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:1340
[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:1353
[email protected]://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:1301
@http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js:21048
n.Callbacks/[email protected]://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js:2
n.Callbacks/[email protected]://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js:2
[email protected]://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js:2
[email protected]://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js:2
http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js
Line 9509

Это соответствующий блок кода:

            $defer.promise.then(function (data) {
            settings.$loading = false;
            log('ngTable: current scope', settings.$scope);
            if (settings.groupBy) {
                self.data = settings.$scope.$groups = data;
            } else {
                self.data = settings.$scope.$data = data; // line 411
            }
            settings.$scope.pages = self.generatePagesArray(self.page(), self.total(), self.count());
        });

Update:

Вот мой plunkr, который демонстрирует, что изменение полей внешнего фильтра не работает. У меня также есть два $watch-es, прокомментированных, чтобы попытаться исправить это. Когда я прокомментирую это, я получаю сообщение об ошибке в ng-таблице, жалуясь на нулевую область.

Update:

Я попытался добавить параметры newvalue, oldvalue к моим $watch-es (я обновил plunkr). Теперь изменения в полях вызывают обновление таблицы. К сожалению, я все еще получаю эту трассировку стека в строке 411 ng-таблицы.

Ответ 1

Вам не нужны часы и пользовательские фильтры, которые вы создали. Фактически фильтр angular 'filter' довольно мощный.

Вам просто нужно создать объект, который отслеживает ваши значения фильтра с элементами, соответствующими полям ваших элементов. Что-то вроде этого.

$scope.filter = {
    key: undefined,
    failed: undefined
}

то вы можете вернуть его обратно в обратный вызов getData, используя params.filter(). Я обновил ваш plunker здесь. Вы также можете проверить образец ниже:

var app = angular.module('main', ['ngTable']);

app.controller('MainCtrl', function($scope, $http, $filter, ngTableParams) {

    $scope.completedQueries = [{"key":"abc000","lastRun":123,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc001","lastRun":1234,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc002","lastRun":111111111,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":true},{"key":"abc003","lastRun":123,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc004","lastRun":1234,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":true},{"key":"abc005","lastRun":111111111,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc006","lastRun":123,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc007","lastRun":1234,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc008","lastRun":111111111,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc009","lastRun":123,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc010","lastRun":1234,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc011","lastRun":111111111,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc012","lastRun":123,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":true},{"key":"abc013","lastRun":1234,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc014","lastRun":111111111,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc015","lastRun":123,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":true},{"key":"abc016","lastRun":1234,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false},{"key":"abc017","lastRun":111111111,"lastSuccessfulTime":9999,"elapsedTime":456,"rows":10,"failed":false}];
    $scope.filter = {
        key: undefined,
        failed: undefined
    };
    $scope.tableParams =  new ngTableParams({
        page: 1,
        count: 10,
        filter: $scope.filter
    }, {
        debugMode: true,
        total: $scope.completedQueries.length,
        getData: function($defer, params) {
            var orderedData = params.sorting() ? $filter('orderBy')($scope.completedQueries, params.orderBy()) : data;
            orderedData	= $filter('filter')(orderedData, params.filter());
            params.total(orderedData.length);
            $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
        }
    });
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/ng-table/0.3.3/ng-table.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ng-table/0.3.3/ng-table.min.js"></script>
<div  ng-app="main"  ng-controller="MainCtrl">
  <label for="keysFilter">Filter Keys:</label>
  <input id="keysFilter" type="text" ng-model="filter.key"/>
  <label for="showOnlyFailed">Show only queries that failed?</label>
  <input id="showOnlyFailed" type="checkbox" ng-model="filter.failed"/>
  <br/>
  <table ng-table="tableParams" class="table">
    <tr ng-repeat="queryInfo in $data">
        <td data-title="'Key'" sortable="'key'">{{queryInfo.key}}</td>
        <td data-title="'Last Run'" sortable="'lastRun'">{{queryInfo.lastRun}}</td>
        <td data-title="'Last Successful Run'" sortable="'lastSuccessfulRun'">{{queryInfo.lastSuccessfulRun}}</td>
        <td data-title="'Elapsed Time'" sortable="'elapsedTime'">{{queryInfo.elapsedTime}} ms</td>
        <td data-title="'Rows'" sortable="'rows'">{{queryInfo.rows}}</td>
        <td data-title="'Failed'" sortable="'failed'">{{queryInfo.failed}}</td>
        <td data-title="''"><button class="btn">Detail</button></td>
    </tr>
  </table> 
<div>

Ответ 2

Похоже, что у вас возникла проблема с самой ng-table, которая является сторонней библиотекой, которая не очень хорошо поддерживается.

В качестве доказательства этого, проверьте их страницу Github, которая имеет более 200 открытых выпусков и 1200 звезд (1: 6):

https://github.com/esvit/ng-table

Сравните это с библиотекой типа D3.js, которая пользуется полной технической и финансовой поддержкой New York Times. Он имеет 150 выпусков и более 30 000 звезд (1: 200).

https://github.com/mbostock/d3

Большое количество открытых проблем означает, что люди находят ошибки быстрее, чем разработчики могут их решить. Скорее всего, разработчики, вероятно, перешли к новым проектам и больше не заинтересованы в поддержке этой библиотеки.

Другими словами, вы столкнулись с ограничением функциональности самой библиотеки. Когда это произойдет, у вас есть два варианта:

1. Прекратите использование библиотеки, выберите новую библиотеку или создайте свой собственный.

К сожалению, я изучил это, и сейчас нет действительно хорошей библиотеки таблиц angular

2. Прокрутите репозиторий, изучите внутреннюю работу библиотеки, реализуйте функцию, которую вы хотите, и сделайте запрос на растяжение.

Просто так, что это не полный облом, возможно UI-Grid может помочь вам, где ng-table не удалось.

http://ui-grid.info/

Раскрытие информации: я не являюсь аффилированным с ui-сетью и все еще в значительной степени на заборе о том, хорош ли он даже

Ответ 3

В нашей ситуации мы использовали ng-table-dynamic для того, чтобы у нас был набор настраиваемых столбцов, для которых необходимо, чтобы пользовательские столбцы отображались в списке пользователей из базы данных на уровне кода. Поскольку отображение выполняется вручную на уровне кода, мы находим уникальный способ обработки фильтрации и сортировки с помощью ng-table-dynamic.

Вот код

          function initialize() {
    this.$scope.userList = new this.NgTableParams({
          page: 1,
          count: 10
        }, {
          counts: [],
          total: this.usersList.length,
          getData: (params) => { // handles custom sorting or filtering
            var results= [];


            //add logic here if any
            results = this.$filter('filter')(results, this.$scope.searchKey);

            params.total(results.length);
            pages = Math.ceil(params.total()/ params.count());
            if(pages > 1){
              results = results.slice((params.page() - 1)* params.count(), params.page() * params.count());
            }
            return results;
          }
        });
    }

Примечание. Мы использовали объявленные переменные, где они будут содержать данные, которые будут отображаться в таблице.

Также в контроллере нам нужно обрабатывать пользовательский поиск

вот код:

    this.$scope.$watch('searchKey',(newValue)=>{
          if(typeof newValue !== 'undefined'){
            this.$scope.userList.reload();// cause re-render of result in key-press
          }
    });

Этот код вызовет функцию getData NgTableParams, позволяющую вам создать собственный фильтр.

Примечание. Вам также необходимо сохранить массив результатов, поэтому вам нужно сохранить его в переменной $scope

Код будет выглядеть так:

    function foo() {
        Provider('Method', (error, users) => {
          if (!error) {
            this.usersList  = users;
            this.$scope.userList.reload(); // cause render of results to the table
            this.$scope.$apply();
          }
        });
      }