AngularJS承诺并使用自定义过滤器计时

时间:2013-08-15 23:36:20

标签: angularjs filter timing promise

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

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

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

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

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个帖子,但看起来即使数据存在,过滤器也会再次调用它。

这种行为也与其他事情有关......我想知道我做错了什么。


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

5 个答案:

答案 0 :(得分:4)

我认为实现此目的的真正方法是覆盖默认的$interpolateProvider以启用返回promises的过滤器。这样,您可以推迟渲染某些已过滤的表达式,直到它们被解析。

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

我现在面临着同样的问题,因此,我可以继续前进而去做。如果是这样,我将确保在我的项目(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变量进行了摘要循环,这意味着正在为该变量提供的过滤器也将被重新执行,这反过来意味着一切都会按预期顺利进行。

答案 1 :(得分: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>

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

希望有所帮助!

答案 2 :(得分:0)

//defining a empty data array
var theData = {};

这是一个对象定义,而不是数组定义。尝试用

替换它
var theData = [];

它可能会解决您的问题。

答案 3 :(得分: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类别)

答案 4 :(得分: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一次又一次地运行过滤器,直到它获得有效的返回。