1

In an effort to be more Angular 2.0 like I've decided to eliminate defining a controller in the traditional way:

e.g.

return {
   restrict: 'E',
   controller: 'MyCtrl',
   controllerAs: 'vm',
   bindToController: true,
   scope: {},
   templateUrl: 'path/to/tempate.html'
}

Here we can see that the controller name is passed as a string meaning that we have something like this defined somewhere:

app.controller('MyCtrl', function(...));

In a traditional test I'd just inject $controller service into the test to retrieve the MyCtrl controller. But I've decided to do this:

return {
   restrict: 'E',
   controller: MyCtrl,
   controllerAs: 'vm',
   bindToController: true,
   scope: {},
   templateUrl: 'path/to/tempate.html'
}

Here the only difference is that what is being passed to the controller declaration is a function called MyCtrl. This seems all well and good but do you go about retrieving this controller and testing it?

I tried doing this:

var $compile, $rootScope, $httpBackend, element, ctrl;

    beforeEach(module('app'));
    beforeEach(function() {
        inject(function(_$compile_, _$rootScope_, _$httpBackend_) {
            $compile     = _$compile_;
            $rootScope   = _$rootScope_.$new();
            $httpBackend = _$httpBackend_;

            element = $compile('<directive></directive>')($rootScope);
            ctrl = element[0].controller;
        });
   });

But in the above I get undefined coming back for the controller. Has anyone else made this move to be more like Angular 2.0? What differences have had to be made when it comes to testing?

Thanks

P.S The idea of the change in syntax is to make it easy to upgrade to Angular 2.0 when the time comes for us to do it.

EDIT

I've spent the last day or two trying to retrieve the controller and the only way that works seems to be just passing the controller in the traditional way. Here's a list of the ways I've tried (see the comments to determine what each outputs)

(function() {
'use strict';

/*
    This part is used just for testing the html rendered
*/
describe('flRequestPasswordReset template: ', function() {

    var $compile, $rootScope, $templateCache, template, element, ctrl;
    var path = 'templates/auth/fl-request-password-reset/fl-request-password-reset.html';

    beforeEach(module('app'));
    beforeEach(module(path));

    beforeEach(inject(function(_$compile_, _$rootScope_, _$templateCache_) {
        $compile        = _$compile_;
        $rootScope      = _$rootScope_;
        $templateCache  = _$templateCache_;

        // Using nghtml2js to retrieve our templates
        template = $templateCache.get(path);
        element = angular.element(template);
        $compile(element)($rootScope.$new());
        $rootScope.$digest();

        // $rootScope has a reference to vm which is what my  
        // controllerAs defines but only contains one of the variables

        // This returns undefined
        ctrl = element.controller('flRequestPasswordReset');
    }));

    it('should be defined', function() {
        expect(element).toBeDefined();
    });
});

describe('flRequestPasswordReset: template with controller: ', function() {

    var $compile, $rootScope,  $httpBackEnd, element, ctrl;
    var path = '/templates/auth/fl-request-password-reset/fl-request-password-reset.html';

    beforeEach(module('app'));      

    beforeEach(inject(function(_$compile_, _$rootScope_, _$templateCache_, _$httpBackend_) {
        $compile        = _$compile_;
        $rootScope      = _$rootScope_;
        $httpBackEnd    = _$httpBackend_;

        $httpBackEnd.expectGET(path).respond({});

        element = angular.element('<fl-request-password-reset></fl-request-password-reset>');
        $compile(element)($rootScope.$new());
        $rootScope.$digest();

        // Uses alternate name for directive but still doesn't get the 
        // controller
        ctrl = element.controller('fl-request-password-reset');
    }));

    it('should be defined', function() {
        expect(element).toBeDefined();
    });
});

/* 
    This part is used for testing the functionality of the controller by itself
*/
xdescribe('flRequestPasswordReset: just controller', function() {
    var scope, ctrl;

    beforeEach(module('app'));
    beforeEach(inject(function($controller, $rootScope) {
        scope = $rootScope;
        // Instantiate the controller without the directive
        ctrl  = $controller('FlRequestPasswordResetController', {$scope: scope, $element: null});
    }));

    it('should be defined', function() {
        expect(true).toBe(true);
    });

    /*
    This works because I moved the controller back to the old way
    */
});

}());

According to my understanding of this question under pkozlowski.opensource answer you can use pass the controller as a function only if you don't encapsulate it inside the directive. This way it's exposed globally. This is a bad thing as it pollutes the global namespace however he seems to suggest wrapping it up when the application is built.

Is my understanding of this correct?

Community
  • 1
  • 1
Katana24
  • 8,706
  • 19
  • 76
  • 118
  • You're setting the controller as vm, which will be available as a property of the scope. But unless you're doing heavy dom manipulation (which IMO is unnecessary if you use data binding creatively), you should be able to just test it like any other controller, without attaching it to anything. – Amy Blankenship Dec 02 '15 at 17:42
  • I inspected all the elements above in the debugger and couldn't find a controller on any of them which suggest that either I'm not looking in the right place it's just not there. – Katana24 Dec 02 '15 at 21:46
  • Are you looking for a property named "controller", or a property named "vm," which is what you've told Angular you wanted it called? – Amy Blankenship Dec 02 '15 at 22:01
  • @AmyBlankenship I'm looking for both – Katana24 Dec 02 '15 at 22:03
  • I was inspecting the objects that I defined above and could see a vm object that existed on the $rootScope - probably because that's what I'm compiling the template against. However this object only has one variable on it when in the actual controller i have 10 variables. – Katana24 Dec 03 '15 at 10:37
  • Yes, one problem with testing the compiled directive rather than just testing the controller in isolation is that you can't share a jsfiddle or plunker of the problem. – Amy Blankenship Dec 03 '15 at 16:29

0 Answers0