Angularfire - $scope 在异步调用后不会更新,并且 $apply 抛出错误
Angularfire - $scope wont update after async call, and $apply throws error
我目前正在绞尽脑汁试图更好地理解 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:查看
这是我的观点,我想做的是一旦用户登录,显示属于该用户的 firebase 结构中的一些信息 uid
<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>
您实际上没有使用 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 是一个实时侦听器,这是一种反模式。
Promises 只触发一次,而 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);
});
Angular当您使用 $firebaseObject
或 $firebaseArray
.
时,Fire 会为您解决这个问题
在路由器中使用resolve
注入用户
app.config(["$routeProvider", function($routeProvider) {
$routeProvider.when("/home", {
controller: "HomeCtrl",
templateUrl: "views/home.html",
resolve: {
currentAuth: ["Auth", function(Auth) {
return Auth.$requreAuth();
}]
}
})
我目前正在绞尽脑汁试图更好地理解 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:查看
这是我的观点,我想做的是一旦用户登录,显示属于该用户的 firebase 结构中的一些信息 uid
<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>
您实际上没有使用 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 是一个实时侦听器,这是一种反模式。
Promises 只触发一次,而 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);
});
Angular当您使用 $firebaseObject
或 $firebaseArray
.
在路由器中使用resolve
注入用户
app.config(["$routeProvider", function($routeProvider) {
$routeProvider.when("/home", {
controller: "HomeCtrl",
templateUrl: "views/home.html",
resolve: {
currentAuth: ["Auth", function(Auth) {
return Auth.$requreAuth();
}]
}
})