1

I'm familiarizing myself with controllerAs syntax in AngularJS, and I've come to a problem when I need to do a simple binding to a service variable. Typically a $scope.$watch or $scope.$on would do, but that would involve injecting $scope, which seems to defeat the purpose of controllerAs.

Currently what I have is that after clicking one of the buttons and calling config.setAttribute(attr), the controller calls the service's setAttribute function, but not getAttribute, so config.attribute never changes.

Is there something I'm overlooking in how I'm approaching this? Would I need to inject $scope or change the controller syntax to use $scope instead?

View:

<div data-ng-controller="ConfigCtrl as config">
    <h3>Customize</h3>
    <pre>Current attribute: {{config.attribute}}</pre>

    <label>Attributes</label>
    <div data-ng-repeat="attr in config.attributes">
        <button ng-click="config.setAttribute(attr)">{{attr.name}}</button>
    </div>
</div>

Service:

(function() {
'use strict';

angular.module('app')
.factory('Customization', Customization);

function Customization() {
    var service = {
        attribute: null,
        getAttributes: getAttributes,
        setAttribute: setAttribute,
        getAttribute: getAttribute
    }
    return service;
    /////
    function getAttributes() {
        return [
            {name: 'Attr1', value: '1'},
            {name: 'Attr2', value: '2'} // etc.
        ];
    }

    function setAttribute(attr) {
        service.attribute = attr;
    }

    function getAttribute() {
        return service.attribute;
    }
}})();

Controller:

(function(){
'use strict';

angular.module('app')
.controller('ConfigCtrl', ConfigCtrl);

function ConfigCtrl(Customization){
    var vm = this;

    vm.attribute = Customization.getAttribute(); // bind
    vm.attributes = [];

    // Functions
    vm.setAttribute = Customization.setAttribute;

    init();
    /////
    function init(){
        // Get attributes array
        vm.attributes = Customization.getAttributes();
    }
}})();
jynt
  • 31
  • 4
  • 1
    Injecting `$scope` doesn't defeat the purpose of controllerAs. That's kind of the point - that you inject `$scope` when it is needed as a service for things like `$watch` and `$on` and not by default in order to publish ViewModel properties. – New Dev Aug 08 '15 at 22:23
  • http://www.fullstacktraining.com/articles/angularjs-using-watch-with-controlleras – Itamar L. Aug 08 '15 at 22:27
  • Thanks for the comments, they definitely cleared up my confusion. – jynt Aug 08 '15 at 22:31

1 Answers1

0

Here is what my controller looks like after injecting $scope and adding the watch for attribute:

(function(){
'use strict';

angular.module('app')
.controller('ConfigCtrl', ConfigCtrl);

function ConfigCtrl($scope, Customization){
    var vm = this;

    vm.attribute;
    vm.attributes = [];

    // Functions
    vm.setAttribute = Customization.setAttribute;

    init();
    /////
    function init(){
        // Get attributes array
        vm.attributes = Customization.getAttributes();
    }

    $scope.$watch(function() {
        return Customization.getAttribute()
    }, function() {
        vm.attribute = Customization.getAttribute();
    });

}})();

I also have the Karma test in case anyone is interested:

(function() {
    'use strict';

    describe('ConfigCtrl', function () {

        var ConfigCtrl, scope;

        beforeEach(module('app'));

        beforeEach(inject(function($rootScope, $controller) {
            scope = $rootScope.$new();
            ConfigCtrl = $controller('ConfigCtrl',
                {$scope: scope}
            );
        }));

        describe('#setAttribute', function(){
            it('sets the current attribute', function(){
                var selected = {
                    name:'Attr1',
                    value:'1'
                };
                ConfigCtrl.setAttribute(selected);
                scope.$apply();
                expect(ConfigCtrl.attribute).to.eql(selected);
            });
        });
    });
})();

Thanks for the help. I'm welcome to any better answers anyone else might have.

jynt
  • 31
  • 4
  • I know this question is a little bit older. So just as a hint: don't use the implicit injection if you want to minify your code. See https://docs.angularjs.org/guide/di#implicit-annotation – Philipp Jul 19 '16 at 14:42