problem with angular. On my website a have comments. Each comment shares the same 'ng-controller="commentCtrl"' directive. Now when I have about 300 comments on my web site, there are 300 commentCtrl instances. In the html of the controller I am using ng-disabled="author_provided()" on a button.
When I am changing the author input text field, all the 300 comments are invoking author_provided() ( because this function depends on on the author ng-model). This causes performance issues. I want the author_provided() function be invoked only in the controller where I am changing the author. How to achieve that ?
The author_provided function will be evaluated for each comment on every $digest cycle. If you must circumvent this behavior, I suggest adding the author provided boolean as a property of the comment object. Then your template code can simply read: ng-disabled=comment.author_provided (no function call) and Angular will evaluate the result without calling the controller function.
The function is called for every comment because the templating engine can't know the result of ng-disabled for each comment without evaluating the controller function call.
Related
I am trying to understand what angularjs $parse service does. I have read the official documentation at https://docs.angularjs.org/api/ng/service/$parse but that's not really helping. Searching online did not render any good examples.
Any help will be appreciated.
I was going to explain but could not do a better job than this post
"$parse takes an expression, and returns you a function. When you call the returned function with context as first argument. It will execute the expression with the given context."
In the simplest form: It's main purpose is for example to access some tags click function with the right context inside a directive without tight coupling so you can execute it (maybe with some extra params).
The purpose of $parse from my POV is to let us evaluate a "property" from a given $scope. The result of calling $parse is "property", for example:
my controller
$scope.author.name = "Hello World";
somewhere else under the same controller:
var property = $parse("author.name");
Property Getter: property($scope); in this case it is evaluated against the same scope.
Property Setter: property.assign($scope,'Felipe'); assigns a new value to the author name.
$scope give us the context where to evaluate or search for the "property".
I have found them useful when creating directives to still maintain the directive decoupled from the controller but still needing to interact with objects present in the controller.
$parse takes in a string and returns you a function. Below is a simple example of it in action
http://plnkr.co/edit/nicdbwVL2ZZbljZy2Z9S
Demo here
Quick question: in the following code I only call the function isSpecificPage() once, why it console.log twice?
<div ng-hide="isSpecificPage()">
<p>Hello!</p>
</div>
Angular puts a watch on your ng-hide function so that every digest cycle it can see if the results changed (and thus if it needs to change from hiding to showing your element or vice-versa).
When watched functions are evaluated (during $digest) if any of them have changed from the previous $digest then Angular knows that change might ripple through to other watched functions (perhaps the changed variable is used in another watched function). So every watch is re-evaluated (also called dirty processing) until none of the watches results in a change. Thus typically you'll see 2 calls to watched functions per digest and sometimes more (up to 10- at 10 loops through it gives up and reports an error saying it can't stabilize).
Here's more on watch and digest:
http://docs.angularjs.org/api/ng.$rootScope.Scope
http://www.benlesh.com/2013/08/angularjs-watch-digest-and-apply-oh-my.html
ng-hide is one of the directives that uses $watch internally. Since $watch uses digest cycle(which runs atleast 2 times to check if the value has changed or not), so your function isSpecificPage has run twice.
For a list of directives that use $watch internally, refer this stackoverflow answer directives that add watch internally.
I have a page that has a number of directives. There are a number of directives in the header/navigation each with there own scope. There is also a ng-repeat of 25 items and each one of those creates a directive each with its own scope.
One of the directives includes a form that includes a custom filter to display form errors, it looks like this:
<span>{{ createProjectForm.name.$error | nagParseErrors }}</span>
Now the concern I have right now is that nagParseErrors is being executed about 33 times when anything in any scope changes even though this data createProjectForm.name is binded to (with ng-model) is only contained in the controller scope and the directive's scope containing the form (which is just being passed to the directive from the controller scope). I know it is related to the number of scopes (or directives) on the page because if I limit the ng-repeat from 25 items to 1, the filter is only called 9 times. This also happend for built-in filters (like json, and it even runs more times).
Is there something I might be doing wrong here or is this in fact how it should work in AngularJS?
BTW, I realize now that displaying the errors might be better off as a directive than a filter I am planning on going the directive route however I would like to clear up my understanding of filters here since I will probably run into this at some point down the road.
This has been addressed numerous times. All AngularJS expressions will be constantly re-evaluated throughout the lifecycle of the app. This is how two-way databinding in AngularJS works.
So, there's nothing wrong with your code. It's just that you need to make sure your filter is idempotent (returns the same output given the same input).
For more info take a look at Why Scope.$apply() calls $rootScope.$digest() rather than this.$digest()? and scope docs.
I'm trying to change an input box value referring to its model.
The input box is inside an ng-repeat, that gets its elements from a function and not from a variable.
Is there a way for the application to print the model full path/name so that I can refer to it from the controller? (Note that the models are working, since a division beneath it with ng-show responds properly to the input model being filled.)
In general, you shouldn't use a function inside an ngRepeat. With the way the digest cycle works, your function will end up getting called multiple times. That's not very efficient. Instead, you should run the function from your controller and assign it to a scope variable that you pass to ngRepeat.
As it turns out, this makes the solution to your problem quite easy. You could for example use each item's $index to reference it from the controller.
If you post a fiddle or plunker, I can update this answer with something more specific. But it's hard to say more without some code.
I have this basic plnkr which just implements a basic "Hello, X" directive.
In the link function I am logging scope.name but I get undefined? Why is it so? Shouldn't it log the value of name property in console?
This is a known "problem" where interpolation of # attributes happens after linking function is invoked. There is a pull request open to change this issue but it is not clear if this one is going to be merged.
In the meantime a way of getting an interpolated value is by observing an attribute like so:
attrs.$observe('hello', function(changedValue){
console.log(scope.name);
});
And the plunk: http://plnkr.co/edit/Lnw6LuadTLhhcOTsPC8w?p=preview
So, at the end of the day this is a bit confusing behavior of AngularJS that might be changed in the future.
Pawel is right (https://stackoverflow.com/a/14552200/287070) but I wanted to add that the problem is that any attribute that contains {{}} interpolation will be set to null in the attrs parameter during the link function as the first $digest since the compilation has not yet run to evaluate these.
The fact that # bindings are null in linking functions is just a symptom of this.
Currently there is no real fix, since we can't start running $digests in the middle of the compilation process. So $observe (or $watch) is the only real way to get hold of these values.
For those in 2015 who are reading this post, please note that the way Angular handles "#" attributes has changed.
Angular 1.2 onwards, interpolation occurs prior to the invocation of the linking function.
An excellent post on this topic is present here.