$loaded() object does not $destroy() correctly
EvanLomas opened this issue · comments
Angular: 1.61
Firebase: 3.6.9
AngularFire: 2.3.0
angular-ui-router: 0.4.2
Chrome Browser 55.0.2883.95 (64-bit)
In ui-router, it is recommended we use this in our resolve functions to force states to not render until firebase resources are connected and loaded (to avoid screen flicker):
resolve: {
data: function($firebaseArray, ref) {
$firebaseArray(ref).$loaded();
}
}
We then use this promised object during a scope's $destroy() event:
$scope.$on('$destroy', function() {
data.$destroy();
});
However this does not disconnect the resource correctly, if you then immediately log the user out of firebase on the next state you get this error:
Error: permission_denied at {{ REF_PATH }}: Client doesn't have permission to access the desired data.
To prove this is a bug caused by the $loaded() function, we can create 2x resolve objects in our state like such, and this works as expected:
resolve: {
data_array: function($firebaseArray, ref) {
$firebaseArray(ref);
},
data: function(data_array) {
return data_array.$loaded()
},
}
$scope.$on('$destroy', function() {
data_array.$destroy();
});
Does this mean $loaded() promise does not return the original $firbaseArray() object as we'd expect? Is it a duplicate?
update
Although the use case explained above did work as an initial work around several times, immediately after posting this that too failed in the same way when a user was re-logged in and logged out for a second time in the same session.
This could potentially be a bug, but I'm not sure the repro is totally valid. Do you mind putting together a Plunker or JSFiddle which exhibits this behavior? One thing that is missing that I would expect is a return
in front of $firebaseArray(ref)
. If you don't return it, then resolve()
won't actually wait for the promise to finish:
resolve: {
data: function($firebaseArray, ref) {
return $firebaseArray(ref).$loaded();
}
}
That still might cause a repro, but looking at the code for $destroy()
, it definitely looks like it is doing the right thing.
@katowulf might have some more thoughts here.
Agreed, need a detailed repro here as this will be highly nuanced. Could be a coding bug, also might also be related to how routing works.
What happens, for example, if you double-click a route? Does it start the resolve method, then abort it and start another route? It could be getting into a strange state if that is the case.
Seems like $destroy is handled in all the places, with the possible exception of the unbind method, which I'd expect to be calling $destroy(), given that $scope.$on('$destroy', ...) was invoked (looks like someone refactored this since I worked on it). We should probably evaluate that separate from this issue.
@katowulf Thank you, you were on the right track. There was a very obscure bug in ui-router causing my resolve to get triggered twice on a second login when automatically jumping down to a child state even though the controller was only being triggered once (and therefor only one $destroy during cleanup)