Client Code Structure, Part 1: Model Files
Having good code structure is important to making any project successful. In order to learn good code structure you first need to know what code structure means. To me, it means both how you split different pieces of functionality into different files as well as how you organize the code within your files themselves.
Code structure is not something that you can usually find good examples of. This is because unless the point of the example is to demonstrate good code structure it is usually more effective to get to the point of the functionality instead. The example code from AngularJS is usually short and has a very limited scope making it a poor candidate for an example of code structure. Pretty much all functionality in the examples within Angular’s documentation takes place within the controller file and this is how we originally structured our code.
angular.module('ModuleName').controller('ControllerName', [ ...injected dependencies... function( ...injected dependencies... ){ ...Do Stuff... } ] );
This structure started off fine, until we needed to share properties across controllers. The solution was actually pretty simple to handle this. We simply created a SharedProperties object and injected it into each controller which needed it. We could even add SharedProperties to scope and any changes to it anywhere would show up in the view in which is was bound.
angular.module('ModuleName').controller('ControllerName', [ '$scope', 'SharedProperties', ...other injected dependencies... function( $scope, SharedProperties, ...other injected dependencies... ){ $scope.SharedProperties = SharedProperties; ...Do Stuff... } ] );
This worked fine for a while, but as our need for adding shared properties grew, so did our SharedProperties file. Eventually, the file looked like this:
angular.module('Derby.services').service('sharedProperties', [function () { return { selectedDivisionId: 0, divisions: [], selectedDivision: null, raceYears: [], selectedRaceYear: null, RaceYearId: 2012, RaceYear: 2012, isUserLoggedIn: false, isUserAdmin: false, loggedInUser: null, collapseResources: false, sharedResourcesURL: "", imagePath: "/Derby/Apps/Shared/Assets/Images/", attemptingLogin: false, collapseChampionshipRound: false, drivers: {}, divisionHeatCache: {}, isUserLoggedInViaFacebook: false, facebookAccessToken: null, facebookUser: null, selectedHeat: null }; }]);
A loose collection of properties thrown together because we couldn’t find a better place to put them. Property files this large makes properties difficult to find, and therefore difficult to debug.
Even though the SharedProperties technique was being abused and was becoming unwieldy, that didn’t mean it was all bad. In fact, it reminded me of a similar technique used in ActionScript. The only difference was that we would create multiple files and each would hold only properties relevant to a single concept. These are known as “Model” files. This way we could inject only the properties we need and not worry about the rest. This methodology is maintainable, much cleaner, and easy to debug.
divisionModule.factory('DivisionModel', [ function () { return { divisions: [], selectedDivision: null, manageDivisionInUrl: true } } ] );
This file is injected and used in the same manner as SharedProperties, just only as needed.