AngularJS $http等待响应

6

我是javascript/angularjs的新手。 我想在某些元素上执行鼠标悬停时显示bootstrap弹出框。

我为此创建了一个指令。

(function(angular, app) {
  app.directive('popOver',["$window","$http",function($window,$http){
    return function(scope,elem,attrs){
      elem.on('mouseover',function(){
        console.log('mouseover');
        var data = scope.$apply(attrs.popOver);
        console.log('in directive again');
        console.log(data);
      });
    };
  }]);
})(angular, app);

指令的值是一个函数

<span class="asdf" pop-over="getVCard(id)">name</span>

函数vcard(id)在我的angularjs控制器中定义。它检查本地存储中是否已经存在数据,如果存在,则返回数据。否则,它会进行$http get请求并将数据存储在本地存储中。

$scope.getVCard = function(id){

  var vcardKey = vcardKeyPrefix+id;

  var vCardFromLS = localStorageService.get(vCardKey);
  if(vCardFromLS){
    return localStorageService.get(vCardKey);
  }

  $http.get(app.common.vcardUrl(id)).
    success(function(data){
      localStorageService.set(vCardKey,data);
    }).
    error(function(error){
      F1.common.error(data,function(){});
    });

    return localStorageService.get(vCardKey);
};

由于$http返回的是promise对象,所以我在指令中得到了undefined值。 如何确保函数getVCard同步返回?

通过谷歌搜索,我阅读了许多结果。 但是建议使用回调函数。 但是我不确定回调函数如何使其同步。 如果有任何帮助,将不胜感激。

更新 1(响应Foo L的评论) 我计划在多个地方使用相同的指令。 Vcard和产品信息等弹出窗口。唯一不同的是传递给弹出窗口指令的参数。 这样指令只需关心显示弹出窗口。 获取数据的位置将在传递给指令的单独函数中。


将函数和ID分别传递到指令中如何?您可以在内部使用ID作为参数调用该函数,并设置一个scope.$watch,当它成功返回时触发。 - Foo L
1
我计划在多个位置使用相同的指令。例如用于名片和产品信息等的弹出窗口。唯一不同的是弹出指令的参数。这样,指令只需关注显示弹出窗口本身即可。获取数据的方法将在传递给指令的单独函数中进行。 - Anirudhan J
使用 ui-bootstrap - Stewie
@Stewie,你能给我一个例子吗?我不清楚我们如何使用ui-bootstrap来实现这个。 - Anirudhan J
1个回答

10

你无法强制$http.get()同步执行。由于$http.get()不可避免地是异步的,所以你只能返回一个值的Promise而不能返回值本身。

并且,因为当调用$http.get()时需要这样做,你还必须在另一种情况下返回一个Promise——当成功从本地存储中检索vCardFromLS时。这确保了任何对$scope.getVCard()的调用返回的对象都具有.then()方法,即它是一个Promise,无论是否调用$http.get()

因此,代码应该像这样:

$scope.getVCard = function(id) {
    var vcardKey = vcardKeyPrefix + id;
    var vCardFromLS = localStorageService.get(vCardKey);
    var dfrd = $q.defer();
    if(vCardFromLS) {
        dfrd.resolve(localStorageService.get(vCardKey));
    }
    else {
        $http.get(app.common.vcardUrl(id)).success(function(data) {
            localStorageService.add(vCardKey, data);//.add() rather than .set() ?
            dfrd.resolve(data);
        }).error(function(error) {
            F1.common.error(data, function() {
                dfrd.reject('$http.get(app.common.vcardUrl(id)) failed');
            });
        });
    }
    return dfrd.promise;
};

现在你需要确保对$scope.getVCard()的响应被适当地处理,例如:

$scope.getVCard(id).then(function(data) {
    //success - do whatever is required with data
}, function(reason) {
    //failure - log and/or display `reason` as an error message
})

编辑:

我的“...在另一种情况下,你必须返回一个promise…”说得过于绝对了。

我应该说,“...为了简单起见,在另一种情况下,你可以返回一个promise…”。

另一个可能性是根据返回的是Promise还是另一种类型的对象/值进行分支。然而,这样做会很混乱,并且通常会导致代码重复。


太棒了!我从未想过返回一个Promise,无论数据是否在LocalStorage中可用。 - Spock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接