I've an array and I'm looping over this array and calling a function into this ng-repeat and this function is in link function of a directive
so my problem is, ng-repeat looping only twice (length of array items) but calling directive link function more than twice
Here my code snippet
link:function(scope){
$scope.test = function() {
console.log('sssss');
}}
and template is
<div ng-repeat ="item in items> {{test()}}></div>
please help me to prevent calling test function more than items length.
You should do this,
<div ng-repeat ="item in items" ng-init="test()"> </div>
Reason test() is getting called more times than length of array items is
because of this {{test()}}.
AngularJS registers a watcher for {{}}(binding) in view.
This watcher keeps track of whether value has been changed or not(it compares the old value to new value). watcher's expression(in your case, test()) gets evaluated atleast once in this process.
You can read more about it here.
Thats the reason your test() is getting more number of times than length of array.
<div ng-repeat ="item in items" ng-init="test()"> </div>
If you use ng-init, test() will only be initialized(called) once per each repeat.
Related
I have a simple ng-repeat to build a HTML list from a javascript array.
Each item can be moved using an input to get the new rank. This input is binded to a variable rank. This variable is initialized using the ng-init directive.
Code looks like this :
<li ng-repeat="item in ctrl.getItems()">
<div ng-init="rank = $index">
[$index: {{$index}}]
{{item}}<br/>
<label>
Move to
<input type="number" ng-model="rank"/>
</label>
<button type="button" ng-click="ctrl.moveItem($index, rank)">
Ok
</button>
</div>
</li>
At runtime, when I change the input value and click to the Ok button, function ctrl.moveItem is called and item is really moved in the ctrl.getItems() array.
So the ng-repeat is replayded and items appears in the new order.
BUT variable rank is not reinitialized and 2 items appears with the same rank.
The sample is here : https://jsfiddle.net/nlips/4ng34b7b/
My question is not so much about moving items in a list, but I need to understand how ng-init works in the context of ng-repeat.
I did not find anything on this subject in the AngularJS official documentation.
From AngularJS docs:
The ngInit directive allows you to evaluate an expression in the current scope.
Now. You are working with different scope.
You are using ngInit into the transcluded scope, overriding $scope.rank each time it repeats that portion of template.
If you want to persist your rank you should init it into the ngRepeat scope.
Try with:
<li ng-repeat="item in ctrl.getItems()" ng-init="rank = $index">
[$index: {{$index}}]
{{item}}<br/>
<label>
Move to
<input type="number" ng-model="rank"/>
</label>
<button type="button" ng-click="ctrl.moveItem($index, rank)">
Ok
</button>
</li>
EDITED ANSWER
Ok, i got it.
The ngInit expression is evaluated only when that part of template is going to be rendered into the DOM.
So, when the page is loaded for the first time your expression is fired and each rank is evaluated correctly.
But, when you make changes on an item that is already rendered, your template is not going to be rendered again, so your ng-init will not be fired.
If you want that ng-init to be executed again you have to remove the item from the DOM and then append it back, into the new position.
There are several alternatives to this approach, but i hope this clarifies what was going on.
I use ng-repeat to populate my table. One of the columns in the table should be dynamically populated again by a different function call.
Below is my code snippet.
<tr ng-repeat="item in ctrl.items">
<td><span ng-bind="item.name"></span></td>
<td><span ng-bind="getItemDetails(item.id)"></span></td>
</tr>
I have array of items. I need to display those items in a table. Item name will be present in the item object, however, item details will be populated by another function call which needs item id.
On using ng-bind (like in the code above) I face 2 issues.
Multiple calls to function even if array has 1 items. Sometimes it goes on thereby freezing my browser and server out of memory issue
The item id doesn't get passed to function always. Sometimes it is undefined.
I am not sure if ng-bind is the right directive to be used. ng-model doesn't work though. Is there any other directive or other way to do it?
How can I achieve this?
EDIT:
Here is the jsfiddle url: https://jsfiddle.net/grubxaur/
If you check browser console, you can see the function is called twice. I guess it is called N no. of times where N is no. of columns in the table.
I have tweaked my implementation a bit to get rid of this issue. Rather than calling a function within ng-repeat, I modified the items array within the controller using angular.forEach before ng-repeat is invoked.
Something like code below.
angular.forEach(self.items, function(item){
item.details = $scope.getItemDetails(item.id);
});
I am running this simple code with angularjs :
HTML :
<div ng-app ng-controller="AController">
<code>{{ itemsInArray }}</code>
<div ng-repeat="item in itemsInArray">
<input ng-model="itemsInArray[$index]" />
</div>
</div>
JavaScript :
function AController($scope) {
$scope.itemsInArray = ["strA", "strB", "strC"];
}
Binding appears to be working correctly when indexing into the array but after entering one character the input loses focus.
You can find the working code here on this fiddle : http://jsfiddle.net/QygW8/
I think this is happening because you are manipulating the same item which is iterated over ng-repeat. So ng-repeat sees a change in the item and re-runs the `ng-repeat which regenerates the items.
If you look at your fiddle html, you may notice this effect.
To make it work, one way you can do this
http://jsfiddle.net/cmyworld/CvLBS/
where you change your array to object array
$scope.itemsInArray = [{data:"strA"}, {data:"strB"}, {data:"strC"}];
and then bind to item.data
Try to change the model:
<div ng-repeat="item in itemsInArray">
<input ng-model="item" />
</div>
Even am an newbie to the angularjs, up-to my findings ng-repeat updates/repeats and recreates the whole HTML elements when there is an change in the model. Hence when a single character added to model causes ng-repeat to react and creates the all the HTML elements again which results to losing the focus.
This is an fiddle , In which u will be able to observer the changes with the model inside the ng-repeat and outside the ng-repeat.
Sorry i don't have the solution, Hope using ng-change apart of ng-model may help.
I have written an angular function that works fine. Now, I want to write another angular function that calls the first function. How can I do this?
app.controller("AppCtrl",function($scope,$location,$http,$compile,$rootScope){
$scope.func1 = function(param1,param2){
... // function body
};
$scope.func2 = function(param3) {
var value = "someValue";
$scope.func1(value,param3);
};
});
Unfortunately func2() doesn't work, even though fucn1() works if I call it directly (e.g., <div ng-click="func1(val1,val2)">Click Here</div>. Why?
UPDATE:
It's because I'm doing the following:
<ul>
<li ng-click="func2('{{item.value}}') ng-repeat="item in items">Item</li>
</ul>
For some reason {{item.value}} is not being compiled in the ng-repeat. I can inspect the HTML and see that the values are there, but the ng-click doesn't do anything. However, if I manually enter a value for the parameter in func2, then it works. So, now my questions is: How do I compile items in an ng-repeat?
I fixed your example here: http://jsfiddle.net/CsffM/
The problem is that you are using brackets in your expression,
Insted, do this:
<li ng-click="func2(item.value)" ng-repeat="item in items">Item</li>
I hope I have helped!
I have an angulars setup as follows, trying to mimic some excel functionality where I have a controller nested inside an ng-repeat.
<tr ng-repeat="lw in lw_list" my-lw ng-model="lw"
<td>
<!-- next two elements act as an excel cell, one for inputing data, they other for displaying calcualtion result -->
<div ng-controller="MyCellCtrl">
<input type="text" class="inputdiv" ng-model="lw.library.name" >in</input>
<div class="output" ng-bind="getCellValue(lw.library.name)" syle="postion:absolute" contenteditable="True" >out</div>
</div>
<div ng-controller="MyCellCtrl">
more input / div pairs to act as a new cell
.....
</div>
</td>
I have the stylesheets set up so that input and output are in the same position, and get hidden / unhidden, so that they act like an excel cell (you type a formula, then when you leave focus, it updates the content).
Anyway, when I put a console.log() inside the getCellValue() function, to show what instance of the controller is being called, then typing in one particular cell, I can see that getCellValue() is being called on every cell.
Is there some way to call getCellValue() when the input is updated without calling the method on every instance?
(I based this code on the code from this tutorial:
https://github.com/graunked/spreadsheet
you can see the same behaviour by putting a console.log in the compute function. If you increase the arrays to 20 x 20 elements, it starts to get slow when you type anything.)
Is there some way to call getCellValue() when the input is updated without calling the method on every instance?
<div class="output" ng-bind="foo">
then use $watch:
function MyCellCtrl($scope)
{
$scope.foo = $scope.lw.library.name;
$scope.$watch('foo', function(newValue) {
$scope.foo = getCellValue($scope.foo);
});
}
or use viewChangeListeners as an alternative:
function MyCellCtrl($scope)
{
$scope.foo = $scope.lw.library.name;
this.$viewChangeListeners.push(function(newValue) {
$scope.foo = getCellValue($scope.foo);
});
}
References
Effective Strategies for avoiding watches in AngularJS
Compile, Pre, and Post Linking in AngularJS