while building a very large scale angular application i came across a performance issue. all my components and views heavily depend on data bindings, there ng-repeats everywhere. so i decided to remove all the 2 way databindings by adding {{::scopevariableName}}, now since there is no 2 way data binding my question is will the view update if modal value is changed in the controller?
function myCtr(){
var that=this;
this.scopevariableName='hannad'
this.someFunction=function(){
someEvent(function(callbackData){
if(callbackData){
that.scopevariableName='new value for scope variable'
}
});
}
}
<p ng-bind="::$ctrl.somevariableName"></p>
from the docs: One-time expressions
will stop recalculating once they are stable, which happens after the
first digest if the expression result is a non-undefined value.
My understanding of one-time binding is after the first initialization of the scope variable if the object value changes the view will NOT change.
because the watchers are not in effect.
ex
$scope.myVar=null;
<p>{{::myVar}}</p>
if the there are any changes to $scope.myVar those will not effect in the view
because it will take only its first initialization value. thats null.
Related
I am learning angular js and have now a question where I couldn't find the right answer yet.
in the template HTML, you can use expressions to show the scope variables or call scope functions. But I see all the time different versions of it.
{{name}} shows the variable and binds it
{{::name}} the same thing but without binding
userdirective="{{::key}}" But what is the difference here?
ng-if="::field.sortable" With ng-if they are not using {{ but with there userdirective they do?
userdirective="{condition:isActive(route.name),mdColors:{color:'primary'}}" And then there is the last one with just one {. Thats when you create an object.right?
Maybe someone can help me to understand all of it.
Thank you very much for your time. Pat
{{name}} as you say is two-way data-binding
{{::name}} one way databinding
userdirective="{{::key}}" is the interesting case. This statement uses one-way binding into the userdirective ... which means after the $digest it just says userdirective="someValue"
So the userdirective gets that value as a plain value. Now I would have to test it but in the scopepart of the directiive it should say # so it gets read as a string and not as a expression.
The last one is simply as any JSON you build
{ name: value?true:false }
setting value according to conditions that angular evaluates, with a bit of magic involved :D
hope that helps
{{ anything here}} - That is angular expression interpolation. Angular interpolation - here you can find more about that. Basically idea that it interpolate anything you will put inside those brackets. So if you will put expression with some calculations or just variables related to current scope it will convert all variables to their values and apply calculations.
For instance: {{scopevar1 + scopevar2}} in case this variables has some values, let it be 1 and 2, as result we will see 3.
:: - This mean one time binding. For instance {{::scopevar1}} it will be interpolated once and will not check for changes of scopevar1, always stay as first value. Even if scopevar1 will change every second, the value in template will be the same. Angular Expressions - here you can find some live examples and more information.
userdirective="{{::key}}" - This case is nothing more then assigning dynamic value to your directive. UserDirective expectes to get a simple value, but we have it inside our scope, so we need to say: Hey, angular please interpolate scope variable - key, but only once, so my directive will get value, and will not looking for updates of key. And angular does it with pleasure!
userdirective="{condition:isActive(route.name),mdColors:{color:'primary'}}"
The last case is when your directive expects to get some kind of specific JSON. And we don't want to build it inside of controller. It is sometimes easier to do such things in the tempalte. So we put specific object with two properties: condition, mdColors. And saying that first property assigned to result of function, and second one is simple object {color:'primary'}.
That's it!
{{var}} is a two way binding expression and {{::var}} is a one-way binding expression. expression with :: will not change once set, it is a candidate for one-time binding.
go through : https://docs.angularjs.org/guide/expression for better examples on these
{{name}} is the regular case you will find. You basically print the variable name and update it once it changes.
{{::name}} is the same but your value will not receive updates once it stabilises.
So in the first case, your template updates once name is changed. In the latter, it isn't.
userdirective="{{::key}}" is a one-way one-time binding. Leave the :: out and your directive receives updates if key changes. However, if the directive changes key, it will not update the parent.
ng-if="::field.sortable" is a two-way binding. The changes go both ways. In this case, field.sortable is watched by the directive.
userdirective="{condition:isActive(route.name),mdColors:{color:'primary'}}" is used when you want to build adhoc-objects. A popular case is ng-class as well. You may build this object in the controller as well as you should not put too much logic in your template.
In any case, it is advisable to read the excellent docs https://docs.angularjs.org/guide
I have a input field with model named "name" with some initialized value say "James".
And when i console $scope i can see model value as "James".
ie. $scope.name = "James".
But immediate on next statement if i use setTimeout function and update input field value with Say "Marcus" and console $scope again, why i get $scope.name = "James" only.
I know, i have updated input field value by going out of box than angular and angular is not aware of updation.
So my real question is, Is Angular JS written over Javascript Language? And if yes, why using setTimeout not updated model value in $scope.
Please help me, if i am thinking/question is right or wrong.
This has to do with how angular handles two way data-binding and the digest loop. If you're interested in knowing all the details of how this works then you can read about it here: https://www.ng-book.com/p/The-Digest-Loop-and-apply/
The basic idea is that in angular the digest loop is used to keep track of all the variables you placed in your $scope. Whenever a variable changes it sets off the digest loop, in each cycle of the loop the new value of the variable is compared to the old value and updated accordingly until the new value is equal to the old value which ends the loop. That being said this only works as long as you change your variables within angular's context, this is especially true for changing variables within callback functions or with jquery. Angular doesn't know that a variable was changed so the digest loop never gets started, you could force it by using $scope.$apply but some cases should be avoided in general unless there's no angular alternative for it (Which usually isnt the case).
For setTimeout there's an angular alternative which is $timeout, this has the exact same functionality as setTimeout but it works within angular's context so your variable will be updated.
AngularJS allows you to implement two-way data binding. However, the interesting part is how it detects model changes? The model is usually a plain object like the code below. We can change the name property of $scope.user but how does AngularJS detect the model changed? Does AngularJS enum all the properties of the $scope object?
angular.module('myApp', [])
.controller('BusinessCardController', function($scope){
$scope.user = {
name: 'Tanay Pant'
}
});
<input type="text" ng-model="user.name" placeholder="Full Name" />
There is a digest cycle, where the scope examines all of the $watch expressions and compares them with the previous value. It looks at the object models for changes, if the old value isn't the same as the new value, AngularJS will update the appropriate places, a.k.a dirty checking.
In order for the digest cycle to be execute $apply(fn) has to be run, this is how you enter the Angular world from JavaScript. How does $apply(fn) get called (taken from AngularJs integration with browser):
The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event, or network event (response from a server).
The event's callback gets executed. This enters the JavaScript context. The callback can modify the DOM structure.
Once the callback executes, the browser leaves the JavaScript context and re-renders the view based on DOM changes.
Data Binding
Digest Cycle Explanation
In order to achieve two-way binding, directives register watchers. For a page to be fast and efficient we need to try and reduce all these watchers that we create. So you should be careful when using two-way binding - i.e. only use it when you really needed. Otherwise use one-way:
<h1> {{ ::vm.title }} </h1>
Here it is quite obvious that the title of the page probably won't be changed while the user is on the page - or needs to see the new one if it is changed. So we can use :: to register a one-way binding during the template linking phase.
The main issues I've seen with explosions of watchers are grids with hundreds of rows. If these rows have quite a few columns and in each cell there is two-way data binding, then you're in for a treat. You can sit back and wait like in modem times for the page to load!
Two-way binding is limited almost exclusively to elements that use ng-model. The direction going from view to model uses standard event handlers to detect changes that must be updated within the model (e.g., onchange). The direction going from the model back to the view is updated during a $digest. But we do not call $digest directly.
Every element that is on your page that is going to respond to the digest cycle will, somewhere, attach a listener and an expression to its scope using $watch. When you write {{ foo() }}, or when you use ng-model='user.name', internally there is a call to $watch made on your behalf with a Javascript expression that will be run every time a digest cycle is run. This registration might happen during the compile of the template (our first example), or it might happen during the link phase of a directive (our second).
There is no magic here. The listeners that are attached are regular functions -- in our example, the listener for the expression foo() is provided for you, and it will update the html text on the page, while the listener for the expression user.name will call setText, or setOption, or whatever is required by the particular input which ng-model has been attached.
While angular can handle most of the listening, you can attach your own watch expressions with your own listeners manually inside any function that has access to a scope (scope is important because we will tear down those watchers if the corresponding parts of the page are removed). Be mindful of excess. Bindings aren't free, and the more things that are bound, the slower the page will respond. One-time bindings are one way of reducing this cost. Using $on with $emit and $broadcast are another.
So when is digest called? It is certainly not automatic. If the digest cycle is running, it means someone somewhere called $apply on their scope or on the root scope. ng-model attaches handlers which will respond to regular html events and will make calls to $apply on your behalf. But foo(), on the other hand, will never get called until some other bit of script somewhere calls $apply. Fortunately, most functions that you fill out for angular wrap those functions with a call to $apply, so you don't often have to make the call yourself (e.g., $timeout is wrapped with $apply, which is why we use it instead of setTimeout). But if you were using something outside of the scope of angular (a 3rd party library that connects to events), you would need to remember to call $apply yourself, and just like above, you can do this manually by calling $apply anywhere you have access to a scope.
In order to make Data Binding possible, AngularJS uses $watch API's to observer the changes on the scope. AngularJS registered watchers for each variable on the scope to observe the value in it. If the value of the variable on the scope gets changes, then the view gets updated automatically.
It happens because of the $digest cycle is triggered. Hence, AngularJS processes all the registered watchers on the current scope and the children and check for the updates and call the dedicated watcher listeners until the model is stabilized and no more listeners are fired. Once the $digest loop finishes the execution, the browser re-renders the DOM and reflects the changes
By default, every variable on a scope is observed by the angular. In this way, unnecessary variable are also observed by the angular that is time consuming and as a result page is becoming slow.
I use a same controller twice on my page, with same data, but i display it not in the same way (not same parts of the array).
I can see with ng-inspector that the scope is correctly updated when i perform some change, and it's duplicate. But in the view, it's doesn't change ! A simple param to false by default and passed at true with a simple timeout, in the view, it's always to false.
If i display only one time the ng-controller, the view it's updated.
How to correct that ?
Ok, so you didn't actually give much in the way of code, but this sounds like you're falling victim to one of the classic scope blunders. The first thing to try is to use properties of objects, not primitives on the scope.
This means instead of using this code ---
$scope.myBool = false;
{{myBool}}
use this code ---
$scope.myBool = { value: false };
{{myBool.value}}
The reason being is that Angular likes to do funny stuff like use prototype for new scopes. This means you'll get the prototype of the parent scope, and then when you instantiate your scope, you can override the parents prototype with your new value, without changing the parents value. You get past this by using an object on the scope instead.
The second option that might be occurring is you're doing something that isn't causing an angular digest cycle to happen. You might need to manually kick one off.
Without seeing any code, there's no telling, though.
I have this simple controller markup
<div ng-controller="TestCtrl" ng-show="isVisible()">
<input type="text" ng-model="smth"/><br>
<span>{{smth}}</span>
</div>
And controller itself
function TestCtrl($scope, $log)
{
$scope.smth = 'smth';
$scope.isVisible = function(){
$log.log('isVisible is running');
return true;
}
}
Why after every little change of model (e.g. changing one letter inside textbox) I can see isVisible is running in console? It's not problem for such case but I think it will be in large application. Can I avoid this?
Liviu T. is right. You can't avoid having your isVisible function executed on every scope change. If Angular didn't rerun this function, than it could get out of sync with the rest of the code (for example if it was using $scope.smth model to resolve the return value).
Having this in mind, you should always try to make your functions idempotent/pure (always returns the same output given the same input) and light, because directive such as ng-hide, ng-show, ng-class and similar will always re-evaluate the expression assigned to them, on every $digest cycle.
Now, if you really don't want it to execute all over again, you might try some of the memoization techniques for caching the function output.
This is normal as this is essential pat of how AngularJS does its "magic". This answer has more details: https://stackoverflow.com/a/9693933/1418796
There are different techniques to make sure that you don't run into performance problems but no, in general you can't exclude those expressions from being evaluated.