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.
Related
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.
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.
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.
Due to performance and other issues, I want to split my code into seperate functions as before it was just one big ".ready" function.
I am new to javaScript/jquery but I thought I would give it a go myself. I have done exactly the way I thought it was done but my console is telling me things are undefined so I am guessing I have got things out of scope. I have read up on it in more detail, but still have not got anywhere.
My code works OK at the moment but I want to get into the habbit of clean coding. Can someone point out where I am going wrong so I can carry on myself?
Here is an example of what I have so far
//Global variables
var randomWord = [];
var listOfWords = [];
var populationNumber = [];
var attemptNumber = [];
var completionNumber = [];
var gridSize = [];
generateGrid();
startMusic();
randomizeReward();
//Click event to start the game
$(".start-btn-wrapper").click(function () {
startplay();
});
//Click event to restart the game
$(".restart-btn").click(function () {
restartplay();
});
Thanks
Fiddle: http://jsfiddle.net/QYaGP/
Fiddle with HTML: http://jsfiddle.net/QYaGP/1/
You need to start passing some information into the functions you're defining. If your functions all have no arguments, then you will have to use globally defined variables, hardcoded references to jquery selections etc in order to get anything done.
So as an example, you have a function
function replaySound() {
$("#hintSound").attr('src', listOfWords[randomWord].hintSound);
hintSound.play();
}
This is actually going to play the sound detailed in listOfWords[randomWord] via the element #hintSound. You could do that via:
function playSound(selector, wordlistEntry) {
$(selector).attr('src', wordlistEntry.hintSound);
$(selector)[0].play();
}
And then instead of calling replaySound(), you'd call:
playSound('#hintSound', listOfWords[randomWord]);
This way the behaviour that you want is wrapped up in the function, but the specifics, i.e. the data you need for it, are passed in via the arguments. That allows you to reuse the function to play any sound using any selector, not just #hintSound.
You'll find as you do that that you need to start choosing what a function will act on in the code that calls it, rather than in the function. That's good, because the context of what you're trying to achieve is there in the calling code, not in the function. This is known as 'separation of concerns'; you try to keep logic about a given thing confined to one area, rather than spreading it about in lots of functions. But you still want functions to allow you to encapsulate behaviour. This allows you to change behaviour cleanly and easily, without having to rewrite everything every time some part of the logic changes.
The result should be that you find several functions actually did the same thing, but with different specifics, so you can just have one function instead and reuse it. That is the Don't Repeat Yourself principle, which is also important.
If you are concerned about performance, I would look into using an framework such as AngularJS. You can inject modularized code. Even better, with MVC your view is bound to your model so by changing the model the view automatically updates itself.
Also, stop using class selectors. Use ID selectors. They are much faster. You also want to preload selectors (even with class selectors). That way you are only searching the DOM once:
var ele = $('#elementId');
$(ele).doSomething();
This way, you have a reference to the DOM element. You can use a datastructure to store all of your references outside of the global scope:
var elementReferences = {}; //declaration
elementReferences.mainPage = {}; //use
elementReferences.mainPage.root = $('#mainPage'); //load the root div of a page segment
elementReferences.mainPage.title = $(elementReferences.mainPage.root).children('#title'); //load the title
elementReference.mainPage.form = $(elementReferences.mainPage.root).children('#form'); //load the form
Now you can do this:
$(elementReference.mainPage.form).whatever();
and it doesn't have to search the DOM for the element. This is especially useful for larger apps.
If you put a function within document.ready, as per your fiddle, you are only able to access that function within the scope of the document.ready call. You really want to be able to load/unload functions as needed dynamically within the scope that they are required in, which is where angularjs comes into play.
You also, for the most part, want to remove your functions and variables from the global scope and put them into containers that are sorted by their dependencies and use. This is Object Oriented Programming 101. Instead of having a bunch of arrays sitting within the global scope where they could be overwritten by mistake by another developer, you want to put them within a container:
var arrays = {}; //create the object
arrays.whatever1 = [];
arrays.whatever2 = [];
Obviously, you will probably want a more descriptive name than "arrays". Functions work the same manner:
var ajax = {}; //ajax object
var ajax.get = function(){
};
var ajax.post = function(){
};
var ajax.delete = function(){
};
This generally promotes cleaner code that is more reusable and easier to maintain. You want to spend a good portion of your time writing a spec that fully documents the overall architecture before actually beginning development. NEVER jump the gun if you can help it. Spend time thoroughly researching and planning out the big picture and how everything fits together rather than trying to wing it and figure it out as you go. You spend less time having to reinvent the wheel when you do it this way.
It's developed by google, so it should be around for quite a while. I'm not sure if you are the guy in charge of your system's architecture, but if performance/reusability is an issue at your company it is definitely worth taking a look at. I'd be more than happy to give you a walkthrough regarding most of what I know in terms of software architecture and engineering. Just MSG me if you are interested. Always happy to help!
Is it possible to test myInnerFunction below?
var val = function() {
var myInnerfunction = function(input) {
return input + ' I ADDED THIS';
};
return myInnerfunction('test value');
}();
Because myInnerFunction is essentially a private member of the anonymously executed outer function, it doesn't seem like it is testable from the outside.
You could intentionally expose a testing hook to the outside world, like possibly this:
var val = function() {
var myInnerfunction = function(input) {
return input + ' I ADDED THIS';
};
/* START test hook */
arguments.callee.__test_inner = myInnerFunction;
/* END test hook */
return myInnerfunction('test value');
}();
now, once val has been run at least once, you can reference val.__test_inner and call it with testable inputs.
The benefits to this approach:
1. you choose what is exposed and not (also a negative 'cause you have to REMEMBER to do so)
2. all you get is a copy-reference to the private method, so you can't accidentally change it, only use it and see what it produces
The drawbacks:
1. if the private member changes (or relies on) state of its host/parent function, it's harder for you to unit test around that, since you have to recreate or artificially control the host/parent state at the same time
2. As mentioned, these hooks have to be manually added
If you got really clever, you could have your build process look for blocks of comments like the above and remove the test hooks when creating your production build.
afaik unit testing does not concern about internal workings of things you test. The point is that you test the functionality ie: it does what it's supposed to do, not how it does it.
So if it uses an internal private member, it should not be testable...
You can test the external behavior that is observable. In this simple case you returned just the value of the inner function, but in a real world example you might combine the result of that inner function with something else. That combination is what you would test, not the direct outputs of the private method.
Trying to test the private method will make your code difficult to change and refactor, even when the external behavior is preserved. That said, I like to view unit tests not as extensive tests of your code, but simply providing an example of the API and how it behaves to different conditions. ;)
I think my answer for this is (like so many things) that I'm doing it wrong. What I've defined as a 'private' function is really something that needs to be tested. It was only private because I didn't want to expose it within a utilities api or something like that. But it could still be exposed through my application namespace.
So within the anonymous function that is executed on-dom-ready, I just attach my pre-defined functions as event handlers to the proper DOM hooks. The functions themselves, while not stored with my more open utilities functions, are still stored publicly within a package in my namespace associated with the DOM structure they are dealing with. This way I can get at them and test them appropriately.