Как заполнить фильтры выбора в ng-таблице из асинхронного вызова

StackOverflow https://stackoverflow.com//questions/24019769

  •  21-12-2019
  •  | 
  •  

Вопрос

ТЛ: доктор

Как я могу заполнить ng-таблицу, включая фильтры «выбрать», используя ajax/json?

Планк показывает проблему:http://plnkr.co/Zn09LV


Деталь

Я пытаюсь освоить AngualrJS и расширение ng-table, и хотя я могу получить несколько хороших таблиц с рабочими фильтрами и тому подобное, когда я использую статические данные, определенные в javascript - как только я попытаюсь загрузить реальные данные в стол я наткнулся на корягу.

Основная часть ng-таблицы заполнена правильно, и пока я использую только текстовый фильтр, все работает:

        <td data-title="'Name'" filter="{ 'Name': 'text' }" sortable="'Name'">
            {{user.Name}}
        </td>

Работает просто отлично.

Однако, если я обновлю это, чтобы использовать фильтр выбора:

        <td data-title="'Name'" filter="{ 'Name': 'select' }" sortable="'Name'"  filter-data="Names($column)">
            {{user.Name}}
        </td>

Я столкнулся с проблемой синхронизации: переменная Names всегда оценивается до того, как данные вернутся с сервера.(Возможно, переменная Names оценивается еще до отправки запроса на сервер.) Это означает, что я получаю пустой список для фильтра.

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

Для моего javascript я в значительной степени использовал пример кода ajax со страницы GitHub ng-table и добавил к нему пример кода для фильтра выбора.

    $scope.tableParams = new ngTableParams({
        page: 1,            // show first page
        count: 10,          // count per page
        sorting: {
            name: 'asc'     // initial sorting
        }
    }, {
        total: 0,           // length of data
        getData: function($defer, params) {
            // ajax request to api
            Api.get(params.url(), function(data) {
                $timeout(function() {
                    // update table params
                    var orderedData = params.sorting ?
                    $filter('orderBy')(data.result, params.orderBy()) :
                    data.result;
                    orderedData = params.filter ?
                    $filter('filter')(orderedData, params.filter()) :
                    orderedData;

                    $scope.users = orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count());

                    params.total(orderedData.length); // set total for recalc pagination
                    $defer.resolve($scope.users);
                }, 500);
            });
        }
    });

    var inArray = Array.prototype.indexOf ?
    function (val, arr) {
        return arr.indexOf(val)
    } :
    function (val, arr) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === val) return i;
        }
        return -1
    };
$scope.names = function(column) {
    var def = $q.defer(),
        arr = [],
        names = [];
    angular.forEach(data, function(item){
        if (inArray(item.name, arr) === -1) {
            arr.push(item.name);
            names.push({
                'id': item.name,
                'title': item.name
            });
        }
    });
    def.resolve(names);
    return def;
};

Я попробовал несколько попыток добавить дополнительный $q.defer() и обернуть исходные данные с последующей функцией $scope.names, но мое понимание обещания и отсрочки недостаточно сильное, чтобы что-то работало.

На GitHub есть несколько заметок, предполагающих, что это ошибка в ng-table, но я не уверен, так ли это, или я просто делаю что-то глупое.

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

Указатели о том, как действовать, очень ценятся

-Кейн-

Это было полезно?

Решение

У меня была похожая, но немного более сложная проблема.Я хотел иметь возможность динамически обновлять список фильтров, что казалось вполне выполнимым, поскольку в любом случае они должны быть просто в переменной $scope.В принципе, я ожидал, что, если у меня будет $scope.filterOptions = []; тогда я мог бы установить filter-data="filterOptions" и любое обновление этого списка будет автоматически отражено.Я был неправ.

Но я нашел решение, которое, на мой взгляд, довольно хорошее.Во-первых, вам нужно переопределить шаблон фильтра выбора ngTable (если вы не знаете, как это сделать, это предполагает использование $templateCache и ключ, который вам нужно переопределить, это 'ng-table/filters/select.html').

В обычном шаблоне вы найдете что-то вроде этого ng-options="data.id as data.title for data in $column.data" и проблема в том, что $column.data это фиксированное значение, которое не изменится при обновлении $scope.filterOptions.

Мое решение - передать только $scope ключ в качестве данных фильтра вместо передачи всего списка параметров.Итак, вместо filter-data="filterOptions", Я передам filter-data="'filterOptions'" а затем внесите небольшое изменение в шаблон, например: ng-options="data.id as data.title for data in {{$column.data}}".

Очевидно, что это существенное изменение в работе фильтра выбора.В моем случае это было очень маленькое приложение, в котором была только одна таблица, но вы можете быть обеспокоены тем, что подобное изменение нарушит другие ваши выборы.В этом случае вы можете встроить это решение в собственный фильтр вместо того, чтобы просто переопределять «выбрать».

Другие советы

Вы можете достичь этого с помощью пользовательский фильтр:

Тот Самый код для стандартного фильтра выбора в ngtable указано:

<select ng-options="data.id as data.title for data in column.data"
    ng-model="params.filter()[name]"
    ng-show="filter == 'select'"
    class="filter filter-select form-control" name="{{column.filterName}}">
</select>

Когда вы вызываете эти данные, вы передаете: filter-data="names($column)" а ngtable позаботится о получении данных для вас.Я не знаю, почему это не работает с внешним ресурсом.Держу пари, это как-то связано со столбцом $ и обещанием, как вы указали.

Я быстро нашел обходной путь в своем коде, чтобы избежать этого.Написание моего собственного шаблона выбора фильтра, например:

<select id="filterTest" class="form-control" 
    ng-model="tableParams.filter()['test']" 
    ng-options="e.id as e.title for e in externaldata">
</select>

Вы получаете эти внешние данные в своем контроллере:

$scope.externaldata = Api.query(); // Your custom api call

Это работает отлично, но у меня есть id на моих данных, так что нет необходимости в name функция.

Я понимаю, что это решение не является оптимальным.Давайте посмотрим, напишет ли кто-нибудь здесь больше, чем это "обходное решение", и просветит ли нас.Даже эсвит бывает здесь иногда ;)

Это работает для меня:

html:

<td data-title="'Doc type'" filter="{ 'doc_types': 'select' }" filter-data="docTypes()" sortable="'doc_types'">
    {{task.doc_type}}
</td>
.

angularjs:

$scope.docTypes = function ($scope) 
{
    var def = $q.defer();
    //var docType = [
    //    {'id':'4', 'title':'Whatever 1'},
    //    {'id':'9', 'title':'Whatever 2'},
    //    {'id':'11', 'title':'Whatever 3'}
    //];

    // Or get data from API.
    // Format should be same as above.
    var docType = $http.get('http://whatever.dev', {
        params: { param1: data1 }
    });

    //Or with Restangular 
    var docType = Restangular.all('/api/doctype').getList();

    def.resolve(docType);
    return def;
};
.

Как уже упоминалось @ Andión, вы можете достичь с Пользовательский фильтр .

Легко добиться асинхронных данных популяции с обещаниями ( Сервис $ q в угловом), Интересно Энди Статья о обещаниях / em>

Вы можете изменить метод $ SCOPE.names и добавить $ http-сервис, которая возвращает асинхронные данные и разрешает отсроченный объект как:

$scope.names = function(column) {
  var def = $q.defer();

  /* http service is based on $q service */
  $http({
    url: siteurl + "app/application/fetchSomeList",
    method: "POST",

  }).success(function(data) {

    var arr = [],
      names = [];

    angular.forEach(data, function(item) {
      if (inArray(item.name, arr) === -1) {
        arr.push(item.name);
        names.push({
          'id': item.name,
          'title': item.name
        });
      }
    });
    
    /* whenever the data is available it resolves the object*/
    def.resolve(names);

  });

  return def;
};
.

Я столкнулся с аналогичным вопросом, но не хотел сделать дополнительный вызов AJAX, чтобы получить значения фильтра.

Проблема с кодом OP заключается в том, что функция фильтра-данных выполняется до $ Scope.data заполняется.Чтобы обойти это, я использовал угловые $, чтобы слушать изменения на $ Scope.data.Когда-то $ SCOPE.DATA Действительно, фильтр-данные заполнены правильно.

        $scope.names2 = function () {
        var def = $q.defer(),
             arr = [],
                names = [];
        $scope.data = "";
        $scope.$watch('data', function () {


            angular.forEach($scope.data, function (item) {
                if (inArray(item.name, arr) === -1) {
                    arr.push(item.name);
                    names.push({
                        'id': item.name,
                        'title': item.name
                    });
                }
            });

        });
        def.resolve(names);
        return def;
    };
.

Оригинальный палунчик развесел с изменением: http://plnkr.co/edit/sjxvppqr2ziyasyavbqa

Также видишь это настолько вопрос на $ watch: Как использовать $ PATE. $Смотреть и $ Scope. $ Подаются в Angularjs?

Я решил проблему с $ q.defer (), как упомянуто диабло

Тем не менее, тот код на самом деле довольно прост и простой:

в HTML:

<td ... filter-data="countries">
.

в контроллере:

$scope.countries = $q.defer();
$http.get("/getCountries").then(function(resp){
  $scope.countries.resolve(resp.data.data);
})
.

"Во-первых, вам нужно переопределить шаблон фильтра выбора NGTable (если вы не знаете, как это сделать, он включает в себя использование $ Templatecache, и ключ, который вам необходимо переопределить, это« NG-Table / Filters / Select ».HTML '). "

Я добавил переопределенный скрипт под скриптом NG-таблицы, и все работало хорошо ...

<script id="ng-table/filters/select.html" type="text/ng-template">
 <select ng-options="data.id as data.title for data in {{$column.data}}" ng-table-select-filter-ds="$column" ng-disabled="$filterRow.disabled" ng-model="params.filter()[name]" class="filter filter-select form-control" name="{{name}}"> <option style="display:none" value=""></option> </select>
</script>
.

Что я сделал, просто поместите тег выбора со значениями и иметь значение NG-модель возвращает значения для фильтра.

Это было полезно, поскольку мне нужно было перевести простой текст.

<td data-title="'Status'| translate" ng-bind = "("cc_assignment_active"== '0') ? ('Inactive' | translate) : ('Active'| translate)" 
                    filter="{ cc_assignment_active: 'select3'}" >

</td>

<script id="ng-table/filters/select3.html" type="text/ng-template">
<select  class="filter filter-select form-control"  ng-model="params.filter()[name]" name="{{name}}">
    <option active value="" translate>---All---</option>
    <option value="1" translate>Active</option>
    <option value="0" translate>Inactive</option>
</select>
.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top