AngularJS Made Easy, by Example – Part 4; Controllers

The fourth part of a series on rapidly learning the AngularJS JavaScript framework. Today I illustrate the use of Controllers to get some data into our Views.

In the world of design patterns there are strict policies specifying the responsibilities of Controllers within an MVC framework. A Controller’s job is to present data to the View, and to pass any data back from the View to the Model, and there should be no business logic within the Controller. When getting up to speed with AngularJS I believe the need for a Model to be unnecessary; in my first few AngularJS applications I have found little need for them, instead preferring to use the Controller to communicate data to-and-fro between client and server via Ajax, and letting my stricter server-side .NET Models compute any business logic.

When coding Controllers in AngularJS I follow the rule “one Controller to one View”. I use this one-to-one policy because it’s considered best practice for AngularJS; it makes it easier to organise an application. If we’re being pedantic this makes AngularJS less of an MVC framework and more of an MVVM framework, because in essence it manages what we might call a ViewModel, but I’m not bothered about the labelling semantics.

Here is a slightly simplified example of one of my Controllers:

app.controller('TeacherTeachingClassStudentsController', ['$scope', '$routeParams', 'TeacherFactories', function ($scope, $routeParams, TeacherFactories)
{
	//Private members
	var _TeachingClassId = $routeParams.prmTeachingClassId;


	//Public members
	$scope.teachingClass = {};
	$scope.teachingClassStudents = [];


	//Private methods
	function Init()
	{
		LoadTeachingClass();
		LoadTeachingClassStudents();
	}


	function LoadTeachingClass()
	{
		var comms = TeacherFactories.GetTeachingClass(_TeachingClassId);
		comms.Send()
		.done(function ()
		{
			$scope.teachingClass = comms.Data();
			$scope.$apply();
		})
		.fail(function ()
		{
			alert(comms.Status());
		});
	}


	function LoadTeachingClassStudents()
	{
		var comms = TeacherFactories.GetTeachingClassStudents(_TeachingClassId);
		comms.Send()
		.done(function ()
		{
			$scope.teachingClassStudents = comms.Data();
			$scope.$apply();
		})
		.fail(function ()
		{
			alert(comms.Status());
		});
	}


	//Call to initialiser
	Init();
}]);

In the first line, where I define the Controller as belonging to my AngularJS module “app”, I inject three dependencies into it; $scope, $routeParams and TeacherFactories. As with the “config” declaration (back in Part 2) note that the names of these dependencies appear twice; by additionally defining the names as strings during injection we will prevent JavaScript minifiers from mangling our code. $scope is typically required in all AngularJS Controllers, and thus its inclusion can be seen as something of a necessary default. $routeParams allows me to access parameters passed from other View inside the URL (such as within an anchor tag’s href, see Part 3 of this series for an example). TeacherFactories is a Factory I created that provides several functions that communicate with the server via Ajax, and in the next article in this series I will give a more detailed illustration of my use of Factories and Services. For now, all we need to know is that my Factory functions return jQuery Promises, so I can handle the completion of Ajax requests via done and fail within the Controller.

Within the body of the code, the first thing I do is grab the prmTeachingClassId from the URL parameters and place it in what I’m referring to as a “private member”. Only data values attached to the $scope object are able to be injected into an AngularJS view, and next I define two objects with precisely this purpose in mind; $scope.teachingClass and $scope.teachingClassStudents. The contents of these two objects – the former a few values, the latter an array of objects – will be bound to the View shown back in Part 3 of this series.

The section labelled “Private methods” houses a couple of procedures that call Factory functions to populate the $scope values I previously established. I call these “private methods” as they cannot be triggered from within this Controller’s View. If I wished to define a “public method” I would do this (code taken from one of my other Controllers):

$scope.SaveThresholds = function ()
{
	var comms = TeacherFactories.PostSaveStudentSubjectThresholds(_StudentId, $scope.teachingClass.subjectid, $scope.studentSubjectProgressSummary.lowerthreshold, $scope.studentSubjectProgressSummary.upperthreshold);
	comms.Send()
	.done(function ()
	{
		$scope.$apply();
	})
	.fail(function ()
	{
		alert(comms.Status());
	});
}

This method is callable from within the View because it is attached to $scope. To call this method I would use the AngularJS built-in Directive data-ng-click, housing it within a HTML BUTTON tag, for instance:

<button id="btnSaveThresholds" type="button" data-ng-click="SaveThresholds()">Save Thresholds</button>

Back to the innards of the Controller, the use of $scope.$apply() is a contentious issue, with both proponents and detractors. The latter argue that the use of $scope.$apply() within Controllers should be resisted, and that Directives are a more natural home to trigger updates between $scope and View. In turn this forces the programmer to build Directives for every chunk of their HTML that requires some form of data manipulation (see Part 3 of this series for an example of how I use scope.$watchCollection, a sibling of $scope.$apply(), to sync data between Controller, Directive and thus View). I completely agree that the encapsulation of units of functionality into discrete and reusable components is the way to go, however this does make rapid prototype development a more time consuming endeavour, and seeing as my main purpose, first-and-foremost, is to get something actually working, I see no problem in using $scope.$apply() within the Controller. In an ideal world I would later refactor and refine the Controller to siphon off responsibility into a series of Directives similar to the one in Part 3 of this series, thus delegating the task of updating the scope.

Finally, I make a single call to my initialisation function Init(), which sets the ball rolling by launching the series of methods described above.

In the next article in the series I’ll be discussing Factories, Services and Providers, and give an example of my implementation of these to encapsulate Ajax calls.

Back to Top