When developing I really hate stacked functions that seem to go on forever. However there is a time and place for that. But if you think about the nature of JavaScript, you realize that the nature of the language is both asynchronous and dependency injected.
There is no real wait
command, but rather you pass a callback function to your asynch calls, like the HTTP posts. Well these callback functions are your dependency injected code.
With this in mind, lets build up and look at an Angular.js controller.
First of all, you should wrap your code this way.
(function () { 'use strict'; //your code here. }()
This is a shorthand way of creating a self contained function, that keeps everything pretty much as close to private as possible. The real benefit, is that you strongly reduce collision between multiple JavaScript files.
The 'use strict';
string left like this means that everything you do will have a clear declaration such as var
.
(function () { 'use strict'; var appName = 'MyApp'; var stringArrayOfModules = [ //This is my common list of Angular.js resources. 'ngAnimate', // Animations 'ngRoute', // Routing 'ngResource', // Rest Call 'ui.bootstrap', // Twitter Bootstrap Support 'textAngular', // Rich text editor 'RecursionHelper', // Recursion assistant. Useful for popups and hierarchies. 'UIDragDrop', // Drag and Drop handlers. ]; var app = angular.module(appName, stringArrayOfModules); }()
As you have just noticed I vary greatly from the majority of the samples out there. I find that this is MUCH easier to read, and has a better support for cut and paste code.
If you are using routing, this becomes very evident of what I am doing.
I’ll keep this file as a first load for everything, so that I can use it everywhere. I generally have to use the URLs for links too. So I have them generated at the server, and emitted as a dynamic JavaScript file, and embed into the web page. For .NET you can think of this JavaScript file being a custom .axd request to a custom handler..
var globalUrl = {}; globalUrl.catalog = 'app/catalog/';
These two next sections would normally go into the same section as the module declaration above.
app.config(['$routeProvider', buildRoute]);
So this is the actual buildRoute
function that is called in the app.config call above.
function buildRoute($routeProvider) { $routeProvider .when('/catalog/:id', { templateUrl: globalUrl.catalog, // sample: 'app/catalog/1' controller: 'catalogCtrl', }) .otherwise({ templateUrl: globalUrl.catalog, // sample: 'app/catalog/' controller: 'catalogCtrl', }) }
So lets look at an actual controller now.
(function () { 'use strict'; var appName = 'MyApp'; var controllerId = 'catalogCtrl'; angular.module(appName).controller(controllerId, ['$scope', buildCatalogCtrl]); function buildCatalogCtrl($scope) { $scope.addThis = addThis(#scope); } function addThis($scope){ return function(valueA, valueB){ var valueC = valueA + valueB; scope.ctrlMsg = "Added " + valueA + " to " + valueB; return valueC; } } })();
Lets break this down:
(function () { 'use strict'; var appName = 'MyApp'; var controllerId = 'catalogCtrl';
Using the module I declared above, I have now created a controller.
angular.module(appName).controller(controllerId, ['$scope', buildCatalogCtrl]);
This controller is built with the code below. Where addThis
is named the same in scope as it is in the function. This way I can find the code faster.
function buildCatalogCtrl($scope) { $scope.addThis = addThis($scope); }
And here you can see why I pass scope. I don’t have to handle a bunch of variables back and forth, if I know what scope it. By keeping values A, B, and C, declared locally, I can use this same function in other locations. I can even go so far as build a global library of my most common functions, and still handle the immediate scope issues.
function addThis($scope){ return function(valueA, valueB){ var valueC = valueA + valueB; scope.ctrlMsg = "Added " + valueA + " to " + valueB; return valueC; } } })();
There are other improvements that can be done to this sample code, like passing the name of the message parameter of $scope as a string, so that I can customize the calls per controller. If I wanted to handle exceptions in a consolidated way, I could even provide a clean process for that by passing my exception controller as a parameter when I set this function in the controller.
Though this article isn’t covering resources or directives, you can simplify debugging your code there too by using this style of function breakdown.
Keep your code simple, and it becomes more debug-able.