I cannot figure out why I am getting an infinite $digest loop error in this simple demo. I've read about these loops in the official documentation, but I don't understand why this demo triggers the error.
CodePen Demo
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.min.js"></script>
<script>
var TestApp = angular.module("TestApp", []);
TestApp.controller("TestCtrl", function ($scope) {
$scope.Counter = 0;
$scope.IncrementCounter = function () {
$scope.Counter+=1;
return true
}
});
</script>
<body ng-app='TestApp'>
<div ng-controller='TestCtrl'>
<label>
Number of times <code>$scope.IncrementCounter()</code>
has been invoked: {{ Counter }}
</label>
<input type="hidden" value="{{ IncrementCounter() === true }}" />
</div>
</body>
Is there no way to increment a $scope variable from within a $scope function without causing the whole model to go through a digest cycle?
The problem is because you were calling the IncrementCounter() function from within a binding in the view. In a $digest cycle Angular looks at the {{ }} brackets and executes any functions held within them, your IncrementCounter() function happened to change a value on the scope $scope.Counter, and this in turn kicks off another $digest cycle, and so the process repeats continually.
You should do all of this in the Controller and only use the view for displaying values held on the scope. You could for example do this using a function that calls itself, optionally using the $timeout service to create a delay:
var TestApp = angular.module("TestApp", []);
TestApp.controller("TestCtrl", function ($scope, $timeout) {
var init = function() {
$scope.counter = 0;
incrementCounter();
};
var incrementCounter = function () {
$scope.counter++;
$timeout(function() {
incrementCounter();
}, 1000);
};
init();
});
With the above code your view can then be:
<html>
<head>
<title>AngularJS Function Invocation Tester</title>
</head>
<body ng-app='TestApp'>
<h2>AngularJS Function Invocation Tester</h2>
<div ng-controller='TestCtrl'>
<label>
Number of times <code>$scope.IncrementCounter()</code>
has been invoked: <span class="counter">{{ counter }}</span>
</label>
</div>
</body>
</html>
Related
In this plunk I have a div with a border width that is determined by the value in an input field. I achieve that with ng-style containing a getBorder() function.
My issue is that getBorder() is called twice and sometimes three times, instead of once. Why does this happen and how to fix it?
HTML
Width: <input type="number" ng-model="borderWidth"/>
<br/>
<div style="background-color:orange;height:200px;width:100px"
ng-style="{ 'border': getBorder() }"></div>
JavaScript
var app = angular.module('app', []);
app.controller('ctl', function ($scope) {
$scope.getBorder = function(){
alert('getBorder called');
return $scope.borderWidth + 'px solid black';
};
});
This is because of the digest cycles in AngularJS.
AngularJS registers watchers to observe changes in the scope, and as soon as a change happens, it refreshes the bindings between corresponding views/models using digest cycles. This is the reason why you can see live changes in the data and on the screen.
ngModel is one of the directives which registers a watcher. So, the problem you came across, is not really a problem, because ng-style is trying to get the value using getBorder().
I hope that this solution solved your problem
var app = angular.module('app', []);
app.controller('ctl', function ($scope) {
$scope.borderWidth = 1;
$scope.$watch('borderWidth', function (newVal, oldVal) {
console.log()
if (angular.isDefined(newVal)) {
$scope.styleBorder = newVal + 'px solid black';
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="app">
<body>
<div ng-controller="ctl">
Width: <input type="number" ng-model="borderWidth"/>
<br/>
<div
style="background-color:orange;height:200px;width:100px;"
ng-style='{"border": styleBorder}'
></div>
</div>
</body>
</html>
Recently I am learning Angularjs, my code seems not work as expected:
this is my div:
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="data.name" value=""/>
{{data.count}}
</div>
</div>
my controller is:
<script>
var app = angular.module('myApp',[]);
app.controller('myController', function($scope) {
$scope.data = {
name:"tom",
count = 0
}
$scope.$watch('data', function(oldValue,newValue) {
++$scope.data.count;
},true);
})
</script>
what I expect is when I type something in the <input> box, the {{data.count}} will increase by 1 each time. However the code is initially 11 and each time I make changes in the input field, the count is increased by 11, can someone help me find where have I done wrong? Thanks a lot in advance.
Why this happen?
Watcher calls multiple times because you created watcher for full object data. Flag true will create sub-watcher for every value in object.
Its a proper behavior. I believe you want something like:
$scope.$watch('data', function(oldValue,newValue) {
if(oldValue.name != newValue.name){
++$scope.data.count;
}
},true);
Demo Fiddle
The second solution is to watch on name only:
$scope.$watch(function(){
return $scope.data.name
}, function(oldValue,newValue) {
++$scope.data.count;
});
Here is a another way to do it. Use the ng-keydown directive and update the count only when a key is pressed inside the input element.
var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
$scope.data = {
name: "tom",
count: 0
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app='myApp'>
<input type="text" ng-model="data.name" value="" ng-keydown="data.count = data.count+1" /> {{data.count}}
</div>
One 'correct' way to specify multiple actions within an ng-click is to separate each one with a semi-colon, however I've come across code that specifies two ng-click directives on the same tag. Will both sets of the ng-click instructions be carried out, will this cause an error, or will only one or other of the ng-click instructions be executed?
When you have many ng-clicks on a tag, only the first one will get executed.
DEMO
var myApp = angular.module('myApp', []);
myApp.controller('CookieCtrl', function ($scope) {
$scope.print1 = function () {
alert("first");
}
$scope.print2 = function () {
alert("second");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div id="button-holder" ng-controller="CookieCtrl">
<button ng-click="print1()" ng-click="print2()">Bump!</button>
</div>
</div>
You could also have many functions executed on ng-click using a ; separated.
DEMO
var myApp = angular.module('myApp', []);
myApp.controller('CookieCtrl', function ($scope) {
$scope.print1 = function () {
alert("first");
}
$scope.print2 = function () {
alert("second");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div id="button-holder" ng-controller="CookieCtrl">
<button ng-click="print1();print2();">Bump!</button>
</div>
</div>
I have a curious error on my angular app.
I have a controller where I declare some variables. If I am going to write them in the html it works as expected. But if I try to change them, eg in a input-box nothing happens.
Even when I try to call a function from a button or something nothing happens.
The code below is a simplified version of my controller.
angular.module('myModule', [])
.controller('myCtrl', ['$rootScope', '$scope', 'SomeServices',
function ($rootScope, $scope, someServices) {
'use strict';
$scope.myVar = "foobar";
$scope.myFunction = function() {
// it never gets here
}
$scope.$watch("myVar", function() {
// it even never gets here
});
}]);
My HTML
<!-- change this and the watcher will not doing anything -->
<!-- the value will bee foobar, but after that nothing happens -->
<input type="text" ng-model="myVar" />
<button ng-click="myFunction()">click here and nothing will happen</button>
If I tried it in codepen it works normaly.
Has anyone even got such a behaviour?
I don't see any problem why it should not work, may be this is more simplified demonstration,
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.myVar = "foobar";
$scope.myFunction = function() {
console.log($scope.myVar + ' myFunction called')
}
$scope.$watch("myVar", function() {
console.log($scope.myVar + ' value changed')
});
});
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9">
</script>
<div ng-app="myApp" ng-controller="MainCtrl">
<input type="text" ng-model="myVar" />
<button ng-click="myFunction()">click here and nothing will happen</button>
</div>
Open developer tool to see logs.
Elaborate more if you are facing some other problem.
I've been following a course to learn angularjs and I can't seem to get a simple ng-click binding to work.
HTML:
<body ng-controller="MainController">
<div ng-app="githubViewer">
<h1>{{message}}</h1>
<div>{{ error }}</div>
{{username}}
<form name="searchUser">
<input type="search" placeholder="Username to find" ng-model="username" />
<input type="submit" value="Search" ng-click="search(username)" />
</form>
<div>
<div>{{user.name}}</div>
<img ng-src="http://www.gravatar.com/avatar/{{user.gravatar_id}}" title="{{user.name}}">
{{user.gravatar_id}}
</div>
</div>
</body>
Javascript:
(function () {
var module = angular.module("githubViewer", []);
var MainController = function ($scope, $http) {
var onUserComplete = function (response) {
$scope.user = response.data;
};
var onError = function (reason) {
$scope.error = "Could not fetch the user";
$scope.reason = reason;
};
$scope.username = "angular";
$scope.message = "Github Viewer";
$scope.search = function (username) {
$http.get("https://api.github.com/users/" + username)
.then(onUserComplete, onError);
};
};
module.controller("MainController", MainController);
}());
When you click the search button (search for username "odetocode" or "robconery") it is supposed to display an image but the click event does not seem to be firing. I have searched the documentation and looked over the course again but I can't see what I'm doing wrong.
I'm currently using version 1.2.16 of angularjs.
You have the ng-controller declaration outside of the ng-app declaration right now:
<body ng-controller="MainController">
<div ng-app="githubViewer">
It should be the other way around, or have both on the same element
<body ng-app="githubViewer" ng-controller="MainController">
<div>
AngularJS evaluates your code, and checks for any directives you have declared from the ng-app element down, including the element it is declared on; This currently is missing the ng-controller directive, as it is placed on a parent element of the ng-app element.
You need to put the controller within the context of the module to have it within its scope.
Like so
<body ng-app="githubViewer" ng-controller="MainController">
Demo here