Angularjs function inside ng-repeat called too many times - javascript

I've got a function in my controller very easy that just stamp a console.log. I need to conditioning a button using a function but inside the ng-repeat the controller repeats everything many times! Is it normal? Is it possible avoid this? Thanks. An example here http://plnkr.co/edit/sME67gQEZQSLI9FOwEAG?p=preview
relative code:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.5" data-semver="1.4.5" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="DigestApp">
<div ng-controller="UserCtrl as uc">
<h1>Please open the console to see</h1>
<ul>
<li ng-repeat="user in uc.users" ng-class="uc.userClasses(user)">
{{user.name}}
<button ng-if="uc.isUserEnabled(user)"
ng-click="uc.disableUser(user)">Disable</button>
</li>
</ul>
</div>
</body>
</html>
javascript:
angular.module("DigestApp", [])
.controller('UserCtrl', function(User) {
var vm = this;
vm.users = User.list();
vm.isUserEnabled = function(user) {
console.log('isUserEnabled');
return user.active;
};
vm.userClasses = function(user) {
console.log('userClasses');
return []
.concat(user.active ? ['user-active'] : [])
.concat(user.loggedIn ? ['user-logged-in'] : [])
.concat(user.isMe ? ['user-is-me'] : [])
.join(' ');
};
vm.disableUser = function(user) {
user.active = false;
};
})
.factory('User', function() {
return {
list: function() {
return [{
name: "me",
active: true,
loggedIn: true,
isMe: true
}];
}
};
});

Yes, this is normal. There is no way that angular can tell whether the result of the function will have changed, so it calls it on every digest loop. If you want to avoid that you should calculate the value once and set the result as an attribute on the user object.
<li ng-repeat="user in uc.users" ng-class="user.classes">
{{user.name}}
<button ng-if="user.active"
ng-click="uc.disableUser(user)">Disable</button>
</li>
and in the controller add some code to precalculate a user.classes attribute on each user object (and to update it when you change the state of the model).

Please update your li in the HTML to the below. I got it working here http://plnkr.co/edit/Cx5OZaY0dhmBT4WLYZpX?p=info.
<li ng-repeat="user in uc.users" ng-class="{ 'user-active': user.active, 'user-logged-in': user.loggedin, 'user-is-me': user.isme }">
{{user.name}}
<button ng-if="user.active" ng-click="uc.disableUser(user)">Disable</button>
</li>
By using a function in your ng-class and ng-if it was evaluating to often. You can just pass the scope variables instead.

Related

How to pass the value into other controller?

I don't think this information is enough to completely make sense but I just some direction on this. or if you want I can share the complete code with you in floobit
I am trying to put this array from API into a seller array.
this.fetchSellers = function () {
var date = new Date().toISOString();
$http.get('/api/sellers', {params: {auctionDate: date}})
.success(function (sellers) {
sellers && sellers.length && ($scope.sellers = sellers);
});
};
I am trying to put this one of the value Seller.symbol into the array list below.
$scope.sellers = []
It able to load into ng-option like
<select class="form-control" ng-model="auction.seller" ng-options="o.id as o.symbol for o in sellers" required></select>
But the question is, I am not able to pass the value into another controller in the table.
code for HTML
<tbody class="auction-group" ng-repeat="a in auctions">
<tr>
#*auction *#
<td>{{ acv('auctionDate', a) }}</td>
#*seller*#
<td>{{ a.seller }}</td>
Code for ng
this.tableComplexFieldDict = {
auctionDate: {
label: 'Auction',
cacheKey: 'auctionDate',
getCacheValue: function (a) {
return moment(a.startAt).format('MM/DD/YY');
}
},
facility: {
label: 'Facility',
cacheKey: 'facility',
getCacheValue: _.property('contract.facility')
},
seller: {
label: 'Seller',
cacheKey: 'seller',
getCacheValue: function (a) {
return a.seller;
}
},
in order to do that you have to use another service. Ideally, you can pass data between controller to service vice versa. so just create another service and include it in which ever controller you want to pass data to.
here is example
or
// declare the app with no dependencies
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
return { FirstName: '' };
});
myApp.controller('one', function( $scope, Data ){
$scope.Data = Data;
});
myApp.controller('two', function( $scope, Data ){
$scope.Data = Data;
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://rawgit.com/angular/bower-angular/master/angular.min.js"></script>
</head>
<body>
<div ng-app="myApp">
<h3>write something in text box it will be printed by both controller</h3>
<div ng-controller="one">
<input type="text" ng-model="Data.FirstName"><!-- Input entered here -->
<br>Controller 1 : <strong>{{Data.FirstName}}</strong><!-- Successfully updates here -->
</div>
<hr>
<div ng-controller="two">
Controller 2: {{Data.FirstName}}<!-- How do I automatically updated it here? -->
</div>
</div>
</body>
</html>
Passing data between controllers can be done in 3 ways:
Shared service both controllers use with set get data functions
Using direct data binding through root scope as a medium to store read data from
(terrible abusage of root scope)
through events
For me, best practice is Using a store and read service ;)

Error in retrieving value of dynamic ng-model

I am working on a form having multiple radio button listings in which I will need to create dynamic ng-model for each of the radio button. I am being able to do that, but when same I am trying to retrieve in controller (USING the ng-model iteration with angular forEach loop) it seems model cannot be replicated with console.log. Anyone help?
HTML
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<p ng-repeat="x in dummy">
<input type="radio" name="{{x.name}}" id="{{x.id}}" ng-model="Ques[x.id]"><span>{{x.value}}</span>
</p>
<button ng-click="ok()">Click</button>
<script>
//module declaration
var app = angular.module("myApp",[]);
//controller
app.controller("myCtrl", function($scope){
$scope.dummy = [
{name:"name1",value:"red",id:"id1"},
{name:"name2",value:"blue",id:"id2"},
{name:"name3",value:"yellow",id:"id3"},
];
$scope.ok = function(){
angular.forEach($scope.dummy, function(val, key) {
console.log($scope.Ques.val.id);
});
}
});
</script>
</head>
</html>
The Angular model is a JavaScript object itself. Instead of looping through the object, you can output the entire object to the console like this:
console.log( JSON.stringify($scope.dummy) );
To make it more easy to read and span multiple lines for complex objects, just add these arguments to stringify:
console.log( JSON.stringify($scope.dummy, null, 2) );
It also looks like you need a little work on how you handle the Radio buttons, I'll leave that to the excellent odetocode blog/site:
http://odetocode.com/blogs/scott/archive/2013/06/25/radio-buttons-with-angularjs.aspx
The main problem is that you're inside ngRepeat and it creates a child $scope, so to make it work, you should use or the Dot Rule or controller-as-syntax, as below:
$scope.model = {};
Then in your view:
<label>
<input type="radio" id="{{x.id}}" value="{{x.value}}" ng-model="model.Ques[x.id]">{{x.value}}
</label>
See it working:
(function() {
'use strict';
angular
.module('myApp', [])
.controller("myCtrl", function($scope) {
$scope.dummy = [
{
"name":"name1",
"value":"red",
"id":"id1"
},
{
"name":"name2",
"value":"blue",
"id":"id2"
},
{
"name":"name3",
"value":"yellow",
"id":"id3"
}
];
$scope.model = {};
$scope.ok = function() {
// With your original code:
angular.forEach($scope.dummy, function(val, key) {
console.log($scope.model.Ques[val.id]); // <- note the syntax
});
// Or you can get all key / values stored in radio buttons:
/*for (var key in $scope.model.Ques) {
console.log('Key => ', key);
console.log('Value => ', $scope.model.Ques[key]);
}*/
}
});
})();
<!doctype html>
<html lang="en">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<p ng-repeat="x in dummy">
<label>
<input type="radio" value="{{x.value}}" id="{{x.id}}" ng-model="model.Ques[x.id]">{{x.value}}
</label>
</p>
<button ng-click="ok()">Click</button>
</body>
</html>
For reference, check the #PankajParkar's answer.
Have a look at that.
<body ng-app="myApp" ng-controller="myCtrl">
<p ng-repeat="x in dummy">
<input type="checkbox" name="{{x.name}}" id="{{x.id}}" ng-model="Ques[x.id]" />
<label>{{x.name}}</label>
</p>
<button ng-click="ok()">Click</button>
<script>
//module declaration
var app = angular.module("myApp",[]);
//controller
app.controller("myCtrl", function($scope){
$scope.dummy = [
{name:"name1",value:"red",id:"id1"},
{name:"name2",value:"blue",id:"id2"},
{name:"name3",value:"yellow",id:"id3"},
];
$scope.Ques = {};
$scope.ok = function(){
angular.forEach($scope.dummy, function(val, key) {
console.log($scope.Ques[val.id]);
});
}
});

AngularJS custom filter called 2 times

I'm trying out Angular custom filter example from: https://scotch.io/tutorials/building-custom-angularjs-filters#filters-that-actually-filter which in my version looks like:
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="demo" >
<div>
<p><strong>Original:</strong></p>
<ul class="list">
<li ng-repeat="x in example1">{{ x.name }}</li>
</ul>
<p><strong>Static Language Filter:</strong></p>
<ul class="list">
<li ng-repeat="x in example1 | staticLanguage">{{x.name }}</li>
</ul>
</div>
</div>
<script>
var app = angular.module('myApp', []);
var counter=0;
app.controller('demo', function($scope){
$scope.example1 = [
{name: 'C#', type : 'static'},
{name: 'PHP', type : 'dynamic'},
{name: 'Go', type : 'static'},
{name: 'JavaScript', type: 'dynamic'},
{name: 'Rust', type: 'static'}
];
});
// Setup the filter
app.filter('staticLanguage', function() { // Create the return function and set the required parameter name to **input**
return function(input) {
counter+=1;
console.log(counter);
var out = [];
// Using the angular.forEach method, go through the array of data and perform the operation of figuring out if the language is statically or dynamically typed.
angular.forEach(input, function(input) {
if (input.type === 'static') {
out.push(input);
}
});
return out;
};
});
</script>
</body>
</html>
It seems from console.log that for some reason custom filter function staticLanguage is called two times but from the code itself it is called only one time: ng-repeat="x in example1 | staticLanguage"
Anyone has any idea why?
P.S I've yet to figure out what does "dirty-checking" has to do with my question...
if I remove counter variable and just put some console.log("text") in it's place staticLanguage function is still called two times
As far as I am aware this is due to AngularJS dirty-checking and has been asnwered elsewhere here. This is normal, have a read of the link.
This is normal, angularjs uses a 'dirty-check' approach, so it needs to call all the filters to see if any changes exist. After this it detects that you have a change on one variable (the one that you typed) and then it re-executes all filters again to detect if it has other changes.
See the first answer of this question
How does data binding work in AngularJS?
Well, I don't know if this will serve to you , but here's a snippet working that could be a possible solution for you:
var app = angular.module('app', []);
app.controller('mainCtrl', function($scope) {
$scope.languages = [
{
"name":"C#",
"type":"static"
},
{
"name":"PHP",
"type":"dynamic"
},
{
"name":"Go",
"type":"static"
},
{
"name":"JavaScript",
"type":"dynamic"
},
{
"name":"Rust",
"type":"static"
}
];
$scope.static_languages = $scope.languages.filter(x => x.type == 'static');
$scope.dynamic_languages = $scope.languages.filter(x => x.type == 'dynamic');
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js">
</script>
</head>
<body ng-controller="mainCtrl">
<div>
<p><strong>All languages:</strong></p>
<ul class="list">
<li ng-bind="language.name" ng-repeat="language in languages"></li>
</ul>
<p><strong>Static Languages Filter:</strong></p>
<ul class="list">
<li ng-bind="language.name" ng-repeat="language in static_languages"></li>
</ul>
<p><strong>Dynamic Languages Filter:</strong></p>
<ul class="list">
<li ng-bind="language.name" ng-repeat="language in dynamic_languages"></li>
</ul>
</div>
</body>
</html>

Undefined Controller AngularJS

My template is loading but I get an error that the controller is undefined. The controller does exist in my sources exactly at the location defined. What is wrong with this code?
Error: [ng:areq] http://errors.angularjs.org/1.2.14/ng/areq?p0=ItemsController&p1=not%20aNaNunction%2C%20got%20undefined
at Error (native)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js:6:450
at xb
This is my html:
<div ng-app="myApp">
<ul ng-controller="ItemsController" class="nav">
<input type="text" value="ItemName" ng-model="newItemName" placeholder="name of new item...">
<button ng-click="addItem()">Add Me</button>
<li ng-repeat="item in items.data" id="item{{item.id}}">
{{item.title}}
<a ng-click="deleteItem($index)" class="fa fa-trash-o"></a>
</li>
</ul>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular-route.js"></script>
<script type="text/javascript" src="js/te.js"></script>
'use strict';
var myApp = angular.module("myApp", []);
myApp.factory("items ", function() {
var items = {};
items.data = [];
return items;
});
myApp.controller("ItemsController", function ItemsController($scope, items) {
$scope.items = items;
$scope.deleteItem = function(index) {
items.data.splice(index, 1);
}
$scope.addItem = function(index) {
items.data.push({
id: $scope.items.data.length + 1,
title: $scope.newItemName
});
}
});
The Only Problem I see in your code is in following line:
myApp.factory("items ", function() {
space in the factory name "items " and you are injecting in your controller as items.
Here is the working PLNKR of your code, proof its working fine.
Happy Helping!
I have faced this problem many a times and usually this is caused when you have not included your js in the template.
If its not the case, then if you could share your some part of code, then only anybody will be able to answer it properly.

angularJS function in controller does not respond

AngularJs ng-click does not respond
I'm trying to use the GitHub Search-API.
When someone click the button my Controller should call a search from github here is my code
HTML:
<head>
<script src="js/AngularJS/angular.min.js"></script>
<script src="js/Controller/RepoController.js"></script>
</head>
<body ng-controller="RepoController">
<input type="text" ng-model="param" /><button ng-click="search()">suchen</button>
<ul>
<li ng-repeat="repo in repos">
{{repo.name}}
</li>
</ul>
</body>
and here is my controller:
var App = angular.module('RepoSearch', []);
function RepoController($scope, $http) {
$scope.repos = [];
$scope.search = function() {
$http.get('https://api.github.com/search/repositories?q='+$scope.param).success(function(data) {
$scope.repos = data['items'];
}
}
}
but my repos-array is still empty
when I swap the $http.get with an alert it is working fine.
You forgot to close ( after $scope.repos = data['items'];}, and to define ng-app, which in this case, can be in <head> : <head ng-app="RepoSearch">
http://docs.angularjs.org/api/ng.directive:ngApp
The reason could be cross domain call. Use JSONP to call into github.
$http.jsonp('https://api.github.com/search/repositories?q='+$scope.param+'&callback=JSON_CALLBACK'+).success(function(data) {
$scope.repos = data['items'];
}
The callback method maybe different so look at these SO posts
Using angularjs JSONP when callback cant be defined
Using JSONP method for $http service in AngularJS
In HTML
<head ng-app="RepoSearch">
<script src="js/AngularJS/angular.min.js"></script>
<script src="js/Controller/RepoController.js"></script>
</head>
<body ng-controller="RepoController">
<input type="text" ng-model="param" /><button ng-click="search()">suchen</button>
<ul>
<li ng-repeat="repo in repos">
{{repo.name}}
</li>
</ul>
</body>
in Controller
var App = angular.module('RepoSearch', []);
//bind controller with your app
App.controller('RepoController',RepoController);
function RepoController($scope, $http) {
$scope.repos = [];
$scope.search = function() {
$http.get('https://api.github.com/search/repositories?q='+$scope.param).success(function(data) {
$scope.repos = data['items'];
}
}
}

Categories

Resources