Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Error: $apply already in progress" when using FireBaseAuthClient with AngularJS

I'm trying to record the logged in user in an AngularJS scope, but getting an error when the page first loads up.

Clicking "Login" seems to work, but then logout bails with the same "$apply already in progress" error.

I'm expecting I've just misunderstood how $apply works, can anybody help?

Update:

It works seamlessly if I wrap just the if (user) part in $apply() as follows.

var auth = new FirebaseAuthClient(ref, function(error, user) {
    if (user) {
        $scope.$apply(function(){ $scope.user = user; });
        console.log(user);
    } else if (error) {
        // I can't vouch for this, haven't tested it
        $scope.$apply(function(){ $scope.user = null; });
        console.log(error);
    } else {
        $scope.user = null;
        console.log("logged out");
    }
});

Original code:

app.js:

var app = angular.module('app', ['firebase']);

controller.js:

app.controller('Login', ['$scope', 'angularFire',
function($scope, angularFire) {
    $scope.loginstatus = 'logged out';

    var ref = new Firebase("https://app.firebaseio.com/");
    var auth = new FirebaseAuthClient(ref, function(error, user) {
        if (user) {
            $scope.$apply(function(){ $scope.loginstatus = 'logged in'; });
        } else if (error) {
            $scope.$apply(function(){ $scope.loginstatus = 'error'; });
            alert("Login error: " + error);
        } else {
            $scope.$apply(function(){ $scope.loginstatus = 'logged out'; });
        }
    });

    $scope.login = function(provider, args) {
        auth.login(provider, args);
    }

    $scope.logout = function() {
        auth.logout();
    }
}]);

index.html:

<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
    <meta charset="utf-8">
    <title>App</title>
    <script type="text/javascript" src="https://cdn.firebase.com/v0/firebase.js">
    <script type="text/javascript" src='https://cdn.firebase.com/v0/firebase-auth-client.js'></script></script>
    <script type="text/javascript" src="lib/angular/angular.js"></script>
    <script type="text/javascript" src="lib/angularFire.js"></script>
    <script type="text/javascript" src="js/app.js"></script>
    <script type="text/javascript" src="js/controllers.js"></script>
</head>
<body ng-controller="Login">
    <p>Login status: {{loginstatus}}</p>
    <div class="container">
      <h1>Login</h1>
      <p>
        <button ng-hide="user" ng-click="login('twitter')" class="btn btn-primary btn-large">Login</button>
        <button ng-show="user" ng-click="logout()" class="btn btn-primary btn-large">Logout</button>
      </p>
    </div>
</body>
</html>
like image 801
Dave E Avatar asked May 09 '13 00:05

Dave E


1 Answers

$apply is only needed when you're outside of the context of Angular (like a setTimeout, setInterval, or non-Angular XHR callback).

Since FireBaseAuthClient is a tool written with Angular in mind and injected into your controller, it's likely that the callbacks are already wrapped in $scope.$apply. You can fix your code by removing $scope.$apply, so just:

var ref = new Firebase("https://app.firebaseio.com/");
var auth = new FirebaseAuthClient(ref, function(error, user) {
    if (user) {
        $scope.loginstatus = 'logged in';
    } else if (error) {
        $scope.loginstatus = 'error';
        alert("Login error: " + error);
    } else {
        $scope.loginstatus = 'logged out';
    }
});
like image 147
Langdon Avatar answered Sep 23 '22 08:09

Langdon