From the AngularJS documentation Understanding controllers:
Associating Controllers with Angular Scope Objects
You can associate Controllers with scope objects implicitly via the
ngController directive or $route service.
Those two approaches are well known and mainly used across AngurlaJS applications.
What draw my attention is this part found in Developer Guide / Understanding the Controller Component:
Associating Controllers with Angular Scope Objects
You can associate controllers with scope objects explicitly via the
scope.$new api or implicitly via the ngController directive or $route
service.
So in addition to implicit association of Controller with scope there is mentioned also explicit way by using scope.$new api.
I am aware that scope.$new is used to create new [isolated] scope but I somehow don't understand how exactly is it related to explicit association of controller with scope.
It would be great to see some practical use case and/or more detailed explanation.
It's primarily used during testing when you want to construct a controller directly and do something with it.
You can use the $controller service to do this:
var scope = $rootScope.$new();
var ctrl = $controller(MyAwesomeController, { $scope: scope });
Now I can manipulate the scope variable directly and check for side effects:
scope.foo = 'bar'
scope.$digest();
expect(scope.bar).toBe('YEAH BABY!!!');
The $controller service will instantiate a controller with all of the dependencies injected like you would expect. It also allows you to pass in a hash of locals that will override any of the dependencies with something you provide explicitly.
Again, this is very useful for testing because you can swap out a service with a mock if needed.
Related
I just started learning about Angular JS. Though I can code up an Angular module, I have lot of questions on how Angular works.
How does $scope work? I understand that one root scope is created at the point of declaration of ng-module. This represents the DOM in some way and also watches the DOM for any changes in properties, events, etc.. How does this watch implemented?
How is it ensured that this scope object is ready once the page loads, so that it can be used by the controller? How does access permissions work with the scope object, cases where controller might not have access to a variable?
In which cases you might want to use an injector explicitly?
What's lifecycle of an angular app in detail? Is there any client side versus server side component or is it entirely client side? When I say, ng-repeat, this is still client side?
$scope is what angular uses to expose variables to the template. Think of scope as your 'view model'. Anything attached to the scope will be exposed. $scope uses prototyped inheritance. This means that children of a scope will have access to the parents properties. There are more detailed nuances to scope inheritance involving change detection.
Dependency injection solves the 'ready' problem. Angular takes care of injecting the scope properly in each controller already instantiated.
If you need to create a function that modifies a service when that service hasnt been instantiated yet. For example configuring an error handler. Only providers will be available, using the injector we can grab the service when the error is thrown.
`
function(rejection) {
if (rejection.status === 401) {
// have to lazy inject cause http interceptors are defined
// at provider configuration
var sessions = $injector.get('sessions');
var $state = $injector.get('$state');
sessions.logout().then(function() {
$state.go('login');
return $q.reject(rejection);
});
}
}
return $q.reject(rejection);
}
I recommend reading this documentation to shed some light on 'the angular way'. https://docs.angularjs.org/guide/scope
I have been reading wrox angular book. Inside the book the author describes that a method of sharing data between controllers is to
Have a property on the root scope
Update that property on the root scope
Broadcast the fact that the property was updated
All children scopes that need to know , will listen for the broadcast.
as opposed to expose an object on a Service and letting angular's two way databinding do all the heavy lifting. Why would anyone go with the 'root scope publish/subscribe' methodology, instead of exposing an object on the service?
That's interesting question.
First we should consider differences on various levels:
Scope
in case of $rootScope we define variable in global scope
in case of shared services we can inject this service to controllers that really use this value
Extensibility
$rootScope - we have limited options to add additional logic to work on this value (we can define another global function)
shared services - we are free to define any kind of logic
Encapsulation
$rootScope - all object defined in $rootScope would be visible in all modules
shared services - we can decide what is visible and what is not
Modularity
$rootScope - global variables is not places in module spaces
shared services - service is a separate module for application
Maintaining
$rootScope - it's very hard to find which components use our $rootScope variable.
shared services - we can see which services we use and we can find in which component we use this service
Binding
$rootScope - it is easy to setup two-way binding in several controllers on one variable in $rootScope
shared services - could be tricky to enable two-way binding
In my opinion this is only useful for make really global variables.
Say you have two controllers A and B, and a service S, storing the common data.
When A changes data in S, B cannot directly change its scope value by understanding that data in S has changed. Someone has to say to it that data in S has changed and update its scope according to this change. This may be done two ways.
One is rootScope broadcast: service S broadcast changes and B listens this broadcast.
The other $scope.$watch: In controller B, scope must watch the changes in service data.
It depends which kind of data you are managing, if you are for example relying on a DB where you perform CRUD actions, you'd like a service to just interact with the DB.
That's called a stateless service, some people vouch for it and some are against and prefer to have state also on the service, exposing the object as you mentioned.
I'll leave you a couple resources with more information on the topic so you can decide which solution suits you best
http://www.johnpapa.net/sharing-data-in-an-angular-controller-or-an-angular-service/
http://www.webdeveasy.com/angularjs-data-model/
So many controllers I see have no members except for this on scope. I would imagine shared validation and business logic code never accessed directly from a binding expression need not know about the non-scope code, and only members accessed from the view actually have to be on the scope.
Could someone please clear this up for me?
That's right. Functions available to your expressions in the partials and directives should be assigned to the $scope object.
All your other logic does not have to be. If you're planning to reuse any logic between controllers it is better to extract it into a factory/service.
$scope is what binds the controller to the view; it is a special prototypical object that can be dynamically adjusted, making it very easy to quickly hook up to the view. However, using $scope directly isn't the only way to handle your controllers.
Due to the prototypical nature of $scope, and the fact that there can be multiple scopes present on a given page, it's been commonly suggested to follow the "Dot Rule" when dealing with $scope. In essence, the dot rule simply suggests that instead of assigning primitives to $scope, e.g. $scope.myString, it's always preferable to "use a dot", or, assign objects to the $scope, a la $scope.someObject.myString.
Starting with angular 1.2, a new syntax was introduced which is designed to help with this task, the ControllerAs syntax. In essence, it allows you to assign your controller (which is already an object) directly to the $scope, using a syntax like ng-controller = "someController as ctrl", and then refer to all of your bindings as properties of the controller, a la ctrl.myString. You are now automatically using the "Dot Rule", without even having to think about it.
app.controller('someController', function () {
var ctrl = this; //self reference for this
ctrl.myString = 'Some title';
});
Note that even though we are still ultimately using $scope, we don't actually need to provide $scope as a dependency on the controller, and don't need to interact with it directly. However, it is still available, should we need to use an advanced service like $broadcast.
Using the ControllerAs syntax does not eliminate $scope, because the controller is still a property of $scope, but it does allow you to break the coupling between your controller and scopes down a bit, and can make your HTML / controllers easier to read. having customerCtrl.name and companyCtrl.name is much easier to understand than having two name properties which are only really understood in context of the surrounding elements.
Unfortunately, the vast majority of documentation and tutorials still use the $scope object directly, and migration to the ControllerAs syntax has been slow. However, $scope will not exist in angular 2, and the first step to migrating from 1.x to 2.x will be to convert to the ControllerAs syntax, so if you write your code this way now, it will make it trivial to migrate.
There can be methods in controllers that are not in $scope also (May be you'll be using them as helper methods that'll be calling from $scope methods). Normally methods you wanted to call my view or variable that needed to be bind to view are kept in $scope.
I'm just starting to look at Angular, but having a hard time wrapping my head around the need for $scope. Javascript already has a concept of scope via the context (i.e. this) and allows programmers to inject that context on a function using call or apply.
Are there any differences between Angular's, $scope, and the keyword this?
If there is a difference, then what is the value of this within a controller or directive?
Thanks in advance :)
Yes, they are not the same at all. The constructor is just an instantiated new ed constructor (the function you wrote) created by the injector.
$scope is more conceptually related to the DOM. In that elements with ng-controller get that $scope and child elements do as well. If a child element with its own scope (controller/directive) had the same properties as the parent scope You wouldn't be able to access them. It also has all of the internal information angular uses in its digest loop (dirty checking/ data binding) like watches,events,etc. I'd have a read through this
As for the myCtrl as syntax, this is nice but all it really does is put the controller instance onto the scope. With the name that you set.
eg myCtrl as foo is basically $scope.foo = myCtrlInstance;. Which you are capable of doing in your controller as well.
Is it better to use var than to use $scope. in AngularJS variables inside functions?
My reason for asking this is not as simple as it seems. I recently read about $watch, $digest, $apply. Although I didn't understand it completely, I understood that $digest works in a loop.
This post explains it quite well.
How do I use $scope.$watch and $scope.$apply in AngularJS?
So if you have $scope.myVar defined in your controller, you are explicitly telling Angular to monitor the changes on myVar. Doesn't this activity slow down the overall system?
Short answer: yes, it's better to declare any variable that's purely internal to your controller as a javascript variable ("var") rather than adding it to an Angular scope.
Angular scope objects provide many nice features that allow them to act as the model in an model-view-*(MV*) architecture (for instance data binding). Or said another way, as the Angular guide to scopes says "Scope is the glue between application controller and the view".
It's best to only put objects that you need in your model, that you need bound both to the DOM and your controller/services/..., on the scope as those features do come at a performance cost, as you point out. And it can also be confusing to other's who read your code if your scopes are "cluttered" with variables that aren't actually part of your model.
Here's the specific features of scopes from Angular scope docs:
Scopes provide APIs ($watch) to observe model mutations.
Scopes provide APIs ($apply) to propagate any model changes through
the system into the view from outside of the "Angular realm"
(controllers, services, Angular event handlers).
Scopes can be nested to limit access to the properties of application
components while providing access to shared model properties. Nested
scopes are either "child scopes" or "isolate scopes". A "child scope"
(prototypically) inherits properties from its parent scope. An
"isolate scope" does not. See isolated scopes for more information.
Scopes provide context against which expressions are evaluated. For
example {{username}} expression is meaningless, unless it is evaluated
against a specific scope which defines the username property.