Angularjs: $scope vs scope - angularjs

In Angularjs, is there a specific reason to use $scope in controllers and scope (without "$") in directives link function? Is it just a convention or anything else?

The case when you do $scope in controller the Dependency Injection injects scope based on matching the variable name $scope, in this case using scope as name would not work.
For case of directive the injection is position based so you can name your variable a or b or any thing. The directive order for link function is
(scope, iElement, iAttrs, controller)
so first element is always scope object.

The module factory methods like controller, directive, factory, filter, service, animation, config and run receive arguments through dependency injection (DI). In case of DI, you inject the scope object with the dollar prefix i.e. $scope. The reason is the injected arguments must match to the names of inject-able objects followed by dollar ($) prefix.
For example, you can inject the scope and element objects into a controller as given below:
module.controller('MyController', function ($scope, $element) { // injected arguments });
When the methods like directive linker function don’t receive arguments through dependency injection, you just pass the scope object without using dollar prefix i.e. scope. The reason is the passing arguments are received by its caller.
module.directive('myDirective', function () // injected arguments here
{
return {
// linker function does not use dependency injection
link: function (scope, el, attrs) {
// the calling function will passes the three arguments to the linker: scope, element and attributes, in the same order
}
};
});
In short, in case of dependency injection the scope object is received as $scope while in case of non-dependency injection scope object is received as scope or with any name.

The reason of this behavior is that, Unlike controllers, directives don't use dependency injection, instead they are passed the scope created by the controller that is placed behind the view.
this is very tricky, So you can reuse your directive on different scopes.

The $ in "$scope" indicates that the scope value is being injected into the current context.
$scope is a service provided by $scopeProvider. You can inject it into controllers, directives or other services using Angular's built-in dependency injector:
module.controller(function($scope) {...})
which is shorthand for
module.controller(['$scope', function($scope) {...}])
However, scope could be anything, it's a function parameter name, could be foo or a12342saa.
function link( scope, element, attributes ) {
// Only scope
}

The "$" in "$scope" indicates that the scope value is being injected into the current context. But, not all references to scope are based on dependency injection.

Related

Is $scope dependecy injection in AngularJS controller definition?

All:
What confused me about dependency injection in AngularJS is its concept(I guess I have still not get the whole idea of DI):
I wonder how to tell which are dependencies that should be(and can be) injected?
For example:
If I define a controller and directive:
app.controller("maincontroller", ["$scope", "dependency1", function($scope, dependency1){
}])
app.directive("dir1", ["dependency2", "dependency3",
function(dependency2, dependency3){
return {
restrict: "AE",
scope:{},
controller: function($scope){},
link:function(scope, EL, attrs, ctrl){}
};
}])
I wonder why I need to inject $scope as dependency in controller, but no need to do that in directive definition. And what is more: if I put $scope in that directive function, it will gives me an error:
Error: error:unpr
Unknown Provider
Unknown provider: $scopeProvider <- $scope <- dir1Directive
[1] Is this $scope a dependency, or [2] my understanding about dependency is totally wrong(someone told me that $scope is not a dependency) or [3] my understanding about directive def function is wrong(only service can be put in directive def function)?
If my wrong is the last one, then how many type of dependencies does AngularJS have?
Thanks
$scope isn't actually a service !
This is why you don't have to pass it as a dependency of the directive. $scope is a local passed to the injector, a value passed to the function, as if i would do console.log(myValue). myValue is not a service.
The $injector's invoke method, which passes services to a function, can replace a requested dependency by a given value, see the docs for more details. I recognize that mixing services and simples values in the arguments list are confusing, as we can't know which one are the services and which one are simple arguments, but it works that way.
We can consider that something like this is executed when a new controller is instanciated :
var $rootScope = $injector.get('$rootScope');
$injector.invoke(controller, context /*the this binding*/, {
$scope: $rootScope.new()
});
See also the code of the controller service for further details.
Update : difference between $scope and services
Take this function :
function add(a, b) {
return a + b;
}
a and b are "simple arguments", they are the values on which the function perform calculations. Imagine that you want to broadcast a message to your app that an addition just have been performed. In Angular, we can use the $rootScope.$broadcast method.
function add(a, b) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
But here, $rootScope has never been declared. We have to load the $rootScope service in our function. Angular uses the $injector.invoke method in order to do this : it takes a function or an array (the "square bracket" notation), extract the services names from the function arguments/array elements, and calls the function with the corresponding services passed as arguments.
function add(a, b, $rootScope) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
var $injector = angular.injector(['ng']); // Creates the injector, with the services of `ng` module.
$injector.invoke(add); // Calls the function with the requested services
It will throw an error, because a and b aren't services. In fact, they don't have to be services, because they are values that we want to set ourself. For the injector, they are locals. In order to perform the addition of 2 and 3 with the add function, we have to do the following :
function add(a, b, $rootScope) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
var $injector = angular.injector(['ng']);
$injector.invoke(add, this, {
a: 2, // When requesting the `a` service, return the value `2`
b: 3 // The same here, with `b` and `3`
});
The same is done with controllers. $scope itself is not a service, but a new scope, build each time the directive is loaded. Like a and b, the newly created $scope is a value on which the function perform some logic and modifications. $scope is not a dependency, but an argument of the function. And because the $injector extracts the dependencies from the arguments list, we can't tell if an argument is a service or not (except if you already know it, but this isn't an evidence). Here, $scope is not a service, and is also the same object that in the link function in your directive. Note the fact that the documentation does not name the scope argument $scope, but scope. If scope was a service, it would throw an error, but actually it doesn't, because the link function isn't called with $injector.invoke.

Is there a way to ensure directive scope is resolved before using it?

I'm writing an element-level directive that has a number of attributes on it. These attributes are then put into an isolate scope using the '#' modifier.
The directive is used on a page that populates the attributes with expressions i.e
<my-directive attr1="{{foo.bar}}"></my-directive>
I'm finding that when the directive controller executes, the $scope hasn't resolved the expressions yet. Is there a way to force the scope to resolve before entering the controller?
No, you can't force the scope to be resolved before the controller runs. Use $observe in the controller to asynchronously get the value (and to be notified whenever the value changes -- just like $watch):
controller: function($scope, $attrs) {
$attrs.$observe('attr1', function(newValue) {
....
});
}

When to use '$injector' and just injector in angularjs?

In the documentation for angularJS injector, some of the code uses $injector, while some code uses just injector. I am confused. What are the differences between these two, and where are the appropriate places to use them? I also have the same question for $scope and scope.
When you want to use injector, scope, or any other build in angularjs provider as dependency of other service you need to prefix them with $, as angular by convention registers all build in providers with $ prefix, so scope is registered as $scope, injector as $injector etc.
When angular instantiates objects(using injector btw) it checks what dependencies object has (as it needs to inject them) and the way it's done is by checking variables names of constructor function of that object, so that's why it's so important to name variables correctly or you'll get error unknown provider ...
When you retrieve injector as following:
var injector = angular.injector(['gaad', 'components']);
you don't use $ prefix, as it's normal variable and actually you can call it whatever you want ($injector included).
When you want to have injector as dependency, you need to name it as following:
angular.module('app').factory('$exceptionHandler', function($injector) {
...
});
or (convention needed when you use minification of angularjs scripts):
angular.module('app').factory('$exceptionHandler', ['$injector', function(anyNameYouWant) {
...
}]);
Similar for scope, when used in link function in directive:
link: function(scope, element, attr) {
}
you don't have to call it $scope as nothing is injected here. It's only one of function parameters. You can call it whatever you want, but convention here is to use scope not $scope to differentiate cases when scope is injected and when is used as parameter.

How to define AngularJS Helpers that reuse injectors from their context?

How do I define a function that has access to all the injected arguments of the controller/directive/service, without passing them explicitly?
myShortHand = function(scope, http, element, url) {
http.get(url).success(function (data) {
element.html(data);
});
}
function MyCtrl($scope, $http, $element) {
$scope.name = 'Superhero';
$scope.click = function() { myShortHand($scope, $http, $element, url); }
}
This is just a simplified example, and shows the amount of boilerplate code. My shorthand actually takes 4 $ arguments and it kind of defeats the purpose of a shorthand! Note that I can't really use a service: $http could be injected in a service, but $scope and $element need to use the same injector as MyCtrl. Is there a way to define controller methods (perhaps in the prototype) that can use dependency injection in their argument list and use the same injector as the instance they are called on?
It is not clear from your question where the myShortHand function is defined but if it happens to be defined within a directive's linking function you could simply relay on the closure scope. In this case you don't need to pass arguments available in a closure.
Next, what you could do is to inject $injector into your directive and pass it in into the helper function as the only argument beside $element and $scope. Then, in your helper function you can retrieve any service by calling $injector.get method. For example, to get access to the $http service you could write:
myShortHand = function(scope, element, url, $injector) {
$injector.get('$http').get(url).success(function (data) {
element.html(data);
});
}
This technique would work if your helper can't relay on the closure scope and needs access to several services.
Having said all the above I would re-consider code split in your helpers. Usually functions that need many arguments are not well-focused and got many side effects (or are doing too much).

How to access variable defined in directive to controller?

I have to access variable defined in directive and access it in the controller using angularjs
directive :
app.directive('htmlData', function ($compile) {
return {
link: function($scope, element, attrs) {
$(element).on('click', function() {
$scope.html = $compile(element)($scope).html();
});
return $scope.html;
}
};
});
and use $scope.html in controller.
Since you are not creating an isolate scope (or a new scope) in your directive, the directive and the controller associated with the HTML where the directive is used are both using/sharing the same scope. $scope in the linking function and the $scope injected into the controller are the same. If you add a property to the scope in your linking function, the controller will be able to see it, and vice versa.
As you set the variable in the $scope, all you got to do is to bind to it normally. In your case, osmehting like:
<div html-data>{{html}}</div>
Maybe you're not seeing the update because it lacks a $scope.$apply().
Anyway, let me say that I see two problems on you code. First of, you could use ng-click directive to attach click events to your HTML.
Secondly, why would you recompile your HTML every time? There is almost no need for that. You may have a big problem, because after first compilation, your template is going to be lost, so recompiling will render it useless.
If you need to get the element, you can inject $element.

Resources