AngularJS Controller Best Practices

In Angular JS, Controllers allow you to create an isolated scope and “control” data before it is passed to the View. Using a Controller, you are able to define an isolated scope and then control what part of your application has access to this scope; therefore, the data is bound to it. The traditional method for implementing Controllers is to inject a new scope instance, using the $scope variable and then, bind data to it. You will, then, be able to access this bound data within the View where your Controller has governance.

The following example illustrates this principle.



This method provides the simplest way to bind your data with the View. This has some additional benefits as the $scope object provides various functions available for use in your Controllers. These include some of the following. These are just few example functions available when injecting $Scope object into Controller.

  • $scope.$watch() - To watch the changes on scope properties
  • $scope.$eval() - Evaluates an expression against the scope
  • $scope.$emit() - Emit custom events
  • $scope.$on() - Listen for and act on events

More details on these functions are available at the below link.

https://docs.angularjs.org/api/ng/type/$rootScope.Scope

Disadvantages of binding data to $scope

Using $scope is really simple to bind data in traditional way. There are a few downsides of using the traditional implementation. In large applications using this method, it is very common that accidentally scope variables get overwritten and have adverse effect in Views. The following example demonstrates this.

The example has two Controllers where DetailController is nested under the MasterController which is very common in many applications. MasterController has “firstName” and “lastName” assigned to its scope to output these fields to View. Now, the issue is that if accidentally similar variable name is used within DetailController, it overwrites the “firstName” in MasterController. When name is output to the View, it appears wrong. This seems silly but is very common.

Using the traditional method, it can be difficult to understand where the View variables are originally declared when you have multiple Controllers being used in the same View. With the simple example above,

It’s difficult to know where the “firstName” and “lastName” are declared - within the MasterController or DetailController, without looking into the code. If more Controllers and many similar variable names are there, it becomes headache.

Using ControllerAs Method

AngularJS provides some additional functionality when declaring where the Controllers are used. This additional functionality is called controllerAs method.

The following example shows how to use the ControllerAs syntax. This can also be used when defining routes.

The difference with the example above is the use of the as keyword within the ng-controller declaration highlighted below.

code

Using the as keyword, an alias can be provided for each of the controllers. In the above example, MasterController is now aliased to master and DetailController has an alias of detail. Now scope data can be bind to controller instance using this keyword.

code

In the above code sample, you will notice the missing injected $scope variable. Instead of the $scope variable provided by Angular we simply declare a ms variable which is for ViewModel and assign this to it (i.e. the instance of the controller function). All variables will now be assigned to the ms object instead of $scope.

The above example also illustrates how ControllerAs solves the scope bleed issue. As both the controllers now have meaningful aliases, it is possible to use the same variable name firstName without overriding a parent variable. When you have multiple nested controllers like this the ControllerAs method ensures your view code is more semantic and easier to understand.

Now, in above example, question may be why to have separate variable ms and assigned “this”. Can’t you skip and use this keyword directly? The problem with just using this keyword directly is that, it is contextual to its wrapping function. In below example it refers to the ‘foo’ function and not controller function as whole.

code

It is always better to declare scope variable i.e. ms in above example at the top of the controller.

To use $scope methods, you need to inject $scope as and when you want to access those methods.

code

Angular JS Controller Practices
  • Controllers should be lightweight; hold zero logic, and used only to bring the logic together.
  • Controllers should bind references to Models only (and call methods returned from promises).
  • Controller drives Model changes, and View changes.
  • Use ControllerAs.
  • Keep method and property names consistent across shared methods.
  • Factories should be used to hold Model, change, get, update, and persist the Model changes