如何使用异步数据

时间:2015-07-15 07:40:08

标签: javascript json angularjs angularjs-directive angularjs-controller

我有一个控制器,它获取名为content.json的json数据。当我渲染UI时,我无法知道json是否可用。控制器加载时,控制器会尽快获取json数据,它是整个应用程序中用于各种目的的对象数组:

vg.ctrls.controller('vg.ctrls.getContentJson', ['$scope', '$http', 
  function($scope, $http) {
    $http.get('models/content.json').success(function(data) {
      if(data instanceof Array){
        vg.content = data; // store in app global variable
      } else {
        vg.log('vg.ctrls.getContentJson: data not array.');
      }
      $scope.vg = vg;
      // vg.Content.createMenu(); // Document may not be ready yet.
    });
  }]
);

在我的UI中,我有一个基于content.json的导航菜单。显然是在<ul>中使用ng-repeat的好地方:

<li ng-repeat="entry in content">
  <a ng-click="vg.Content.setById(entry.id)">{{entry.title}}</a>
</li>

不幸的是,在AngularJS呈现指令时,content.json并不总是被加载。所以,我在我的应用程序中创建了一个函数vg.Content.createMenu(),该函数生成菜单并将其放在$ http.get()的回调中 - 但是,此时文档并不总是准备就绪。

换句话说,我有两个异步进程和一个依赖于它们的函数,但是我不知道哪个将首先完成。我之前没有使用我自己的自定义事件的框架解决了类似的问题。但是,我想相信在AngularJS中有一种处理方法吗?

创建一个在content.json加载之前不会执行的自定义指令怎么样?

---------------------编辑-----------------------

这是一个中间解决方案:

vg.directives.directive('vgContentMenuByCategory', function(){
  return {
    scope: {
      categoryId: '='
      ,title: '='
      ,content: '='
    }
    ,replace: true
    ,link: function(scope, element, attrs){
      scope.$watch('content', function(content){
        var $ul = element.find('ul.dropdown-menu');
        for (var i = 0; i < content.length; i++) {
          var entry = content[i];
          if(entry.category===scope.categoryId){
            var $li = $('<li><a>'+entry.title+'</a></li>');
            _goToContentClickHandler({$element:$li, id:entry.id});
            $ul.append($li);
          }
        };
      });
    }
    , templateUrl: 'templates/vgContentMenuByCategory.html'
  }
});

这不是真正的&#34;正确的&#34;解决方案,因为我没有使用&#34; ng-repeat&#34;在我的模板中但是在范围内的循环中这样做。$ watch()。如果我在模板中使用ng-repeat,那么&#39;内容&#39;还没有加载。问题进一步复杂,因为我实际上想要在另一个运行类别的指令中使用上面的指令 - 而且还必须等待content.json加载。

那么,在加载值之前,如何使指令等待渲染模板或在其中执行任何指令?

2 个答案:

答案 0 :(得分:0)

创建内容工厂:

app.factory('Content',function(){ 
            var category; 
            var title; 
            var id;
            return {
                     category:function(){return category;},
                     title:function(){return title;},
                     id:function(){return id;},
};

然后在你的app.js中定义一个带有get请求的函数来检索内容并填充app.js范围内的Content对象。最后,当路由到您的控制器时,将此功能添加到解析列表中。 定义控制器时,将参数扩展为

vg.ctrls.controller('vg.ctrls.getContentJson', ['$scope', '$http', 
  function($scope, $http,Content) {

访问Content对象。

答案 1 :(得分:0)

好的,我明白了 - 感谢您的意见和答案以及本教程:https://youtu.be/rHmk0UhJSb4

首先,我创建一个获取数据并通过方法填充数据的服务:

vg.ctrls.service('vg.ctrls.contentService', ['$http', '$q',
  function($http, $q) {
    var deferred = $q.defer();

    $http.get('models/content.json').then(function(data) {
      deferred.resolve(data);
    });

    this.getContent = function(){
      return deferred.promise;
    };

  }]
);

然后,我创建一个注入服务并将数据放入范围的控制器:

vg.ctrls.controller('vg.ctrls.content', ["$scope", "vg.ctrls.contentService", 
  function($scope, service){
    var promise = service.getContent();
    promise.then(function(o){
      $scope.content = o.data;
    });
}]);

随后,当数据加载时,将更新该控制器范围内的指令,如:

<div class="well" id="test" ng-controller="vg.ctrls.testService">
  <span ng-repeat="entry in content">
    <p>{{entry.id}}: {{entry.title}}</p>
  </span>
</div>

或者在这样的指令中:

vg.directives.directive('vgContentMenuAllCategories', function(){
  return {
      scope: {
        content: '='
      }
    , controller: 'vg.ctrls.content'
    , link: function(scope, element, attrs){
        //vg.log('vgContentMenuAllCategories');
    }
    , template: 
      '<span ng-repeat="entry in content"><a ng-click="vg.Content.setById(entry.id)">{{entry.title}}</a><br/></span>'
  }
});

为了确保数据在以后到达时有效,我修改了服务以便在5秒后获取数据 - 当数据到达时指令确实会更新:

var foo = function(){
  $http.get('models/content.json').then(function(data) {
    deferred.resolve(data);
  });
};
setTimeout(foo, 5000);