Firebase,AngularJS和GeoFire - 等待工厂的响应

时间:2014-11-12 22:25:09

标签: javascript angularjs firebase geofire

我开始学习很多与混合应用编码相关的东西。我决定继续使用基于AngularJS的Ionic框架(这让我有机会学习它),并使用Firebase作为我的后端解决方案。

我想在我的应用中设置地图,所以我会找到用户和他周围的一些兴趣点。我发现GeoFire已经存在,所以我开始使用它。

我的问题:我无法显示保存在Firebase中的用户位置。我使用GeoFire查询它,并获得响应(console.log),但它不会更新范围变量。

这是JS代码:

myApp.factory("MyLoc", function(){
    var firebaseRef = new Firebase("https://myfirebase.firebaseio.com/geofire/");
    var geoFire = new GeoFire(firebaseRef);

    /*    geoFire.set("user_loc", [37.785326, -122.405696]).then(function() {
     console.log("Provided key has been added to GeoFire");
     }, function(error) {
     console.log("Error: " + error);
     });*/

     return geoFire.get("user_loc").then(function(location) {
        if (location === null) {
            console.log("Provided key is not in GeoFire");
            return 0;
        }
        else {
            console.log("Provided key has a location of " + location);
            return location;
        }
    }, function(error) {
        console.log("Error: " + error);
        return 0;
    });
})

myApp.controller("firstCtrl", function($scope, MyLoc) {
    $scope.data = {};

    $scope.data.myLoc = MyLoc;
});

HTML只显示它:

{{data.myLoc}}

因为我是Angular N00b,我想我错过了一些明显的东西。我想我应该使用承诺或类似的东西,只是无法弄清楚如何!有谁可以帮助我吗 ? :)

非常感谢兄弟们!

[UPDATE]

好的,这是第一个问题的答案!

MyLoc.then(function(data){
  $scope.data.myLoc = data;

  $scope.$apply();
});

但是我必须使用

$scope.$apply();

要显示HTML,显然它不会立即更新。你知道为什么吗?我理解Angular必须通知它要显示的更改,正如在此非常好地解释的那样:http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

我不明白,为什么会出现这种情况?为什么Angular不知道这种变化?

再次感谢!

1 个答案:

答案 0 :(得分:4)

Firebase的API是异步的,你被咬了。您无法以您尝试的方式从异步调用返回值:

这是您的代码的相关片段:

myApp.factory("MyLoc", function(){
    var firebaseRef = new Firebase("https://myfirebase.firebaseio.com/geofire/");
    var geoFire = new GeoFire(firebaseRef);

     return geoFire.get("user_loc").then(function(location) {
        if (location === null) {
            console.log("Provided key is not in GeoFire");
            return 0;
        }
        else {
            console.log("Provided key has a location of " + location);
            return location;
        }
    }, function(error) {
        console.log("Error: " + error);
        return 0;
    });
})

请注意您传递给then的那些功能?这些被称为回调函数,是Firebase(以及现代Web)的异步操作的关键。如果将这些回调提取到常规的全局命名函数中,那么更容易理解为什么你的构造不起作用:

myApp.factory("MyLoc", function(){
    var firebaseRef = new Firebase("https://myfirebase.firebaseio.com/geofire/");
    var geoFire = new GeoFire(firebaseRef);

     return geoFire.get("user_loc").then(onGeoFireResult, onGeoFireError);
})

function onGeoFireResult(location) {
    if (location === null) {
        console.log("Provided key is not in GeoFire");
        return 0;
    }
    else {
        console.log("Provided key has a location of " + location);
        return location;
    }
}

function onGeoFireError(error) {
    console.log("Error: " + error);
    return 0;
}

当您致电geoFire.get("user_loc")时,它会向服务器执行请求。该请求可能需要一些时间,我们不希望浏览器被阻止。因此,我们不是等待请求完成,而是在服务器响应请求的数据时传入一个要调用的函数。由于事情可能会出错,我们还会传入一个函数,以便在出现错误时调用。

因此,必须认识到onGeoFireResultonGeoFireError的运行时间与调用geoFire.get("user_loc")的时间不同。因此,您无法从封装函数返回结果。相反,你必须处理一个承诺的概念:一种在某些时候会有你所要求的数据的结构。

如果您想在没有AngularFire的情况下继续使用Firebase和Angular,我高度建议您按照此视频教程进行操作:https://www.youtube.com/watch?v=3YVlcFyxBr4。花一个小时左右的时间会在这里为您节省数十个问题。

或者设置两个断点:一个在onGeoFireResult上,另一个在geoFire.get("user_loc")之后的行上,看看哪个断点首先触发。