Performance of $rootScope.$new vs. $scope.$new - javascript

My current Controllers $scope is kind of thick with: $watch and event handlers.
On one point I need to create a new scope for a modal, which does not have its own controller, because its quite simple. Still it needs a property of the current $scope. I was wondering which of the following solutions is better, and why?
a)
var modalScope = $rootScope.$new();
modalScope.neededValue = $scope.neededValue;
b)
var modalScope = $scope.$new();
// modalScope.neededValue already there
Should I be worried that the created modalScope will watch also those expressions and events? Any other aspects I should be aware of?

Option a) only copies the value once and doesn't keep it in sync with $scope, so it may cause confusing bugs. If your modal has a deep scope hierarchy then there are some cases where option a) might give you a very slight performance advantage (calling $broadcast on $scope means it needs to cover a smaller scope tree), but all in all I'd say option b) is the way to go.
Your $watches won't really benefit from the location of the scope unless you're only $digesting a particular scope.
EDIT: I see your modal is fairly light, in this case, definitely go with option b), the performance advantage from a) will be negligible.

Related

Angular.js - Performance implications of binding a model to be watched, using $watchCollection

I'm trying to get a better understanding of a new codebase, and haven't found a ton of info (or maybe I'm not fully understanding what I have read) about the performance implications of some choices that were made. The previous maintainer isn't available for insight.
Each controller is set up to make its data request before being rendered initially using this pattern:
$routeProvider.when('/path', {
resolve: { someMethod: function($q) {
var deferred = $q.defer();
.... do some stuff ...
return deferred.promise;
}
});
This is then injected into the controller - that part makes sense and from my understanding is more performant because you're saving digests by waiting for the data to come through first. A loading state is shown by default and removed when we digest.
The value of the injected param - in this case someMethod - is then assigned to this.someModel.
Throughout the controller, remaining methods and properties are set with this.doStuff = function() { ... }, this.property = 'some string', etc.
In the end, a watch is setup and properties made available to the $scope by setting a $watchCollection like this:
$scope.$watchCollection(angular.bind(this.someModel, function() {
return this;
}), function(newTitle, oldTitle) {
if (newTitle.title !== oldTitle.title)
{
self.updateThings(newTitle);
}
});
I understand that it's binding the context of the watch to the current context of this, then watching to changes on its properties using $watchCollection which will perform shallow reference checks of its top level properties.
Per this article I get that simply using properties in my templates with {{ property }} will cause them to be watched, but in this case they aren't watched until they're bound to the $watchCollection.
Of course this is less expensive than using $watch and passing it true for the optional object equality param, but is it better or worse from a performance perspective than putting things directly onto $scope? Why, specifically?
I've never seen it done this way before, so any readings, insights, or things that could give me a better understanding will be greatly appreciated.
The only real thing I can think of is that the author wanted to keep everything off the scope directly, only populating properties of the controller instance, which will be available on the scope via the property specified in the controllerAs property on the route definition object.
However, when doing it that way, if you want to set up $watches on the scope to be notified of changes to something, you can't, because you don't actually know what property of the scope it will be published on.
You could settle on a convention, such as vm, ctrl or whatever, for the controllerAs value, but that is not ideal and is fragile and not really a concern of the controller code itself. But if you made that concession, you could use $scope.$watchCollection('vm.someModel', ... instead.
One final possible reason is that $scope.$watch() and $scope.$watchCollection() always ultimately end up dealing with a function as the thing they use to get the value of the thing being watched during the digest. If you pass a string, it uses the $parse service under the covers to turn it into a function. If you pass a function directly, no conversion is necessary, so this can be perceived as a minor performance improvement.
The real reason(s) may be one, all or none of these, but they are valid reasons for adopting this approach. I actually admire it for its purity and strict adherence to "controller as", which is the in-vogue strategy encouraged by the Angular team and community at the moment.

Functions results in angularjs view

I'm afraid this question is pretty stupid....
Using the {{current.name}} syntax I can show the name of $scope.current in the view. I set current when I switch from the list view (/mythings) to editing an item (/mythings?id=someId).
Actually this is redundant as I have the information both in the $location and in $scope.current. This redundancy makes it more complicated to understand, so I'd like to get rid of it.
I replaced current by a current item returning function and hoped it would work (like it does in many other cases). But it doesn't, I need to write {{current().name}} everywhere, which I tend to forget.
Maybe I'm doing it all wrong? I'm a beginner here.
Is there a way to make it work? Somehow bless current so it always gets evaluated before use?
Both alternatives discussed have pros and cons:
Using a property (current) is easier (and more natural) to reference in the view, but it needs to be manually kept in sync with the location.
Using a function (current()) takes care of the keeping in sync issue, but is less intuitive.
All things considered, I would value the auto-syncing feature higher and go for the second alternative (current() function).
But, what if we could get a third option that combines the best of both worlds ?
And in fact we can :)
We can use the concept of Object Properties, introduced by ECMAScript 5, and define some "computed properties" for our $scope (or service?).
Without getting into much detail (see the docs for more details), we could augment a scope like this:
.cotroller('someCtrl', function ($location, $scope) {
Object.defineProperty($scope, 'current', {
get: function () {
return {
name: $location.path();
id: $location.search('id');
};
}
});
Now, we can access $scope.current.name and $scope.current.id as if current were a normal property of $scope (intuitiveness !) and current will be automatically computed based on the current location (auto-sync !).
Thank you ECMAScript 5 :)
This article provides a simple and clear introduction to the concept.

Does it affect Angular performance to have everything in scope?

I was wondering if anyone has advice for the usage of $scope vs plain JavaScript objects when inside a module. For instance I have some variables in a controller that I am attaching to $scope for my convenience but they could just be a regular object inside the controller without any functional difference.
My question is does it affect performance when Angular enters a digest cycle to have everything in scope?
Is this :
$scope.viewpanel = {};
$scope.viewpanel.date = new Date();
$scope.viewpanel.day = $scope.viewpanel.date.format('d');
$scope.viewpanel.week = $scope.viewpanel.date.format('W');
$scope.viewpanel.month = $scope.viewpanel.date.format('m');
$scope.viewpanel.year = $scope.viewpanel.date.format('o');
better or worse than this :
var viewpanel ={};
viewpanel.date = new Date();
viewpanel.day = viewpanel.date.format('d');
viewpanel.week = viewpanel.date.format('W');
viewpanel.month = viewpanel.date.format('m');
viewpanel.year = viewpanel.date.format('o');
Yes, it does affect performance if you are using it in your template, which creates watchers automatically. If they aren't bound to anything, then it doesn't matter that much (there is some overhead added), but lots of people don't like crowded scopes. This wiki article on Scopes will explain exactly what happens when you add something to $scope.
Another alternative is bind-once or angular-once. It's like regular binding but only sets it one time so it reduces the number of watchers.
Also, for your specific example, you may consider using filters to format dates. Although that won't help with performance per se, it'll give you cleaner code.

AngularJS: When to pass $scope variable to function

I am using the TodoMVC app to get better with the AngularJS framework. In the index.html on lines 14-16 you see this:
<form id="todo-form" ng-submit="addTodo()">
<input id="new-todo" placeholder="What needs to be done?" ng-model="newTodo" autofocus>
</form>
Notice how the ng-submit directive calls the addTodo() function without the newTodo model being passed as an argument.
A short time later I came across the following code in the very same file on line 19:
<input id="toggle-all" type="checkbox" ng-model="allChecked" ng-click="markAll(allChecked)">
You can see the author decided to pass the allChecked model to the markAll() function this time. If I understand correctly, they could have referenced $scope.allChecked inside the controller instead of passing it in.
Why use two different approaches in the same file? Is one approach better in some circumstances? Is this a case of inconsistency or is there a deeper logic being used?
I would prefer to always pass in arguments to the function:
It's clearer what parameters the function expects.
It's easier to unit-test because all parameters are injected into the function.(good for unit- testing)
Consider the following situation:
$scope.addToDo = function(){
//This declaration is not clear what parameters the function expects.
if ($scope.parameter1){
//do something with parameter2
}
}
And even worse:
$scope.addToDo = function(){
//This declaration is not clear what parameters the function expects.
if ($scope.someobject.parameter1){ //worse
}
}
Because of scope inheritance parameter2 may come from parent scope, accessing parameter2 inside the function creates a tight coupling, also causes troubles when you try to unit-test that function.
If I define the function like this:
//It's clearer that the function expects parameter1, parameter2
$scope.addToDo = function(parameter1, parameter2){
if (parameter1){
//do something with parameter2
}
}
In case your parameter2 is inherited from parent scope, you could still pass it in from the view. When you do unit-testing, it's easy to pass all parameters.
If you have ever worked with ASP.NET MVC, you would notice something similar: the framework tries to inject parameters into action function instead of accessing it directly from Request or HttpContext object
It's also good in case others have mentioned like working with ng-repeat
In my opinion, Controller and Model in angular are not quite clearly separated. The $scope object looks like our Model with properties and methods (Model contains also logic). People from OOP background would think that: we only pass in parameters that don't belong to object. Like a class Person already has hands, we don't need to pass in hands for every object method. An example code like this:
//assume that parameter1 belongs to $scope, parameter2 is inherited from parent scope.
$scope.addToDo = function(parameter2){
if ($scope.parameter1){ //parameter1 could be accessed directly as it belongs to object, parameter2 should be passed in as parameter.
//do something with parameter2
}
}
There are two parts to this answer, the first part is answering which one is the better option, the other part is the fact that neither of them is a good option!
Which one is correct?
This one is:
$scope.addToDo = function(params1, ...) {
alert(params1);
}
Why? Because A - it is testable. This is important even if you are not writing tests, because code that is testable is pretty much always more readable and maintainable in the long run.
It is also better because of B - it is agnostic when it comes to the caller. This function can be reused by any number of different controllers/services/etc because it does not depend on the existence of a scope or on the structure of that scope.
When you instead do this:
$scope.addToDo = function() {
alert($scope.params1);
}
Both A and B fail. It is not easily testable on its own, and it cannot easily be reused because the scope you use it in might be formatted differently.
Edit: If you are doing something very closely tied to your specific scope and running the function from the template, then you might run in to situations where trying to make it reusable just doesn't make sense. The function simply isn't generic. In that case, don't bother with that, some functions cannot be reused. View what I wrote about as your default mode but remember that in some cases it won't fit.
Why are both wrong?
Because as a general rule you should not be doing logic in your controllers, that is the job of a service. The controller can use a service and call the function or expose it in a model, but it should not define it.
Why is this important? Because again it makes it easy to reuse the function. A function that is defined in a controller cannot be reused in another controller without putting limits on how the controllers are invoked in the HTML. A function that is defined in a service can be injected and reused wherever you feel like it.
But I don't need to reuse the function! - Yes you do! Maybe not right now and maybe never for this specific function, but sooner or later you will end up wanting to reuse a function that you where convinced that you would never need to reuse. And then you will have to rework code that you have already half forgotten, which always take extra time.
It's better to just do it properly from the start and move all logic that you can into services. That way, if you ever need them somewhere else (even in another project) you can just grab it and use it without having to rewrite it to fit your current scope structure.
Of course, services don't know about your scope so you are forced to use the first version. Bonus! And don't fall for the temptation of passing the entire scope to a service, that will never end well :-)
So this is IMO the best option:
app.service('ToDoService', [function(){
this.addToDo = function(params1, ...){
alert(params1);
}
}]);
And inside the controller:
$scope.addToDo = ToDoService.addToDo;
Note that I wrote "general rule". In some cases it is reasonable to define the function in the controller itself as opposed to a service. One example would be when the function only relates to scope specific things, like toggling a state in the controller somehow. There is no real way to do that in a service without things becoming strange.
But it sounds like this is not the case here.
The Zen of Angular suggests:
Treat scope as read only in templates
Treat scope as write only in controllers
Following this principle, you should always call functions explicity with parameters from the template.
However, in any style you follow, you do have to be careful about priorities and order of execution of directives. In your example, using ng-model and ng-click leaves the order of execution of the two directives ambiguous. The solution is using ng-change, where the order of execution is clear: it will be executed only after the value changes.
The custom behavior methods, such as ng-click, ng-submit etc. allow us to pass parameters to methods being called. This is important since we may want to pass something that may not be available freely across till the handler in the controller. For eg,
angular.module('TestApp')
.controller('TestAppController', ['$scope', function($scope) {
$scope.handler = function(idx) {
alert('clicked ' + idx.toString());
};
}]);
<ul>
<li ng-repeat="item in items">
<button ng-click="handler($index)">{ item }</button>
<!-- $index is an iterator automatically available with ngRepeat -->
</li>
</ul>
In case of your second example, since allChecked is within the scope of the same controller which defines markAll(), you are absolutely correct, there is no need to pass anything. We are only creating another copy.
The method would have to simply be refactored to use whats available in the scope.
$scope.markAll = function () {
todos.forEach(function (todo) {
todo.completed = $scope.allChecked;
});
};
Hence, even though we have the option to use parameters in these methods, they are only required some of the time.
I think that this is simply a case of inconsistency in the code. I've pondered this question before and came to the following conclusion...
Rule: Don't pass $scope variables into $scope functions.
Reading the controller code should be enough code to demonstrate what the function of the component will be. The view should not contain any business logic, just bindings (ng-model, ng-click etc). if something in the view can be made clearer by being moved to the controller, so be it.
Exception: Allow conditional ng-class statements (e.g. ng-class='{active:$index==item.idx') - putting class conditionals in the controller can be very verbose, and muddies the logic of the controller with ideas from the view. If it's a visual property, keep it in the view.
Exception: You are working with an item in an ng-repeat. For example:
<ul ng-repeat="item in items">
<li><a ng-click="action(item)"><h1>{{item.heading}}</h1></a></li>
</ul>
I follow these rules when writing controllers & views, and they seem to work. Hope this helps.
Perhaps to illustrate that you can? There is no functional difference between the two assuming that they are the same controller. Note that there are situations where child scopes are generated in which case you won't have the same scope as the controller any longer.

Should I always bind functions to $scope objects?

When I'm creating controllers, I always add functions to the $scope object, like so:
function DummyController($scope) {
$scope.importantFunction = function () { /*...*/ };
$scope.lessImportantFunction = function () { /*...*/ };
$scope.bussinessLogicFunction = function () { /*...*/ };
$scope.utilityFunction = function () { /*...*/ };
}
Of course I'm keeping my controllers encapsulated nicely, making sure that business logic lies in appropriate components (injected via DI) or services. So the controller is focused on orchestrating things between UI and Backend.
However, as you can see - there are still plenty of different kinds of functions. I like to keep more of them, as it improves readability IMHO.
The question
Is that a good practice to have lots of functions attached to $scope object? Does it have performance overhead? I know that $scope is a special kind of object, being constantly evaluated in Angular's digest cycle, so am I doing this right or will I ask for trouble by sticking to my approach?
(Please note: I'm not looking for alternative approaches. I'm looking for some well thought analysis of people who know Angular's internals.)
Thx!
UPDATE:
The answer from Anders is very good and shows you some paths to follow. Today I ran into this beauty, a chrome extension for Angular Js debugging and performance monitoring. It shows you all the scopes and assigned variables, along with some interesting performance graph. A must have for any Angular developer!
Update:
Should I add all of my functions and variables to the scope?
No, you should only add functions and variables to your scope if you need to access them in your template. A variable that is only accessed in the controller function shouldn't be on the scope, it should be local to the controller function.
Will if affect performance if I add a lot of functions to the scope?
Generally, no. Functions on your scope that are executed like ng-click="myFunction()" should not affect performance in a noticeable way. But if your function is executed like this: {{myFunction()}} it will get executed for every digest as Angular needs to know if the return value of it has changed so it can update the UI.
Will it affect performance if I add a lot of variables to the scope?
It can affect performance if you use them in places where Angular will dirty check them. Those cases are where you print them out like {{myVariable}}, if you use them in ng-model="myVariable", ng-show="myVariable", etc. Directives like ng-click does not perform dirty checks, so that doesn't slow things down. The guys behind Angular recommends you not to use more than 2000 expressions on one page that will require repaints/dirty checks, as your performance will start do degrade after that. The limit of 2000 is something they've found while researching performance in Angular.
Note that just because you add a property on the scope, it doesn't mean that Angular will perform dirty checks on it. They have to be used in your template for dirty checks to be performed (but not for ng-click).
If I want maximum performance in my Angular app, what should I be aware of?
Like I mentioned above, try to keep the number of bound template expressions below 2000. And if you implement watches on your scope, make sure that the expression for the watch executes really quickly. This is an example of how you shouldn't do it:
$scope.items = [];
for (var i = 0; i < 1000; i++) {
$scope.items.push({prop1: 'val1', prop2: 'val2', prop3: 'val3'});
}
$scope.$watch('items', function() {
}, true);
By adding the true as the third argument to $watch, you're telling Angular to loop through $scope.items for every digest cycle to check if any property of the thousand items have changed, which will be costly both in time and memory.
What you should do instead is:
$scope.items = [];
for (var i = 0; i < 1000; i++) {
$scope.items.push({prop1: 'val1', prop2: 'val2', prop3: 'val3'});
}
$scope.$watch('items.length', function() {
});
That is, only check when $scope.items.length have changed. That expression will execute very quickly.
Original post:
If your question is "is it better to expose functions to the template than objects" then yes, you should be using functions as much as you can. That way you encapsulate the logic inside the controller instead of letting it bleed into your template. Take this example:
<div ng-show="shouldShow">Toggled div</div>
<button ng-click="shouldShow = !shouldShow">Toggle<button>
Here the template has a little too much knowledge about what's happening. Instead, this should be solved like this:
// controller
var shouldShow = false;
scope.toggle = function() {
shouldShow = !shouldShow;
}
scope.shouldShow = function() {
return shouldShow;
}
<!-- template -->
<div ng-show="shouldShow()">Toggled div</div>
<button ng-click="toggle()">Toggle<button>
By doing it like this it's trivial to expand the logic in the controller without touching the template. While your requirements right now might be to just toggle the div when you press the button, tomorrows requirements might be to update some other part of the application when that happens. And if you use a function instead, it's easy to add that logic inside the function without changing the template.
Functions on your scope have a bit more overhead than using properties, but that overhead probably won't be what's slowing down your app when that day comes. So use functions when they make sense.
But you should still try to keep your controllers as small as possible. If they grow to contain tons of functions/functionality, you should probably split up your controller into reusable directives.

Categories