If you've gone through our directives tutorial,
you may be wondering what the difference is between the scope
variable used in
the directive linking function and the $scope
variable used in controller is.
To illustrate this difference, we'll start with some sample code:
<html>
<head>
<title>$scope vs scope</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0-beta.5/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="app">
<div ng-controller="TestCtrl">
<test-directive></test-directive>
</div>
</body>
</html>
angular.module('app', [])
.controller('TestCtrl', function($scope) {
console.log($scope)
})
.directive('testDirective', function() {
return {
link: function(scope) {
console.log(scope)
}
}
})
If you open your developer console and refresh the page, you should see both
$scope
and scope
. Notice that they both have a $id
property with the same
value, which means that these two variables actually refer to the exact same
scope object.
Now let's modify our directive to use an isolated scope:
.directive('testDirective', function() {
return {
scope: {},
link: function(scope) {
console.log(scope)
}
}
})
If we refresh the page, the two $id
s will be different. This is because the
directive has created a brand new scope
object.
{info}
It's important to realize that regardless of name both scope
and $scope
objects
are instances of the Scope class. The various properties and methods of this class are documented
under $rootScope.Scope
You may be wondering why a scope object is referred to by scope
in linking
functions and $scope
in controllers. The answer lies with Angular's
dependency injections system.
A directive's link
function is just a normal JavaScript function that has
a type signature of scope,
element, attributes object
in that order. If were to flip around the arguments and declare our link function
like:
.directive('testDirective', function() {
return {
scope: {},
link: function(attr, elem, scope) {
console.log(scope)
}
}
})
The first parameter (now called attr
) would STILL be the scope
object.
Unlike link
functions, controller functions are called by Angular's injection
system. This system is more intelligent then a basic function call and will actually
inspect the function parameter names and pass the appropriate values to the controller
function.
Let's see what happens if we try to create a controller using scope
instead of $scope
:
.controller('TestCtrl', function(scope) {
console.log(scope)
})
We are greeted with an error message: Error: [$injector:unpr] Unknown provider: scopeProvider <- scope <- TestCtrl
. Angular's injection system attempts to locate the dependency
called scope
and is unable to find it so it throws an error.
A result of this injection system is that the order dependencies are declared
does not matter as long as the dependencies are able to be resolved. Say we
wanted to make an XHR requests to a server using Angular's $http
service. We
could declare the controller in either of the following ways:
.controller('TestCtrl', function($scope, $http) {
console.log($scope)
console.log($http)
})
.controller('TestCtrl', function($http, $scope) {
console.log($scope)
console.log($http)
})
As we found out above, the name of a dependency matters however a common practice is to minify JavaScript files and mangle variable names before deploying an app to production. Prevent your Angular code from breaking, it is important to add dependency annotations to your code.
To learn more, checkout our guide to explicit dependency injections with annotations.
You should also check out our guide on using ng-annotate to automate dependency annotations.