3

关于自定义过滤器的时间,我现在非常头疼。我有一个演示(学习 Angular)图库应用程序,我在其中使用带有复选框的自定义过滤器来选择不同类别的照片。

Simptoms:在 ng-repeat 指令上使用自定义过滤器时,我注意到了这个http://screencast.com/t/xPGX1lyTu9Yp ...经过几个小时的调试后,我得出的结论是,问题在于数据来自过滤器运行时我的 JSON 不存在,但没有过滤器,一切似乎都可以加载。

这是这个http://plnkr.co/edit/KbBg67的plnkr (我复制粘贴了代码,修改了一下,还没有工作,早上会修复它,但这是代码)

我开始在我的服务中使用延迟和承诺来获取 JSON 数据,所以我的控制器和其他所有人都会等待数据加载,就像这样 [SERVICES]

angular.module('services', []).factory('getAllPosts', ['$http', '$q', '$timeout',
function($http, $q, $timeout) {
  //defining the promised based API
  var deffered = $q.defer();
  //the getData function
  var getData = function() {
    //defining a empty data array
    var theData = {};
    //using $http to get the dat
    $http.get('/wordpress/api/get_recent_posts/').success(function(data) {
      // prepare data here
      //assigning our data to the theData array.
      // // New stuff here, we are pushing the data one by one to populate the array faster so it isn't emtpy .. ?!
      theData.length = 0;
      for (var i = 0; i < data.length; i++) {
        theData.push(data[i]);
      }
      theData = data;
    });
    //setting a timeout for the data and waiting if necesarry.
    $timeout(function() {
      deffered.resolve(theData);
    }, 1000);
    //when it s done, return the promise... i think so. ?!
    return deffered.promise;
  }
  return {
    //creating a getData handler to use in controllers.
    getData: getData
  };
}])

我的控制器是这样的 [CONTROLLER]

.controller('ListController', ['$scope', 'getAllPosts', 'getCategories', '$location',
    function($scope, getAllPosts, getCategories) {
        $scope.name = 'list';
        getAllPosts.getData().then(function(data) {
            return $scope.posts = data.posts;
        });
        getCategories.get(function(data){
            return $scope.categories = data.categories;
        })
    }
])

我正在使用 getData().then() 在加载时获取它。

我意识到我没有对过滤器说同样的事情 [FILTER]

angular.module('filters', [])
.filter('checkboxFilter', function($filter) {
    return function(post, prefs) {
        var i, j, k, n, out, matchingpost = [];
        // loop through the post
        for (i = 0; i < post.length; i++) {
            console.log('I passed the length ... wtf?')
            out = false;
            n = 0;

            if (prefs) {
                // for each item, loop through the checkboxes categories
                for (j = 0; j < prefs.length; j++) {

                    // for each category, loop through the checkboxes categories of the current category
                    for (k = 0; k < prefs[j].categories.length; k++) {

                        // test if the current item property name is the same as the filter name
                        if (post[i][prefs[j].slug] === prefs[j].categories[k].slug) {

                            // test if checkbox is checked for this property 
                            (prefs[j].categories[k].value) ? n++ : out = true;
                            break;
                        }
                    }
                    if (out) break;
                    // if one filter in each categories is true, add item to the matchingpost
                    if (n === prefs.length) {
                        matchingpost.push(post[i]);
                    }
                }
            }
        }
        return matchingpost;
    }
})

问题是我开始阅读一本有棱有角的书,但我并没有理解很多东西,所以我去亲身体验,慢慢地每一点都到位,但是这个……我已经花了很多时间在这上面。我想如果我再次接受它会更有意义。


问题:我怎样才能摆脱这些错误并让过滤器在加载后读取数据?


附带问题:通过不同的服务,我正在输出我的主干(Wordpress)中的所有现有类别并在复选框中重复它们,我如何将复选框链接到此过滤器的结果?(这对我来说还不是很明显,虽然我已经看到了一些例子......)


附带问题 2:为什么我所有的请求都会成倍增加,就像我发布的第一个屏幕截图一样,不用等待,这就是我正在谈论的部分http://screencast.com/t/lcrWnlioL3u ...我只有 44 个帖子到目前为止,但看起来即使在数据存在之后,过滤器也会再次调用它。

这种行为也发生在其他事情上......我想知道我做错了什么。


注释:我今晚使用的是angular 1.2.0rc1,所有行为都出现在我使用的其他版本中:1.0.7.0、1.1.5。

4

5 回答 5

4

我认为真正做到这一点的方法是覆盖默认值$interpolateProvider以启用返回承诺的过滤器。这样,您可以推迟某些过滤表达式的呈现,直到它们被解决。

但请记住,您不能轻易地对链式过滤器执行此操作。您可能也将被迫重写$parse以启用对 Promise 链接的支持。

我现在面临着同样的问题,因此,我可能会继续这样做。如果是这样,我将确保在我的项目(http://github.com/agileapes/bootstrapui)的 github 存储库上发布指向答案的链接。

编辑

另一种(主要)简单的方法是将任意参数传递给通过 HTTP 调用(或任何其他方式)更新的过滤器:

.controller("MyController", function ($scope, $q, $timeout) {
    $scope.result = null;
    var deferred = $q.defer();
    $timeout(function () {
        $scope.result = [1, 2, 3, 4];
    }, 2000);
});

在这里,我刚刚通过超时更新了结果,但我不必那样做。这只是为了演示。您可以$scope.result选择任何方式进行更新。

这是一个示例过滤器,它只会在结果中包含偶数

.filter('even', function () {
    return function (input) {
        if (!angular.isArray(input)) {
            return input;
        }
        var result = [];
        angular.forEach(input, function (x) {
            if (x % 2 == 0) {
                result.push(x);
            }
        });
        return result;
    }
});

现在,在我看来,我可以这样一起使用它们:

<div ng-controller="MyController">
    <ul ng-if="result.length"> <!-- It is nice to not pollute the DOM with empty lists -->
        <li ng-repeat="item in result | even">{{item}}</li>
    </ul>
</div>

大约几秒钟后,应该填充列表并且ngRepeat指令应该接收过滤结果。

这里的诀窍是,我为该特定result变量进行了一个摘要循环,这意味着正在输入该变量的过滤器也将被重新执行,这反过来意味着一切都将按预期顺利进行。

于 2014-04-30T20:02:15.547 回答
1

无需创建自己的承诺,因为 AngularJS$http服务已经为您完成了这项工作。

所以你的服务可以这样简化:

angular.module('services', []).factory('getAllPosts', ['$http', function($http) {
    return {
        getData: function() {

            // Return the promise that is created by the $http service
            return $http.get('/wordpress/api/get_recent_posts/')
        }
    };
}]);

然后在你的控制器中:

angular.module('yourModule')
.controller('ListController', ['$scope', 'getAllPosts',
    function($scope, getAllPosts) {

        $scope.items = []; // Placeholder for the data

        getAllPosts.getData()
            .success(function(data, status, header, config){

                 // Fill placeholder with data as soon as it is received
                 $scope.items = data;
            });
    }
]);

然后在您的视图中,您可以使用ng-model指令或ng-repeat指令来显示您的数据,如下所示:

<li ng-repeat="item in items">
    {{item.someProperty}}
</li>

如果要应用自定义过滤器,只需使用:

<li ng-repeat="item in items | your_filter">
    {{item.someProperty}}
</li>

AngularJS 会在更新时自动更新视图$scope.items,因此您无需为此编写额外的代码。

希望有帮助!

于 2013-08-17T06:12:14.180 回答
0

对于任何有兴趣的人:

我通过简单地捕获由于未定义对象而引发的错误来解决这个问题,然后我抛出一个错误,例如throw "Object not yet loaded. Trying again...";

app.filter('translate', function() {
        return function(input) {
            try {
                var index = input.toUpperCase();
                if (app.translation[index]) {
                    if (input[0] === index[0])
                        return app.translation[index].capitalize();
                    return app.translation[index];
                }
                return input;
            }
            catch(err) {
                 throw "Translation not loaded yet, trying again...";
            }
        };
    })
app.run(function($http) {
        $http.get("translations/" + "NB_ENG" + ".json", {
                cache: true
            }).success(function(data) {         
                app.translation = data;
                console.info("Translation loaded.");
            }).error(function(q,w,e) {
                app.translation = 1;
                console.error("Failed getting translation: "+q + " | " + w + " | " + e + " | ");
            });
    });

我在这里等待翻译文件,然后才能翻译所有单词。Angular 一次又一次地运行过滤器,直到它得到一个有效的返回。

于 2015-04-22T15:12:33.510 回答
0
//defining a empty data array
var theData = {};

这是一个对象定义,而不是数组定义。尝试将其替换为

var theData = [];

它可能会解决您的问题。

于 2013-08-15T23:58:05.773 回答
0

似乎——虽然相当昂贵——解决这个问题的方法是循环直到信息到达。- 欢迎任何其他解决方案。

像这样:

.filter('checkboxFilter', function() {
    return function(post, prefs) {
        //define empty vars, strict mode
        var i, j, k, n, out, matchingpost = [];
        //check to see if the posts have arrived
        if (post == undefined) {
            //posts are not here yet            
        } else {
            //posts have arrived.
            //iterate through all the posts
            for (i = 0; i < post.length; i++) {
                out = false;
                n = 0;
                var eachPostCategories = post[i].categories;
                //console.log(eachPostCategories)
                for (j = 0; j < eachPostCategories.length; j++) {
                    //console.log(' post categories are');
                    //console.log(eachPostCategories[j]);
                    if (prefs == undefined) {
                        //preferences aren't in yet
                    } else {
                        //preferences are here.
                        //now we should iterate through all the preferences and compare them to the posts.
                        for (k = 0; k < prefs.length; k++) {
                            //prefs[j].slug
                            //console.log('categories are');
                            //console.log(prefs[k].slug)
                            if (eachPostCategories[j].slug === prefs[k].slug) {
                                //console.log('this post has the categories');
                                //console.log(post[i]);

                                //WHAT DOES THIS LINE MEAN?
                                /*(prefs[k].slug) ? n++ : out = true;
                                break;*/
                                n++
                            } else {
                                out = true;
                                break;
                            }
                            console.log('n is ' + n);
                        }
                        if (out) break;
                        // if one filter in each categories is true, add item to the matchingpost
                        if (n === prefs.length) {
                            matchingpost.push(post[i]);
                        }

                    }
                }

            }
        }
        if (matchingpost.length == 0) {
            //return matchingpost = post
        } else {
            return matchingpost;
        }

    }
})

过滤器还不完善,每个对象都是重复的,我可以根据它们的类别隐藏元素,但我不能让它们显示出来:|

我似乎没有掌握这个概念,如果有人可以给我一些指示,那就太棒了。

一般来说,我正在比较一个对象,例如:

    debug: [{"id":2,"slug":"architecture","title":"Architecture","description":"","parent":0,"post_count":16,"selected":"true"},{"id":13,"slug":"day","title":"Day","description":"","parent":0,"post_count":37,"selected":"true"},{"id":7,"slug":"funny","title":"Funny","description":"","parent":0,"post_count":6,"selected":"true"},{"id":4,"slug":"landscape","title":"Landscape","description":"","parent":0,"post_count":12,"selected":"true"},{"id":11,"slug":"nature","title":"Nature","description":"","parent":0,"post_count":3,"selected":"true"},{"id":12,"slug":"night","title":"Night","description":"","parent":0,"post_count":8,"selected":"true"},{"id":10,"slug":"old","title":"Old","description":"","parent":0,"post_count":3,"selected":"true"},{"id":3,"slug":"people","title":"People","description":"","parent":0,"post_count":15,"selected":"true"},{"id":9,"slug":"rooftops","title":"Rooftops","description":"","parent":0,"post_count":2,"selected":"true"},{"id":8,"slug":"weird","title":"Weird","description":"","parent":0,"post_count":1,"selected":"true"}]

发布具有多个类别的对象(例如,发布 Zoomed Houses 有 Day、Architecture 类别)

于 2013-08-18T15:00:25.077 回答