FirebaseExtended / angularfire

AngularJS bindings for Firebase

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

$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)