Creating AngularJS Controllers With Instance Methods
In AngularJS, just as in any MVC (Model-View-Controller) framework, Controllers respond to events, gather data, and make that data available to the View. In most of the AngularJS demos that I have seen, Controllers are defined as free-standing, globally-scoped functions. In my opinion, this approach is problematic for two reasons. First, it pollutes the global namespace; and second, it requires putting too much logic into a single function. Luckily, with some self-executing functions, we can create an AngularJS Controller that remains private and consists of smaller, more cohesive instance methods.
In most AngularJS demos, you will see Controllers being defined as free-standing JavaScript functions:
function MyCtrl( $scope ){
$scope.someValue = "All your base are belong to us!";
}
These functions are then referenced in the View using the ngController directive:
<div ng-controller="MyCtrl">
{{ someValue }}
</div>
NOTE: You should never ever abbreviate "Controller" as "Ctrl". I am only doing that here because this it is what you will typically see in a demo. You should try to avoid abbreviations as much as humanly possible when naming things in programming.
The expression used to define the ngController directive is the name of the Controller in your dependency injection (DI) framework. In an AngularJS application, the dependency injection framework is provided directly by AngularJS. This means that, rather than using a free-standing function, we can use the AngularJS controller() method to define our Controllers:
// Define "MyController" for the Dependency Injection framework
// being used by our application.
app.controller(
"MyController",
funciton( $scope ){
$scope.someValue = "All your base are belong to us!";
}
);
Here, we are defining the controller as an identifier - MyController - and a constructor function. And, once we can do this, we can get much more fine-tuned in how we actually define our constructor function.
In the following demo, I have created a FormController object that reacts-to and provides data for the HTML Form tag (and its descendants). While the demonstration is a bit trivial, notice that my FormController constructor gets instantiated, exposing its Prototype methods in the process.
<!doctype html>
<html ng-app="Demo">
<head>
<meta charset="utf-8" />
<title>Controllers With Instance Methods In AngularJS</title>
</head>
<body>
<h1>
Controllers With Instance Methods In AngularJS
</h1>
<form ng-controller="FormController" ng-submit="processForm()">
<!-- Only show this IF the error message exists. -->
<p ng-show="errorMessage">
<strong>Oops:</strong> {{ errorMessage }}
</p>
<p>
Please enter your name:<br />
<input type="text" ng-model="name" size="20"
bn-focus-on-change="submitCount"
/>
</p>
<p>
<input type="submit" value="Submit" />
</p>
</form>
<!-- Load AngularJS from the CDN. -->
<script
type="text/javascript"
src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js">
</script>
<script type="text/javascript">
// Create an application module for our demo.
var Demo = angular.module( "Demo", [] );
// -------------------------------------------------- //
// -------------------------------------------------- //
// Create a private execution space for our controller. When
// executing this function expression, we're going to pass in
// the Angular reference and our application module.
(function( ng, app ) {
// Define our Controller constructor.
function Controller( $scope ) {
// Store the scope so we can reference it in our
// class methods
this.scope = $scope;
// Set up the default scope value.
this.scope.errorMessage = null;
this.scope.name = "";
// The submit count will work inconjunction with the
// bnFocusOnChange directive to focus the form field
// when the submit count increments.
this.scope.submitCount = 0;
// The submit function has to be on the scope;
// however, we want it to be processed by the
// controller. As such, we have to pipe the scope
// handler into the Controller method.
this.scope.processForm = ng.bind( this, this.processForm );
// Return this object reference.
return( this );
}
// Define the class methods on the controller.
Controller.prototype = {
// I clean the form data, removing any values that we
// don't want to incorporate into our processing.
cleanFormData: function() {
// Strip off whitespace.
this.scope.name = this.stripWhiteSpace( this.scope.name );
},
// I greet the person with the given name.
greet: function() {
alert( "Hello " + this.scope.name );
},
// I handle the submit event on the form.
processForm: function() {
// Increase the submission count. This will cause
// the form field to be focused after all the
// watchers have finished processing.
this.scope.submitCount++;
// Clean form data.
this.cleanFormData();
// Check to see if the form is valie.
if ( ! this.scope.name ) {
// Set the error message.
this.scope.errorMessage = "Please enter your name.";
// Don't do any further processing.
return;
}
// If we made it this far, the form is valid.
this.greet();
this.resetForm();
},
// I reset the form (by resetting the scope).
resetForm: function() {
this.scope.errorMessage = null;
this.scope.name = "";
},
// I strip whitespace off the given value (leading
// and trailing).
stripWhiteSpace: function( value ) {
return(
value.replace( /^\s+|\s+$/g, "" )
);
}
};
// Define the Controller as the constructor function.
app.controller( "FormController", Controller );
})( angular, Demo );
// -------------------------------------------------- //
// -------------------------------------------------- //
// Define our directive that will focus a given input when
// expression in question changes.
Demo.directive(
"bnFocusOnChange",
function() {
var linkFunction = function( $scope, element, attributes ) {
// Get the name of the attribute we are going to
// be watching for focus.
var valueToWatch = attributes.bnFocusOnChange;
// Watch the value within the scope and focus the
// input when it changes.
$scope.$watch(
valueToWatch,
function( newValue, oldValue ) {
element[ 0 ].focus();
}
);
};
// Return the link function.
return( linkFunction );
}
);
</script>
</body>
</html>
As you can see, I am defining the FormController() as a constructor method with a prototype. This means that whenever the FormController() is instantiated (implicitly by AngularJS), it will have access to all of the instance methods defined in the prototype. This allows us to break up the Controller logic into smaller, more cohesive parts.
Once the FormController() constructor is fully defined, all I need to do is pass it off to the application's dependency injection (DI) framework for future use.
While not the focus of the demo, I have also created a AngularJS Directive that allows me to focus the input field whenever the form has been submitted. Since AngularJS controllers are never supposed to interact with the DOM (other than by providing $scope-relevant data), the focusing of an input field requires an intermediary that links the $scope data to the DOM (Document Object Model) behavior.
I know that, in the JavaScript world, creating constructor functions and defining prototypes is considered passe. But, gosh darn it, I love it. And, it's nice to know that this approach can still be used in an AngularJS application where Controller objects are instantiated implicitly by the AngularJS framework.
Want to use code from this post? Check out the license.
Reader Comments
This is really a good approach Ben. i am about to start a project using angular project and looking out lot of ideas. this is going to help me big time and thanks a lot.
Thanks Ben :)
Just as I start playing around with AngularJS and Taffy for my CFTracker project, you start blogging about it! Great information so far, hoping that there's more to come on the subject.
@Manithan,
Thanks! I also just started playing with AngularJS. It's taking me a lot of brainpower to wrap my head around it. Going from more explicit code invocation in other approaches to having such an implicit, data-driven approach is requiring me to really change the way I think about building my applications. Very stressful :)
@David,
I'll definitely be sharing some thoughts. I just start looking into this stuff for work. Very different that what I'm used-to. And the fact that you're not supposed to do any DOM manipulation in Controllers is sooooo different than the jQuery stuff I've done in the past. Fingers crossed.
Whats with all these other tuts and their global ctrl functions? hmph!
I'm thinking about wrapping angular calls with require to create classes that can be loaded in from a library. One of the sell points for angular was that it moves away from the traditional library. whatever, imo this is fine as long as I can have a place for the reusable part of my code.
You know this because you're awesome, but it might be worth mentioning that stripWhiteSpace could be handled with a filter.
ie: {{ name | trim }}
As always, great read, thanks.
I decided to create a filter to show how one might use one to trim the input field and guess what? The input field $("#field").val() is already trimmed. Ha! Silly me.
@Shanimal,
How AngularJS can play with something like RequireJS is not something I've had much time to think about. Right now, I'm building my first AngularJS app for production and we currently load ALLLLL the scripts at the bottom. We're planning to create a build-step that simply concatenates and minifies the scripts into a single file (basically what the r.js biuld does in Require).
I've already ran into a few instances of wishing AngularJS has more "loader" type functionality. But I've found some work-arounds for it.
Hi Ben,
Have you tried an example of CRUD with Angular without REST services, using straight $http.
I've looked around on their website, not so many examples of CRUD in action although they position it as Angular's sweet spot.
Best,
Pardeep.
Hi Ben.
I enjoyed this post. I think it's a great approach when building angularJS controllers. I've built 4 controllers using this pattern and had good success with it.
Do you use this approach only for controllers? Have you found the need to namespace any modules within angular for specific cases?
Appreciate your feedback.
Chris
Hi Ben,
I appreciate your pattern for AngularJS controllers, but I wanted to know how you recommend dealing with the corruption of implicit dependency injection with minified js code.
Here is a link to a 'A Note on Minification': http://docs.angularjs.org/tutorial/step_05
I like your ideas about not polluting the namespace with singleton controllers, but I can't figure out how to fix the minification problem with this approach.
hi Ben:
I am Trying To Manipulate My Data Using Jaydata Library And Searched For A Binding For DOM And Now I'm Very Excited To Find Angularjs After I Read Your Post, Please Can U Post A Link For The Integration Of Both Angularjs and Jaydata . Or At Least Advise Me With A More Suitable Way To Be Used With Angularjs..
Best Regards
Nano
You should never abbr anything, therefore use 'Demonstration' instead of 'Demo'.
@Ben Nadel,
Regarding using Angular with Require.js, check out this post which references a couple different seeds: http://stackoverflow.com/questions/13522246/recommended-scalable-angularjs-project-structure.
Great post as usual!
@Pardeep,
I have not yet used the $http service. I think the difference is that it gives you a lot more control over how the request is made. And it returns a Promise rather than an array / object.
To be honest, I don't really use the $resource in the way that it was meant to be used. I use it more like $http than I do like its a "document" with RESTful methods. I'll see if I can put together a $http experiment as I am actually curious how it works.
@Chris,
To be completely honest, I have moved away from this approach a little bit. I still use the .controller() method to define the controller; however I've moved from the "prototype" approach to more of nested-function approach:
The straw that broke the camel's back was when I created a service object for lodash.js / underscore.js. I was injecting "_" as a dependency into the controller:
function( $scope, _ ) { ... }
... and then, in order to make the _ available to the instance methods, I had to store it in the "this" scope. Then, I ended up with references like:
this._.filter()
... and this drove me crazy :D
I actually took 3 hours and completely refactored every Controller in my application to use the nested function / revealing module pattern.
@Ken,
I actually haven't used the dependency-injection notation yet, but I'm pretty sure you would do it like this:
Notice that the second argument is an array that defines a list of strings - the dependencies - to map to the arguments of the Controller instance.
I have not tried this personally; but, I'm 98% sure this is how that works.
@Nano,
I am not familiar with Jaydata, sorry :(
@Jeff,
Ha ha, well played :)
@Thomas,
Thanks, I'll take a look! I am definitely curious as I love me some RequireJS as well.
@Ben
Thanks for the reply. I appreciate your candid response about the change in your approach. I suppose this is bound to happen as we feel our way around new libraries and such. It does take a developer some time to feel his/her way around Angular. I'm going through that process right now.
I wanted to ask one last question related to your new approach. I like the way you created private methods with nested functions. Do you see any benefit in assigning anonymous functions to variables as opposed to using function declarations? In other words, do you think it would be better to use:
var foo= function() { ... }
rather than:
function foo() { ... }
I ask because in large applications, it seems better to assign methods to variables like the former example. I know jQuery uses property methods within objects like:
{
foo: function(event, ui){....}
}
So I was curious about your opinion on this.
Thanks!
Chris
@Chris,
Good question - I have to say that I don't feel that strongly one way or the other. When I first started using the approach, I actually went with variable assignment;
var getXXX = function() { ... };
But then when I presented it to my team, several other members said that they would prefer function declarations (as opposed to function "expression" above).
I didn't feel strongly, so I went with what made the team happy.
That said, when you declare $scope methods, I think you have to use function expressions:
$scope.getXXX = function() { ... };
Hi Ben, great post.<br> To be honest i am little bit confused from your last comment where you said you changed a bit your original post way. Do the gui manipulation is seperated from controller in other js file or all methods are in the controller (and logically seperated)...?<br>
or maybe you have an example published on the web for this.<br>
Thanks,
@Ron,
The GUI manipulations all still take place in the View files (as a reflection to changes in the $scope). What I am referring to in my change-of-approach is simply in how I structure my Controller file / method itself.
I used to try to use this prototype-based class definition for the controller:
... but since Controllers have so many dependencies getting injected into them, it because a large hassle to store all of them in the "this" scope so that I could use them in the instance methods.
Furthermore, the code just didn't "look right" like this. Something about it wasn't very attractive. I think the straw that broke the camel's back was the Underscore.js library: "_". In order to use that in this approach, I had to store it (the injected dependency) into a this-based variable like this:
this._ = _;
Just didn't look / feel right.
As such, I switched over to using just a function with nested functions:
With this approach, you lose the ability to keep common method references; however, to gain the benefit of not having to store your injected dependencies; since each class method is created newly for each instance of the MyController() class, they can create a closure to the class body. As such, they will have direct access to all the dependencies injected into the MyController() controller.
That said, WHAT the methods do to the $scope has not changed at all. They still affect the $scope and provide $scope-based behavior. The only difference has been in how I structure the Controller itself.
Ben, Do you have a sample of the controller built with 'function with nested functions'? Maybe a post? I'd like to see it in action.
It looks like you are creating what i call a closure based class (I love them), which imo is better because it alleviates scoping headaches that force us to create "that" variables and be tediously careful and/or woeful of scoping issues. I still don't quite get the lodash/underscore reference in the post because I thought '_' was a member of window. Why would angular need to inject it or for that matter have anything to do with it? Igor Minar comments about this in a lodash issue at github https://github.com/bestiejs/lodash/pull/266#issuecomment-17737134
If you had a demo or some code I would love to try it out so I could better understand what you are doing and why its necessary.
Also RE: closure based functions and testability... I know as a rule some people don't test private functions, but I feel that private functions are just as important to our classes as our public methods are to the outside world and should be tested accordingly. I just wonder how to handle it in a closure based class: exporting functions gives us access to the 'public' functions and we could probably add a function to expose/return the 'private' members for testing purposes. I'm just wondering if you have some other tricks.
@Shanimal,
I don't know if I have an example off-hand, but your assessment is correct: I'm creating closures inside a parent function by defining "sub functions". And, I agree that this makes the scoping MUCH easier. I used to fight this idea - I used to LOVE using ".prototype" to define all my instance methods, but it made referencing things much harder. In simple cases, it didn't bother me so much; but, in AngularJS with SO much dependency injection taking place, it finally got to me, and I found the closure-based approach much easier to work with.
As far as "_" goes, it is a property of the Window; however, in my main AngularJS app, I actually define "_" as a Factory object. This allows me two benefits:
I can inject "_" into anything (which keeps all my references as injectables).
It gives me a change to add additional utility methods.
So, for example, in my app, I might have something like this:
I tend to add a lot of "withProperty" functions to the core underscore/lodash functions so that I can call them more easily.
I love the "augment" code! I've been meaning to explore _.mixin, but I've been way too busy and haven't had a real use-case. Mr. JD is always super helpful with anything I could conjure up.
Something else I found when using "use strict" lodash needs to be injected otherwise it throws "ReferenceError: _ is not defined…"
@All,
On a somewhat related not, I finally figured out to create Base Controllers in AngularJS:
www.bennadel.com/blog/2521-Using-Base-Controllers-In-AngularJS-An-Experiment.htm
It requires using the controller as a sort of intermediary Factory. I'm not saying that I advocate the approach; but, it took me a year to figure out, so I figured I share.
On the controller naming note, I hesitantly started following the naming conventions I saw in the documentation, with has controllers named like "MyCtrl", even though I thought this was a little bad. In the end though, I'm glad I did, because I have my server side java files in the same application as the client js, and they have names like "LocationController", so now it's very obvious to me, when I see "LocationCtrl" that thats my angular controller, and not the java controller-
FYI, you have a typo "funciton" should be "function".
Very good approach. Actually this is something that I am trying to achieve but I have an issue whenever I am trying to access the controller from a directive.
In the Controller constructor you have assign the $scope into another variable with name 'scope' 'this.scope = $scope;' and I assume that this is the way of assigning any other variables that needs to be accessible from the methods of this module, such as 'cleanFormData'.
The problem arrives when the controller is getting accessed from a directive, from which of course we are not setting up the scope {this.scope = $scope;} or any other module we are using (factories from i.e.).
Do you have any workaround on this matter.
Great post, Ben! As of Angular 1.2, the AngularJS team has adopted a somewhat similar style with the new "Controller As" syntax, which allows you to place your models directly on the controller object.
This also allows for you to place methods on your Controller's prototype, as you've done here.
It's worth checking out the Egghead video on it for anyone who prefers the style you've outlined here. Good stuff!
https://egghead.io/lessons/angularjs-experimental-controller-as-syntax
Thanks for the blog, but apparently, I don't understand it well.
function myCtrl(){} // I understand this
app.controller("myCtrl", function(){}); //But why do you want to put this in the self executing function?
What is the benefit of that, except for testing?
@Ben,
You said you don't use the self executing function anymore and you are using functions in the controller it self, are you still doing this or do you have another solution.
I already saw your answer and used your factory in combination with underscore ("_"), also made it jslint compatible thanks.