Angular中Firestore回调修改ng-include路径需两次点击的问题
$scope.filePath in Firestore's async callback require two clicks to update ng-include? Great question—this is a super common gotcha with AngularJS and external async operations like Firestore's callbacks. Let's break down what's happening and how to fix it:
The Root Cause
AngularJS relies on its digest cycle (a "dirty checking" mechanism) to detect changes to $scope values and update the view automatically. This cycle only gets triggered automatically for Angular-native async operations: things like ng-click, $http requests, or $timeout.
Firestore's .get().then() callback runs outside of Angular's context—it's a browser-level async operation. When you update $scope.filePath inside this callback, Angular has no idea the value changed.
- On your first click: The callback runs and updates
$scope.filePath, but the view doesn't refresh because no digest cycle was triggered. - On your second click: The
ng-clickfires, which does trigger a digest cycle. Angular finally notices the updated$scope.filePathand reloads theng-includecontent.
Also, quick note on your existing code: Your querySnapshot.forEach logic won't catch non-existent clients. If the client ID doesn't exist, querySnapshot will be empty, so the forEach loop won't run at all. You should check querySnapshot.empty instead.
Fixes to Try
1. Manually Trigger the Digest Cycle with $scope.$apply()
The simplest fix is to call $scope.$apply() right after updating your $scope value. This forces Angular to run its dirty checking and update the view:
$scope.searchClient = function() { var id = document.getElementById('client_id').value; db.collection("clients") .where("c_id","==",id) .get() .then(function(querySnapshot) { // Correct check for non-existent client if (querySnapshot.empty) { console.log("Client Doesn't Exist"); $scope.filePath = "createClient.htm"; // Force Angular to detect the change $scope.$apply(); } }); };
2. Wrap Firestore's Promise with Angular's $q
If you prefer a more "Angular-native" approach, wrap the Firestore promise in Angular's $q service. This integrates the async operation into Angular's context, so the digest cycle will trigger automatically:
// Make sure to inject $q into your controller first $scope.searchClient = function() { var id = document.getElementById('client_id').value; $q(function(resolve, reject) { db.collection("clients") .where("c_id","==",id) .get() .then(resolve) .catch(reject); }).then(function(querySnapshot) { if (querySnapshot.empty) { console.log("Client Doesn't Exist"); $scope.filePath = "createClient.htm"; } }); };
3. Use AngularFire (Recommended for Ongoing Firestore Work)
If you're working with Firestore regularly in your AngularJS app, use the official AngularFire library. It's built to handle Firestore async operations seamlessly with Angular's digest cycle, so you won't have to manually trigger updates:
// Inject $firebaseFirestore into your controller first $scope.searchClient = function() { var id = document.getElementById('client_id').value; $firebaseFirestore.collection("clients") .where("c_id","==",id) .get() .then(function(querySnapshot) { if (querySnapshot.empty) { console.log("Client Doesn't Exist"); $scope.filePath = "createClient.htm"; } }); };
Any of these solutions will fix the "two clicks" issue by ensuring Angular knows about the $scope change immediately.
内容的提问来源于stack exchange,提问作者Waleed Arshad

