javascriptangularjsangular-material

Angular.js - how to bind object from dom event


I am working on a game which uses angular.js for the ui and a game engine class.

Because of conflicts between the game engine class and the angular-material library, I have to load the the game engine in order after angular.

Once the game engine is initialized, it does dispatch a custom dom event "clientReady", with 2 objects attached that I need to access within my angular controllers.

So on app.run I initialize this 2 objects to the root scope like this:

app.run(function($rootScope) {
    $rootScope.engine = {};
    $rootScope.editor = {};
    document.addEventListener('clientReady', function(e) {
        $rootScope.$apply(function() {
            $rootScope.engine = e.detail.engine;
            $rootScope.editor = e.detail.editor;
        });
    });
});

Now within my controllers I am trying to set scopes like:

$scope.player = $rootScope.engine.player;

This does not work of course, because the "clientReady" event happens late after angular initialize and the rootscope does not exist when it starts running the controller.

Is there a way to handle this? I'm wondering if I could somehow launch the controllers after the dom event.


Solution

  • You cannot "launch" the controller. You can goto the view and it'll run he controller and bind the scope of the controller with its corresponding view.

    So if you want this event to be listened at all times, you should do this in the BodyController. But if you only want to listen to this event on certain views, the you should probably just do this on app.run

    app.controller('BodyController', function($rootScope) {
      $document.addEventListener('clientReady', function(e) {
        
        //  Decide based on view when you want to change initialize the game or not
        if($location.path == '/login') {
          // don't let the user init as he/she is not logged in
        } else {
          //init
          // Store e in a service or $rootScope. I'd pick a service to do this as I wouldn't want to clutter my $rootScope
          GameService.save(e);
          // Handle transition to the new path
          $location.path('/something'); 
        }
        
        
        
      });  
    })

    Or else if you only want to listen to this on one or more views. 1. Listen to clientReady event on one/more of your views (assuming this event is initialized only once)

    So, lets say your home/default view is '/'. Then in HomeController, you do this:

    app.controller('MainCtrl', function($scope, $document) {
    
        $document.addEventListener('clientReady', function(e) {
            // Store e in a service or $rootScope. I'd pick a service to do this as I wouldn't want to clutter my $rootScope
            GameService.save(e);
            // Goto to View/Controller that needs e
            $location.path('/something');
        });
    }

    1. Next in the '/game' route you can begin all your game processes. This will work because isInitialized() returns true ONLY when the clientReady event has been fired. If isInitialized()return false, then the $location will be changed and execution of GameCtrl will stop (because ofreturn` statement)

    app.controller('GameCtrl', function($scope, GameService, $location) {
      if(!GameService.isInitialized()) {
        $location.path('/home'); // or to any other route you deem fit
        // Instead of redirecting, you can handle this gracefully on this view too. But I think that'll take much more effort.
        return; //Important so that the controller is not processed any further
      }
      // This is where the magic happens!
      $scope.engine = GameService.getEngine();
      $scope.editor = GameService.getEditor();
    }

    1. Your GameService is where game engine and other client given object is stored.

    angular.module('myApp').service('GameService', function() {
      // AngularJS will instantiate a singleton by calling "new" on this function
        this.getEngine = function() {
          // Logic  
        }
        this.getEditor = function () {
          // Logic  
        }
        this.isInitialized = function () {
          // Logic  
        }
    };