Пользовательский фильтр с 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();
}
});
}