Search code examples
angularjsangularjs-controller

Why can't I create this controller in my test?


I have a controller that seems to work very well. I need to write test cases so I added the following in a beforeEach(). The $controller is being injected into the beforeEach, as is the $rootScope that sources the $scope.

    createController = function() {
        var x = $controller('MyCtrl', {
            '$scope': $scope
        });
        console.log(">>>>>>", JSON.stringify(x));
        return x;
    };

When I call createController, x logs as {}.

I checked the $scope being passed in - it looks fine. I misspelled 'MyCtrl' on purpose and got a reasonable error message.

What would cause the $controller object to return an empty object, and not log an error?


Solution

  • There is no error to show there, Because you really have not attached anything on the controller instance, everything is attached to the scope. So you get an empty object, because controller does get instantiated after all. I you use controllerAs syntax or attach properties on the controller instance you would be able to see them.

    angular.module('app', []).controller('ctrl', function() {
      this.name = "John";
      this.getFormattedName = function() {
        return "Hi i am " + this.name
    
      }
    
    }).controller('ctrl2', function($controller) {
      var ctrl = $controller('ctrl');
      console.log(JSON.stringify(ctrl));
    })
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script>
    <div ng-app="app" ng-controller="ctrl as ctrl">
      {{ctrl.getFormattedName()}}
      <div ng-controller="ctrl2"></div>
    </div>

    Also note that not everything gets stringified when you use JSON.stringify

    JSON.stringify converts a value to JSON notation representing it:

    • Properties of non-array objects are not guaranteed to be stringified in any particular order. Do not rely on ordering of properties within the same object within the stringification.
    • Boolean, Number, and String objects are converted to the corresponding primitive values during stringification, in accord with the traditional conversion semantics.
    • If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). All symbol-keyed properties will be completely ignored, even when using the replacer function.

    So just use the specified scope in your case, because that is ultimately the view model.

    var myCtrlScope = $scope.$new(); //Create a child scope, so you keep it separate
    createController = function() {
        var x = $controller('MyCtrl', {
            '$scope': myCtrlScope
        });
        console.log(">>>>>>", myCtrlScope);
        return myCtrlScope ;
    };
    //And use myCtrlScope to access scope methods and properties attached by instantiating the controller `MyCtrl` using the `$controller` provider.