I have my variables bound to this in my controller, and in my route designation I use controllerAs: 'game'. This allows me to include them in my HTML with {{game.var}}. Sometimes, I bind objects that I want to display, but this forces me to write repeatedly {{game.object.a}}, {{game.object.b}}, {{game.object.c}}.
In a previous project using Meteor, I could set the data context with the with keyword.
{{#with object}}
{{a}}
{{b}}
{{/with}}
but I don't see a similar feature to this in Angular. The closest I've been able to make work is adding the attribute ng-repeat="object in [game.object]". This works, but it isn't very semantic. This also causes me to get a quick flash of a second element when game.object changes, as the new one loads before the first one is erased.
Is there a better solution to this problem?
Angular intentionally uses this context scope to avoid confusion between parent and child scopes. If you're not using child scopes, you can skip the controller as syntax entirely and just bind everything to $scope in your model, which turns game.a and game.b to just a and b in your view.
If you are using child scopes, you can still skip controllerAs, but then it becomes confusing what controller a given model in the view belongs to. There's no with or using syntax, so you need to declare the bound scope game.a, childGame.a everywhere you refer to these models, which might be overly verbose but is at least clear.
See this post, as well.
Regarding the flash issue, I would avoid using ng-repeat for semantic purposes. It's primary use case is to display an array of similarly structured data.
Related
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 am using ui-select to create a complex page with multuple views. For those that don't know each view is loaded via ajax when a user selects a tab (or in my case a dropdown). Views traditionally have their own controller and html templet. Views inherrit the scope of the parent page they are on, but have their own child scope.
In my case some of our views have no controller at all. They are so simple that we only have html pages for them. The problem is that I would like them to be able to toggle a boolean on a configuration object on the parent page's scope, to turn off on on some control fields on the parent page. Everything already exists, multiple pages use these views, I simply want to expand them to toggle something on the parent.
I could do this in three ways. I could in my ui-router use the resolve method to explicitly toggle the field I want when the view is loaded, but then I have to do this in each ui-router for each page explicitly; no cool code-reuse.
I could have my ui-router load a controller, where currently I have no controller, and the controller could do this. However, this means manually adding a controller that is mostly unneeded everywhere I use the view, and again doesn't feel like code reuse.
Or I could use ng-init to have my view's explicitly set the value I want at load time. To me this feels cleanest and easiest, since I don't already have a controller.
However, ng-init is frowned on, and should only be used for aliasing supposedly. Is there a cleaner approach then using the ng-init? is it really bad to have a view without a controller (to be more exact they have the parent controller still, but that isn't reloaded when a new view is opened).
The fact that you don't specify controller for the route doesn't change anything. The view has its own scope, so it can be manipulated with either route controller or with directives that don't have their own scope (e.g. ngInit).
The objective of well-known remark on ngInit
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope
is to keep you from 'html programming'. And it doesn't have too much sense because the separation of concerns is not among Angular's virtues. Angular's habit of evaluating the code from html attributes and the way how it is used by core directives prompt you to defy it even further.
Just dont init that variables at all. ng-show="smth", ng-if="smth" works ok if 'smth' is not defined and later u change it using ng-click="smth = !smth".
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 have a parent controller that manages the partial in ng-view.
It has 5 tabs, each tab has a form.
Each form has it's own controller.
I am maintaing a variable in the parent scope.
I want to modify it in one of the child controller's scope so that other controller's also view the change.
How is it possible ? Will a statement like
$scope.sharedProperty = 5
make the one in parent as 5, or create a new one with that value in child scope?
Depends upon where you do it and type. If you are using string, int, bool etc and make changes in the child controller, no other controller including the parent would see them, as this would define an new property on the child controller.
If you create a object, such as $scope.sharedObject on the parent controller, then you can manipulate it anywhere in the child such as
$scope.sharedObject.prop1="newvalue";
and all others would see the change and watch over it using $scope.$watch.
Better way to share data between controllers is to use a service.
Also read on prototypal inheritance of $scope here https://github.com/angular/angular.js/wiki/Understanding-Scopes
I would recommend to use as service to share properties, this way you avoid creating dependencies, just think if you need to reuse your view but not as a direct child but as a nested child, using a service angular would manage that for you without worries.
Check this answer for instance.
Agree with #Chandermani about services. But you can also modify parent scope like this:
$scope.$parent.sharedProperty = someValue;
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.