Extending a Controller in AngularJS

By

When building complex Angular applications with multiple controllers, you may find yourself in a situation where two or more controllers perform similar tasks, but are slightly different. A good way to minimize code duplication is to create a base controller and extend it’s behavior in other controllers. Here’s a simple example to see how this works.

Controllers

Let’s start by defining two separate controllers - SimpleCalculatorCtrl and AdvancedCalculatorCtrl.

angular
    .module("myAwesomeApp", [])

    .controller("SimpleCalculatorCtrl", function() {
        var vm = this;

        vm.add = add;
        vm.subtract = subtract;

        function add(a, b) {
            vm.result = a + b;
        }

        function subtract(a, b) {
            vm.result = a - b;
        }
    });

    .controller("AdvancedCalculatorCtrl", function() {
        var vm = this;

        vm.add = add;
        vm.subtract = subtract;
        vm.multiply = multiply;
        vm.divide = divide;

        function add(a, b) {
            vm.result = a + b;
        }

        function subtract(a, b) {
            vm.result = a - b;
        }

        function multiply(a,b) {
            vm.result = a * b;
        }

        function divide(a, b) {
            vm.result = a / b;
        }
    });

Now lets put our new controllers to use:

<html>
    <body>
        <div ng-controller="SimpleCalculatorCtrl as sc">
            Result: <span ng-bind="sc.result"></span>

            <input ng-model="sc.a"></input>
            <input ng-model="sc.b"></input>

            <button ng-click="sc.add(sc.a, sc.b)">
                Add
            </button>

            <button ng-click="sc.subtract(sc.a, sc.b)">
                Subtract
            </button>
        </div>

        <div ng-controller="AdvancedCalculatorCtrl as ac">
            Result: <span ng-bind="ac.result"></span>

            <input ng-model="ac.a"></input>
            <input ng-model="ac.b"></input>

            <button ng-click="ac.add(ac.a, ac.b)">
                Add
            </button>

            <button ng-click="ac.subtract(ac.a, ac.b)">
                Subtract
            </button>

            <button ng-click="ac.multiply(ac.a, ac.b)">
                Multiply
            </button>

            <button ng-click="ac.divide(ac.a, ac.b)">
                Divide
            </button>
        </div>
    </body>
</html>

SimpleCalculator and AdvancedCalculator both contain identical add and delete functions, but AdvancedCalculator also contains functionality for multiply and divide. Now one way to simplify both controllers is creating a CalculatorService which takes care of the actual logic for us; but there’s more to it, since both controllers also need to update the result and display it’s new value on the DOM.

Creating a Base Controller

Let’s create a base controller for our calculator controllers. Our base controller will contain all of the functionality that is shared between SimpleCalculator and AdvancedCalculator.

angular
    .module("myAwesomeApp", [])

    .controller("CalculatorCtrl", function() {
        var vm = this;

        vm.add = add;
        vm.subtract = subtract;

        vm.data = {};

        function add(a, b) {
            vm.data.result = a + b;
        }

        function subtract(a, b) {
            vm.data.result = a - b;
        }
    });

Extending the Base Controller

Now we can simplify our SimpleCalculatorCtrl and AdvancedCalculatorCtrl like so:

angular
    .module("myAwesomeApp", [])

    .controller("SimpleCalculatorCtrl", function($scope, $controller) {
        var vm = this;

        // Inherit from Base Controller
        angular.extend(vm, $controller('CalculatorCtrl', {
            $scope: $scope
        }));
    })

    .controller("AdvancedCalculatorCtrl", function($scope, $controller) {
        var vm = this;

        // Inherit from Base Controller
        angular.extend(vm, $controller('CalculatorCtrl', {
            $scope: $scope
        }));

        vm.multiply = multiply;
        vm.divide = divide;

        function multiply(a,b) {
            vm.data.result = a * b;
        }

        function divide(a, b) {
            vm.data.result = a / b;
        }
    });

As you can see, we’ve removed duplicate code from both of our controllers by extending our base controller - CalculatorCtrl. We do this by injecting Angular’s built in $controller service to initialize our base controller, and then use angular.extend to extend our current controller with our base controller.

We moved the result property originally on our controller into an empty Object called data. This is because in JavaScript changing the value of a variable never changes the underlying Object itself; however changing the property of an Object referenced by a variable does change the underlying Object. Therefore any properties that we would like to share between two controllers must be kept on a parent Object.

Remember that in this particular case it may make sense to just create a single controller and re-using it as needed. However, as the number of controllers in your application grow and become more complex, the ability to extend a controller becomes very useful.