【问题标题】:Angularfire - $scope wont update after async call, and $apply throws errorAngularfire - $scope 在异步调用后不会更新,并且 $apply 抛出错误
【发布时间】:2016-03-29 22:56:35
【问题描述】:

我目前正在努力尝试更好地理解 Angularfire 在这种情况下是如何工作的。我的“webapp/test”上有 2 个控制器,一个用于用户登录,另一个用于显示一些个人资料信息。用户使用 Facebook 登录后,如果用户存在,我使用“uid”检查 firebase 数据库,并检索一些保存在数据库中的信息。

现在的问题是,如果由于数据库读取调用是异步的,我无法获取要在视图上更新的信息,并且使用 $scope.$apply() 仅在我不更改页面时才有效,一旦我点击了正在使用的页面另一个控制器,我得到以下信息:

Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.15/$rootScope/inprog?p0=%24digest

控制器

app.controller("profile", function($scope, loginService, Auth, userManagement) {
    var authData = Auth.$getAuth();
    if (authData != null) {
        $scope.authData = authData;
        userManagement.saveLastLogin(authData);
        userManagement.userDatabaseRead(authData).done(function(userDatabase) {
                $scope.userDatabase = userDatabase;
                $scope.$apply();
        });
    };
    $scope.logoutFB = function() {
        loginService.logoutUser();
        Auth.$onAuth(function(authData) {
            $scope.authData = authData;
        });
    };
})

工厂

app.factory("userManagement",function(FirebaseUrl) {
    return {
        saveLastLogin: function(authData) {
            var userId = authData.uid;
                var ref = new Firebase(FirebaseUrl);
                var users = ref.child("users");
                var timestamp = Date.now();
                users.child(authData.uid).update({
                    last_activity: timestamp,
                });
                console.log('user ' + userId + ' last login was on' + timestamp);
        },
        userDatabaseRead: function(authData) {
            var ref = new Firebase(FirebaseUrl+"users/"+authData.uid);
            var data, def = $.Deferred();
            ref.on("value", function(snapshot) {
                var userDatabase = snapshot.val();
                def.resolve(userDatabase);
            }, function (errorObject) {
              console.log("The read failed: " + errorObject.code);
            });
            return def.promise();
        },
    }
});

更新 1:查看

这是我的观点,我想做的是一旦用户登录,显示属于该用户uid的firebase结构中的一些信息

<div class="jumbotron">
  <h1>Profile View</h1>
  <p ng-show="authData">Welcome {{ authData.facebook.cachedUserProfile.first_name }}</p>
  <p ng-show="authData">{{userDatabase.last_activity | date:'yyyy-MM-dd HH:mm:ss Z'}}</p>
  <button ng-click="logoutFB()" ng-show="authData" class="btn btn-danger facebook-login">Logout</button>
</div>

【问题讨论】:

  • 您能否添加视图代码以便更好地了解您要执行的操作?
  • @AndréKool 嗨安德烈!刚刚更新了视图代码和简要说明,谢谢!
  • 你为什么还要使用$scope.$apply()?您似乎没有在角度范围之外更新范围值,如果我错了,请更正
  • @rahilwazir 是的,我实际上更新了用户在另一段代码上的最后一个活动,并且它的回调将新数据带到了它不反映在视图上的范围内。尽管如此,就像我怀疑我对代码的结构有更大的问题(根据大卫的帖子)
  • 另一种替代方法是不使用$apply,而是使用$timeout 将$scope 更新推迟到下一个摘要周期。这为我创造了奇迹

标签: javascript angularjs firebase angularfire


【解决方案1】:

您实际上并没有使用AngularFire,只是使用了 Firebase SDK。

Angular 不知道 Firebase SDK,因此您需要使用 AngularFire 来为您触发 $digest 循环。

要包含 AngularFire,请添加 JavaScript 文件,然后将其添加到依赖项数组中。

angular.module('app', ['firebase'])
  .constant('FirebaseUrl', '<my-firebase-app>')
  .service('rootRef', ['FirebaseUrl', Firebase])
  .factory('userManagement', UserManagement)
  .controller('MyCtrl', MyController);

function UserManagement($firebaseObject, rootRef) {
  return {
    saveLastLogin: saveLastLogin,
    userDataseRead: userDatabaseRead
  };

  function userDataseRead(authData) {
    var userRef = rootRef.child('users').child(authData.uid);
     // return a $firebaseObject with the ref, don't create a listener
    return $firebaseObject(userRef);
  }

  function saveLasLogin(authData) {
     // this code is good because it doesn't do any listening,
     // just updates to the server
  }
}

function MyController($scope, userManagement) {
  var authData = Auth.$getAuth();
  $scope.userDatabase = userManagement(authData);
}

原始代码中需要注意的一些事项。

您使用的 Promise 是一个实时监听器,这是一种反模式。

Promise 只触发一次,而 Firebase 数据库侦听器可以触发多次。

ref.on("value", function(snapshot) {
  var userDatabase = snapshot.val();
  def.resolve(userDatabase); // don't do this
 }, function (errorObject) {
  console.log("The read failed: " + errorObject.code);
});

当您使用 $firebaseObject 或 $firebaseArray 时,AngularFire 会为您解决这个问题。

在路由器中使用resolve注入用户

Rather than grab the user in the controller, you can ensure that if the user is authenticated they will be injected in the controller:

app.config(["$routeProvider", function($routeProvider) {
$routeProvider.when("/home", {
  controller: "HomeCtrl",
  templateUrl: "views/home.html",
  resolve: {
    currentAuth: ["Auth", function(Auth) {
      return Auth.$requreAuth();
    }]
  }
})

【讨论】:

  • 使用resolve 很棒。我真的让生活更轻松。你可以解决任何承诺。因此,您也可以使用 $loaded() 函数解析 $firebaseObjects 和 $firebaseArrays。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 2019-07-03
  • 1970-01-01
相关资源
最近更新 更多