Client Code Structure, Part 2: Controller Files

After separating shared properties into their own Model files, the next thing I ran into was that our controller files were getting large and unwieldy. This was especially the case for our main app.js file. I realized that this was because it contained a loose collection of methods thrown together because we couldn’t find a better place to put them (Sound Familiar?) I thought, “Wouldn’t it be nice if we could conceptually group these methods and separate them into different files just like we did with the models?”

It turns out you can! As I built this file I noticed that it reminded me of a controller file. Then I thought, “Wait, don’t we already have a controller?” It was at this point that I realized that what Angular calls a “Controller” is actually a “ViewModel” and what I was creating was a standard “Controller.” With this in mind, I kept view-specific methods in the ViewModel and created a controller class for non view-specific methods.

divisionModule.factory('DivisionController',
    ['DivisionModel', 'DivisionService',
        function (DivisionModel, DivisionService) {

            function getDivisionsResultHandler(result) {
                //Apply the result
            }

            function getDivisions() {
               DivisionService.query(queryParams, getDivisionsResultHandler);
            }

            getDivisions();

            return{
               getDivisions: getDivisions
            }
        }
    ]
);

I then injected these controller files into the relevant ViewModels (Angular Controllers) and began to invoke their methods directly. Something about this did not seem right, however. Controllers are not supposed to be invoked directly. Instead, they should communicate by registering and dispatching events. Angular has events, but since my controllers were not attached to a view they did not have a $scope and therefore would not propagate events. It was at that point I realized that I could inject the Angular $rootScope and dispatch and listen for events from there.

I went back to my ActionScript roots again and created a controller class as such:

divisionModule.factory('DivisionController',
    ['$rootScope', 'DivisionModel', 'DivisionService',
        function ($rootScope, DivisionModel, DivisionService) {

            function divisionSelectedHandler(event, division) {
                DivisionModel.selectedDivision = division;
            }
            $rootScope.$on('divisionSelected', divisionSelectedHandler);

            function getDivisionsResultHandler(result) {
                //Apply the result
                DivisionModel.divisions = result;

                $rootScope.$emit('divisionSelected', selectedDivision);
            }

            function getDivisions() {
                DivisionService.query(queryParams, getDivisionsResultHandler);
            }

            getDivisions();
        }
    ]
);

With the controller structured this way, I no longer had to inject it everywhere that I wanted to use it. Now generally it is bad practice to use $rootScope but since it is only being used as a conduit for communication I think it is acceptable.

 Next -> Client Code Structure, Part 3: Organization