Question

I'm trying to use some of angulars best practices defined on the google-styleguide site: https://google-styleguide.googlecode.com/svn/trunk/angularjs-google-style.html

But at the moment I'm struggling with some issues. Before I used this styleguide I had the $scope variable available to do a $watch on a variable for instance.

app.controller('myController',['$scope', function($scope) {
    $scope.$watch('myVariable', function(val) {
       alert("I'm changed");
    });
}]);

Now with my new approach I don't know how to handle this? Should I still inject $scope? Because I do not have to inject $scope when I'm not using $watch.

function myController($scope) {
   var vm = this;

   vm.myVariable = "aVariable";
   vm.$watch('vm.myVariable', function(val) {
      // error because $watch is undefined
   });

   //$scope.$watch - works
}

app.controller('myController',['$scope', myController]);

The styleguide also advices to make use of prototypes. But what if I had to inject a service? What is the best approach to use a service inside your prototype?

function myController(MyService) {
   var vm = this;

   vm.myService = MyService;
}

myController.prototype.callService = function() {
   var vm = this;

   vm.myService.doSomething();
}

Is this correct? Or am I missing something, is there a place where I can find more information about this style of angular programming?

In my opinion it feels more like natural javascript and I want to use this way of organizing my AngularJS apps.

Thanks in advance

Update

For the 'service' problem I was thinking of something as follows:

function MyBaseController(AService, BService, CService) {
   this.aService = AService;
   this.bService = BService;
   this.cService = CService;
}

function myController() {
   var vm = this;
   MyBaseController.apply(vm, arguments);
}

myController.prototype.doSomething() {
   var vm = this;
   this.aService.somethingElse();
}

But this doesn't feel right imo..

Was it helpful?

Solution

It is perfectly valid to inject $scope to get access to things like $watch even when your using the "controller as" syntax. For example:

JS

var MyController = function($scope) {
    $scope.$watch('ctrl.someVar' function() {
        ...
    });

    this.someVar = 123;
}

MyController.$inject = ['$scope'];

HTML

<div ng-controller="MyController as ctrl">
    ....
</div>

The first example you gave of injecting a service into a controller is good. For the inheritance example I would do something like this.

var BaseController = function(AService, BService) {
    this.aService = AService;
    this.bService = BService;
}

BaseController.prototype.doSomethingWithAAndB = function() {
    ...
}

var MyController = function(AService, BService, CService) {
    BaseController.call(this, AService, BService);

    this.cService = CService;
}

MyController.$inject = ['AService', 'BService', 'CService'];

//Note: you'll need to add a polyfill for Object.create if you want to support ES3.
MyController.prototype = Object.create(BaseController.prototype);

If you find it is too cumbersome to specify all the parameters in the child controller you could always just inject $injector and pass that up to your base controller.

OTHER TIPS

@rob answer is correct. There are perfectly valid reasons to still want $scope injected in your controllers, $watch $on $emit $broadcast to name a few. Sure you can get away with this requirement in some cases using directives or services, but it's not always worth the time or complexity.

I've given the controller as syntax a go, but found that in most of my use cases I still had a dependency on $scope in my controllers. I disliked this and so I ended up with the following convention:

var MyController = function($scope) {
  $scope.vm = {};
  var vm = $scope.vm;

  vm.propA = 1;
  vm.propB = 2;
  vm.funcA = function(){};

  $scope.$watch('vm.propA' function() {
  });
}

So it uses the 'old school' approach but with a new school feel. Everything in the view hangs off vm.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top