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.
Related
Are there strong reasons one should not pass $scope to a service?
I realize services are to be reusable singletons and so passing a (potentially) large object to the service might have maintenance issues, but let's assume solid documentation that would declare "the following members of $scope are required." I just think it looks cleaner than passing in 4-6 parameters.
Any other concerns for this practice would be greatly appreciated. I am being evaluated on this piece of code! :)
Incidentally, I JUST realized how active the angularJS community is on here and I am very grateful!!
Read this article
5 Guidelines For Avoiding Scope Soup in Angular
In point 5, he explains the reason for not passing $scope to Services.
I will quote some of his writing here
The problem with relying on values to be set from a parent controller, or relying on a child controller to set data back up on the parent scope, is that you end up with an implicit coupling that isn't easy to see at first.
Worse, it means the controller implementation is completely dependent on the ordering of the bindings in the view! I can't use or test those controllers independently of each other, because they are coupled together by $scope.
$scope should be used as a glue between View andController. If you want to send some value from $scope to a service, just extract those values out and pass them to the service.
If you see that you are having to pass too many values, consider grouping the values in objects and then passing them.
Changing few things in $scope, might affect your app in unwanted ways.
It is an anti-pattern to use $scope outside controllers.
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.
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.
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.
Is there a way in AngularJS to programmatically bind a controller method using only javascript(instead of using ng-* markup)?
Something like
$("#foo").click(FooController.foo)
PS: I don't have access to $compile
Do you really need controller's methods or scope's?
in case if you need scope's you can use angular.element('#myid').scope().method
But please keep in mind that any modification of scope's values are not detected if you do them outside of AngularJS event processing, so you need to wrap your code into $apply function of scope.
Also keep in mind that in AngularJS DOM modifications from JS are generally "big no no"
Ofcourse there are controller's functions (created using "this.xxx = function () .."). I am not sure that it is good to access them as they are kind of private, but you should be able to access same way: element(..).controller().method
It is not officially supported. For sure it is technically possible with some hacks but binding event handlers from JavaScript would mean getting access to a DOM element from a controller and it goes against principles of AngularJS.
To extend #Valentyn's answer somewhat, if you had wanted to access a controller method (and not a $scope method) -- e.g., a method defined with this in the controller constructor function (on the Angular homepage, the "tabs" directive defines function "addPane" on the controller using this) -- you can use
angular.element('#myid').controller().method()
You can, but you shouldn't. You'll likely run into trouble down the road.
If you can give a bit more explanation as to why you want to do this, there might be a better (and easier) method.