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.
Related
I know people mostly refer to two-way binding when it comes to variables displayed in the HTML, but I keep running into this same problem and realized there is something fundamental about Angular that I'm not understanding.
To set up this question, I am currently developing an RPG browser game. I want each character to have baseStats which are represented as:
'baseStats' : {
'defense' : 5,
'strength' : 3,
'speed' : 7
}
but during the course of the battle, they may be 'buffed' or 'debuffed', so I want to be able to track those changes without permanently changing the characters stats, so baseStats exists to preserve the 'original' state of the stats.
vm.restoreAll = function() {
angular.forEach(vm.activeAllies, function(ally) {
ally.stats.defense = ally.baseStats.defense;
ally.stats.strength = ally.baseStats.strength;
ally.stats.strength = ally.baseStats.speed;
});
};
This works exactly as I intend it to. Each of these stats is reset to the baseStat
vm.restoreAll = function() {
angular.forEach(vm.activeAllies, function(ally) {
ally.stats = ally.baseStats;
});
}
This is obviously more readable and terse, but unfortunately if I make changes to ally.stats from elsewhere in the application, those changes affect the baseStats as well. I'm confused why this would be as they seem to be effectively the same exact thing.
Your issue is not really specific to Angular but Javascript in general. When you pass object values around to different variables you are not passing the actual object value but just a reference to the object. So the object value itself only exists in one place in memory and each variable holds a reference to that location.
This blog post explains further: http://nsono.net/javascript-pass-by-value-or-pass-by-reference/
As the other comments already explain using Angular's copy function will cause an entire new object to be created and stored in memory and fix your issue.
There are various non-Angular ways of cloning objects as well, some described here: http://heyjavascript.com/4-creative-ways-to-clone-objects/
Probably the most concise: JSON.parse(JSON.stringify(myObject))
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.
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.
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!
I'm new to object oriented programming and am slowly learning how to apply it to javascript. So please bear with me. :)
I have two basic objects:
"record" which contains methods for editing a single record from a recordset. (create, save, load, etc.)
"recordList" which contains methods for outputting a paginated list of record titles.
I would like for these objects to be able to work together. For example, if record.save() is called, recordList.refresh() is also called, so that the paginated list reflects the updated data.
To accomplish this, I have created a third object "control" which contains instances of both "record" and "recordList". I am using "control" in the following fashion:
control = {}
control.record = object.create("record");
control.recordList = object.create("recordList");
control.save = function() {
this.record.save();
this.recordList.refresh();
};
This works. But I am wondering, is it proper? (I want to be sure I am not violating any of the rules of OO design in doing this.) Is there a better way?
Thanks in advance for your help.
Speaking from an OOP perspective, I don't think a record would save itself. A record in a database is simply data, and the database itself is what does things with that data, whether it's saving or loading or etc. That being said I'd make record be simply an object that holds data and would create a recordset object for interacting with the data. Within that recordset object you could put your recordList and update it accordingly. Something like:
var recordset = function() {
var me = this;
var currentRecord = object.create("record");
var recordList = object.create("recordList");
me.save = function() {
//Insert record.save code here
recordList.refresh();
};
};
Something to note about that code. In that setup currentRecord and recordList can't be accessed from outside the function and therefore you have encapsulation, one of the hallmarks of OOP. This is because the recordset function is a closure that "closes over" all variables within, meaning that every function within has access to the variables within the scope of recordset.
You could let the outside world get access through get or set functions:
me.getRecordList = function() {
return recordList.getArray(); //Not generally a good idea to simply return your internal object
};
Your solution is fine. Two minor suggestions for improvement
Use a more specific name than control (even 'recordControl' is ok). You may end up with lots of controls for different feature sets.
Use an object literal to create the entire object. Keeps your code tidier and more readable (and saves a few bytes)
(apologies for lack of spacing - editor not doing what I want it to do!)
recordControl = {
record : object.create("record"),
recordList : object.create("recordList"),
save : function() {
this.record.save();
this.recordList.refresh();
}
}
If it's one thing I've learned over time, it is that following any paradigm to the letter will result in more frustration and difficulty than taking the concept as far as you can go and using common sense to dictate your deviations.
That said, your solution will work fine and it's normal to create a container class for multiple objects of varying types that are coupled. If you want a different way to handle it, check out Client Event Pooling. The only thing that I can say about what you've done is to be sure you're using object.create the way it was intended.
Using this method you can create an event, which when triggered will perform a series of other commands that are associated with your event. I have used this with great success in all sorts of applications, from the intended event hooking to simplifying inline javascript injections after a postback.
Good luck.
why don't you provide your recordList into record?
var record = object.create("record");
record.recordList = object.create('recordList');
record.save = function(){
/*
do something
*/
this.recordList.refresh();
}