javascript 如何从异步调用填充 ng-table 上的选择过滤器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24019769/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 02:00:44  来源:igfitidea点击:

How to populate select filters on ng-table from async call

javascriptangularjsngtable

提问by Kaine

tl:dr

电话:博士

How can I populate an ng-table including 'select' filters using ajax/json?

如何使用 ajax/json 填充包含“选择”过滤器的 ng 表?

Plunk showing the problem: http://plnkr.co/Zn09LV

Plunk 显示问题:http://plnkr.co/Zn09LV



Detail

细节

I am trying to get to grips with AngualrJS and the ng-table extension and although I can get some nice tables with working filters and such when I'm using static data defined in the javascript - once I get to trying to load real data into the table I hit a snag.

我正在尝试使用 AngualrJS 和 ng-table 扩展,尽管我可以得到一些带有工作过滤器的漂亮表格,例如当我使用 javascript 中定义的静态数据时 - 一旦我开始尝试将真实数据加载到我在桌子上碰到了一个障碍。

The main body of the ng-table is populated correctly and as long as I only use the text filter everthing seems to be working:

ng-table 的主体被正确填充,只要我只使用文本过滤器,一切似乎都在工作:

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

Works just fine.

工作得很好。

However, if I update this to use the select filter:

但是,如果我更新它以使用选择过滤器:

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

I run into a syncronisation issue in that the Names variable is always evaluated before the data has returned from the server. (Possibly the Names varibale is evaluated before the request to the server is even sent.) This means I get an empty list for the filter.

我遇到了一个同步问题,因为 Names 变量总是在数据从服务器返回之前被评估。(可能在向服务器发送请求之前评估 Names 变量。)这意味着我得到了一个空的过滤器列表。

Once the data returns from the server - I can't seem to find a way of updating the select filter. Re-running the code that creates the filter list initially seems to have no effect - I'm not sure how to trigger the ng-table to re-check its filters so the updated variable isn't read. I also can't figure out a way to postpone the evaluation of the variable until after the async call is complete.

一旦数据从服务器返回 - 我似乎找不到更新选择过滤器的方法。重新运行最初创建过滤器列表的代码似乎没有效果 - 我不确定如何触发 ng-table 重新检查其过滤器,以便不会读取更新的变量。我也想不出一种方法来推迟对变量的评估,直到异步调用完成之后。

For my javascript I have pretty much used the example ajax code from the ng-table GitHub page and added onto it the example code for the select filter.

对于我的 javascript,我几乎使用了 ng-table GitHub 页面中的示例 ajax 代码,并将选择过滤器的示例代码添加到其中。

    $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;
};

I've tried a few attempts at adding on an additional $q.defer() and wrapping the initial data get followed by the $scope.names function - but my understanding of promise and defer isn't strong enough to get anything working.

我已经尝试了一些尝试添加一个额外的 $q.defer() 并包装初始数据 get 后跟 $scope.names 函数 - 但我对 promise 和 defer 的理解还不够强,无法让任何事情正常工作。

There are a few notes on GitHub suggesting this is a bug in ng-table, but I'm not sure if that's the case or I'm just doing something daft.

GitHub 上有一些注释表明这是 ng-table 中的一个错误,但我不确定是不是这种情况,或者我只是在做一些愚蠢的事情。

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

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

Pointers on how to procede greatly appreciated

非常感谢有关如何进行的指示

-Kaine-

-凯恩-

采纳答案by Okonomiyaki3000

I had a similar but slightly more complex issue. I wanted to be able to update the list of filters dynamically which seemed totally doable since they should just in a $scope variable anyway. Basically, I expected that, if I have $scope.filterOptions = [];then I could set filter-data="filterOptions"and any update to that list would be automatically reflected. I was wrong.

我有一个类似但稍微复杂的问题。我希望能够动态更新过滤器列表,这似乎完全可行,因为无论如何它们都应该放在 $scope 变量中。基本上,我预计,如果我有$scope.filterOptions = [];那么我可以设置filter-data="filterOptions"并且对该列表的任何更新都会自动反映。我错了。

But I found a solution that I think is pretty good. First, you need to override the ngTable select filter template (in case you don't know how to do this, it involves using $templateCacheand the key you need to override is 'ng-table/filters/select.html').

但是我找到了一个我认为非常好的解决方案。首先,您需要覆盖 ngTable 选择过滤器模板(如果您不知道如何执行此操作,则涉及 using$templateCache并且您需要覆盖的键是'ng-table/filters/select.html')。

In the normal template, you'll find something like this ng-options="data.id as data.title for data in $column.data"and the problem with that is that $column.datais a fixed value that won't change when we update $scope.filterOptions.

在普通模板中,您会发现类似这样的内容ng-options="data.id as data.title for data in $column.data",而问题在于它$column.data是一个固定值,在我们更新时不会改变$scope.filterOptions

My solution is to pass only the $scope keyas filter-data instead of passing the whole list of options. So, instead of filter-data="filterOptions", I'll pass filter-data="'filterOptions'"and then, put a small change in the template like: ng-options="data.id as data.title for data in {{$column.data}}".

我的解决方案是仅传递 $scope作为过滤器数据,而不是传递整个选项列表。因此,filter-data="filterOptions"我将通过,而不是,filter-data="'filterOptions'"然后在模板中进行一些小的更改,例如:ng-options="data.id as data.title for data in {{$column.data}}"

Obviously, this is a significant change to how the select filter works. In my case, it was for a very small app that only had one table but you may be concerned that a change like this will break your other selects. If that's the case, you may want to build this solution into a custom filter instead of just overriding 'select'.

显然,这是对选择过滤器工作方式的重大改变。就我而言,这是一个只有一张桌子的非常小的应用程序,但您可能担心这样的更改会破坏您的其他选择。如果是这种情况,您可能希望将此解决方案构建到自定义过滤器中,而不仅仅是覆盖“选择”。

回答by Andión

You can achieve that with a custom filter:

您可以使用自定义过滤器来实现:

The codefor the standard select filter on ngtable says:

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>

When you call this data you pass: filter-data="names($column)"and ngtable takes care of getting the data for you. I don't know why this does not work with an external resource. I bet it has something to do with the $column and the promise, as you pointed out.

当您调用此数据时,您会通过:filter-data="names($column)"ngtable 负责为您获取数据。我不知道为什么这不适用于外部资源。正如您所指出的,我敢打赌它与 $column 和 promise 有关。

I did a quick workaround in my code to avoid that. Writing my own select filter template like:

我在我的代码中做了一个快速的解决方法来避免这种情况。编写我自己的选择过滤器模板,如:

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

You fetch this externaldata in your controller:

您在控制器中获取此外部数据:

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

It works perfectly, but I do have an idon my data, so no need of the namefunction.

它工作得很好,但id我的数据上确实有一个,所以不需要这个name功能。

I understand this solution is not optimal. Let's see if somebody writes here more than this 'workaround' and enlightens us. Even esvitis here sometimes ;)

我知道这个解决方案不是最佳的。让我们看看是否有人在这里写的不仅仅是这个“解决方法”并启发了我们。有时甚至esvit 也在这里 ;)

回答by Diablo

This works for me:

这对我有用:

HTML:

HTML:

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

AngularJS:

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;
};

回答by A-Ali

As mentioned by @Andión You can achieve with custom filter.

正如@Andión 所提到的,您可以使用自定义过滤器来实现。

It is easy to achieve Asynchronous data population with Promises (the $q service in Angular), interesting Andy Article about Promises

使用 Promises(Angular 中的 $q 服务)很容易实现异步数据填充,关于 Promises 的有趣Andy 文章

You can amend the $scope.names method and add $http service that return the asynchronous data and resolve the deferred object as:

您可以修改 $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;
};

回答by user22196

I encountered a similar issue but did not want to make the additional AJAX call to get the filter values.

我遇到了类似的问题,但不想进行额外的 AJAX 调用来获取过滤器值。

The issue with the OP's code is that filter-data function executes before $scope.data is populated. To get around this I used the Angular $watch to listen for changes on $scope.data. Once $scope.data is valid the filter-data is populated correctly.

OP 代码的问题在于 filter-data 函数在填充 $scope.data 之前执行。为了解决这个问题,我使用了 Angular $watch 来监听 $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;
    };

The original plunk forked with the change: http://plnkr.co/edit/SJXvpPQR2ZiYaSYavbQA

原始 plunk 与更改分叉:http: //plnkr.co/edit/SJXvpPQR2ZiYaSYavbQA

Also see this SO Question on $watch: How do I use $scope.$watch and $scope.$apply in AngularJS?

另请参阅 $watch 上的这个 SO 问题:How do I use $scope.$watch and $scope.$apply in AngularJS?

回答by Jianwu Chen

I solved the issue with $q.defer() as mentioned by Diablo

我用暗黑破坏神提到的 $q.defer() 解决了这个问题

However, the is code actually pretty simple and straightforward:

然而,代码实际上非常简单明了:

in HTML:

在 HTML 中:

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

in controller:

在控制器中:

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

回答by Tang Thanh Tam

"First, you need to override the ngTable select filter template (in case you don't know how to do this, it involves using $templateCache and the key you need to override is 'ng-table/filters/select.html')."

“首先,您需要覆盖 ngTable 选择过滤器模板(如果您不知道如何执行此操作,则需要使用 $templateCache 并且您需要覆盖的键是 'ng-table/filters/select.html') .”

I added the overrided script below the script of ng-table and everything worked well...

我在 ng-table 的脚本下面添加了覆盖的脚本,一切运行良好......

<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>

回答by Semko

What I did is just put the select tag with values and have the ng-model return the values for the filter.

我所做的只是将带有值的 select 标记放入并让 ng-model 返回过滤器的值。

This was helpful since I needed to translate the plain text.

这很有帮助,因为我需要翻译纯文本。

<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>