1

I am integrating a 2D JavaScript game engine I wrote with Angular. The game is a space game where the engine handles the simulation of the space environment and Angular handles the menus for trading goods with space stations, etc.

Angular services act as an interface between the two contexts. For instance, when the game needs to log a message, it calls logServiceInstance.logDebug("Some message");. There's a ConsoleController that feeds log messages to an HTML div (uses ng-repeat on $scope.logs) that overlays the game canvas:

angular.module('game')
   .controller('ConsoleController', ['$scope', 'LogService', function($scope, logService) {
    $scope.logs = logService.getLogs();
}]);

Because the game engine is updating the service outside of Angular, Angular doesn't know about some of these logs so all of the log methods (logInfo, logError, logDebug, etc) call into this on the logService. Logs created from the game pass 'true' as the second argument to trigger a digest:

addLog: function(log, updateContext) {
    console.log('(' + log.level + ') ' + log.message);
    logs.unshift(log);

    // NOTE: updateContext should be passed if log is
    // called outside of Angular context. This way Angular
    // reacts to the change.
    if(updateContext != null && updateContext === true) {
        // notify angular of a change
        $rootScope.$digest();
    }
}

The ConsoleController fails to pull log messages consistently. Logs are missed in the HTML display but show up in the javascript console. Additionally, I get errors that I'm triggering a digest within a digest.

Questions:

  • How do I know if a digest is already in progress from some other source?
  • Services seem like the best way to unify passing states between two frameworks but is this a bad pattern? If so, why?

Thanks in advance, Angular gurus.

Note: game engine is https://bitbucket.org/profexorgeek/frostflake-js-game-engine/

profexorgeek
  • 1,190
  • 11
  • 21

1 Answers1

1

Q1:

How do I know if a digest is already in progress from some other source?

if (!$rootScope.$$phase) {
    $rootScope.$digest();
}

or just

$rootScope.$$phase || $rootScope.$digest();

Depending on your specific situation, you could want to do $rootScope.$$phase || $scope.$digest(); note $scope instead $rootScope

Q2:

Services seem like the best way to unify passing states between two frameworks but is this a bad pattern? If so, why?

This pattern is recommended. Services are Singletons, what makes them appropriate places.

See: Share data between AngularJS controllers

Community
  • 1
  • 1
rnrneverdies
  • 15,243
  • 9
  • 65
  • 95
  • Awesome, I never saw $$phase mentioned in any of the Angular examples or docs that I read. I get that $scope (vs $rootScope) is more performant if you don't need to evaluate every $watch in the application. But how does the Service know which controller $scope references? I have not fully grasped how Services are instantiated with the correct dependencies. Thanks for the quick, concise answer! – profexorgeek Dec 16 '14 at 22:22
  • 1
    $$phase is widely used internally in angular directives. As i said, Services are singletons, all controllers have a reference to the same service instance. So, any data sorted/modified on that service is automatically available on other controllers. – rnrneverdies Dec 16 '14 at 22:29