Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing AngularJs Directive Controller

I am having a few problems accessing my controller on a directive that I am trying to unit test with jasmine and karma testrunner. The directive looks like this:

directive

angular.module('Common.accountSearch',['ngRoute'])

    .directive('accountSearch', [function() {
        return {
            controllerAs: 'ctrl',
            controller: function ($scope, $element, $routeParams, $http) {

                this.setAccount = function () {
                    var response = { AccountId : $scope.ctrl.searchedAccount.AccountId }
                    $scope.callback(response)
                }


                this.getAccounts = function(searchText){
                    return $http.get('/api/CRMAccounts', {
                        params: {
                            retrievalLimit: 10,
                            search: searchText
                        }
                    }).then(function(response){
                        return response.data;
                    });

                }

            },
            scope : {
                config : '=',
                values : '=',
                callback : '='
            },
            templateUrl : '/common/components/account-search/account-search.html',
            restrict : 'EAC'
        }
    }]);

This here is the test case file so far I believe all is in order and correct (I hope):

test case file:

    describe("Account search directive logic tests", function (){
  var element,$scope,scope,controller,template

  beforeEach(module("Common.accountSearch"))


  beforeEach(inject( function (_$compile_, _$rootScope_,_$controller_,$templateCache) {
    template = $templateCache.get("components/account-search/account-search.html")
    $compile = _$compile_;
    $rootScope = _$rootScope_;
    $controller = _$controller_;
    scope = $rootScope.$new();
    element = $compile(template)(scope)
    ctrl = element.controller
    scope.$digest();
  //  httpBackend = _$httpBackend_;
  }));




  it(" sets the account and calls back.", inject(function () {

    console.log(ctrl)
    expect(ctrl).toBeDefined()
   }));
  //httpBackend.flush()
});

I have managed to print the controller of the directive ( I think) to the console which returns the following ambiguous message:

LOG: function (arg1, arg2) { ... }

I cannot access any of the functions or properties on the directive as they are all returning "undefined", what am I doing wrong?

like image 682
nagrom97 Avatar asked Oct 10 '16 09:10

nagrom97


People also ask

Which directive is used for controller in angular?

The ng-controller Directive in AngularJS is used to add a controller to the application.

What is difference between controller and directive AngularJS?

A controller is usually used to contain and maintain the logic for your view, which gets bound to your view via $scope. A directive is something that you might use repeatedly and is called in your view directly through the directive name which you can pass in as an attribute.

What are the AngularJS controllers?

AngularJS controllers are used to control the flow of data of AngularJS application. A controller is defined using ng-controller directive. A controller is a JavaScript object containing attributes/properties and functions.

How do you share data between controller and viewer?

14) Which of the following is used to share data between controller and view in AngularJS? Answer: B: "using services" is the correct answer.


2 Answers

Controllers for directives are actually fully injectable - instead of providing a constructor, you can just refer to the controller by name. See the directive definition object docs for Angular here: https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object

In your case where you want to unit test the controller you'd just do it like this:

common.accountSearch.js

angular.module('Common.accountSearch', [])
  .directive('accountSearch', [function () {
      return {
          controller: 'accountSearchCtrl',
          scope: {
              config    : '=',
              values    : '=',
              callback  : '='
          },
          templateUrl : '/common/components/account-search/account-search.html',
          restrict: 'EAC'
      }
  }])
  .controller('accountSearchCtrl', ['$scope', function ($scope) {
      $scope.setAccount = function () {
          var response = {
              AccountId: $scope.ctrl.searchedAccount.AccountId
          };
          $scope.callback(response);
      }

      $scope.getAccounts = function (searchText) {
          // Code goes here...
      }
  }]);

common.accountSearch-spec.js

describe("Account search directive logic tests", function () {    
    var controller, scope;

    beforeEach(module("Common.accountSearch"));

    beforeEach(inject(function (_$controller_, _$rootScope_) {        
        $rootScope = _$rootScope_;
        scope = $rootScope.$new();
        controller = _$controller_('accountSearchCtrl', { '$scope': scope });        
    }));

    it(" sets the account and calls back.", function () {
        expect(controller).toBeDefined();
    });
});

This way you can just inject your controller directly into your jasmine tests like any of your other controllers.

Hope this helps.

like image 198
s3raph86 Avatar answered Oct 12 '22 20:10

s3raph86


So close!

element.controller is a function and needs to be passed the name of the directive which you're attempting to get the controller for. In this case it would be

ctrl = element.controller("accountSearch");
like image 38
luke88jones Avatar answered Oct 12 '22 19:10

luke88jones