I have the following html which has an ng-repeat that generates checkboxes:
<span ng-repeat="name in $ctrl.widgetSelectorNames" class="widget-selectors">
<label class="checkbox" for="{{name}}">
<input type="checkbox" value="{{name}}" ng-model="name" ng-change="$ctrl.toggleVisibility(name)" ng-checked="$ctrl.checkIfHidden(name)"/>
{{name}}
</label>
</span>
what I'm trying to do is have ng-checked=true, if the relevant item has property of hidden as true. I think my logic is correct, but the ng-checked function runs wayy too many times:
let numTimesCalled = 0;
$ctrl.checkIfHidden = function (name){
numTimesCalled++;
console.log(numTimesCalled);
$ctrl.widgets.forEach(widget => {
if (name == widget.name && widget.hidden) {
return true;
}
})
return false;
}
there are six items in $ctrl.widgetSelectorNames yet the function is called 48 times as per the numTimesCalled variable! what's happening? if this isn't the right way to do it, what's a better way?
It's an old question but for those who want to understand what is going on behind the scene can read.
The ng-checked expression needs to be evaluated on every $digest.
See here:
https://docs.angularjs.org/guide/scope#integration-with-the-browser-event-loop
Related
I have an array of objects with property published which can be true or false.
I'm building a filter to be able to display only published or only not published items.
So far the published one works fine like this:
<input
type="checkbox"
ng-model="search.published"
ng-change="search.published = search.published ? true : undefined">
Published
And in the ng-repeat it looks like this:
ng-repeat="exhibitor in filterItems = (data | filter: search)"
The problem comes when I try to add another checkbox to display only unpublished items.
I've tried this with a second checkbox:
<input
type="checkbox"
ng-model="search.published"
ng-change="search.published = search.published ? false : undefined">
Unpublished
But of course it can't have the same model as the published items one. Also, the checkbox won't get ticked even if I remove the first checkbox.
Any tips on how to work around this?
Checkboxes automatically assign a true or false value to their ng-model values, so it is unnecessary to use ng-change the way you are.
<input
type="checkbox"
ng-model="search.published">
Published
When checked, search.published will be true. When unchecked, it will be false.
Same goes for the second checkbox, but you should use a different ng-model property.
<input
type="checkbox"
ng-model="search.unpublished">
Unpublished
Next you will need to create a custom filter in your controller:
$scope.myCustomFilter = function(exhibitor) {
if (exhibitor.published && exhibitor.published === $scope.search.published) {
return exhibitor;
} else if (!exhibitor.published && exhibitor.published === $scope.search.unpublished) {
return exhibitor;
}
};
You will need you make sure you define $scope.search in your controller.
$scope.search = {
published: false,
unpublished: false
};
Now for your ng-repeat:
ng-repeat="exhibitor in filterItems | filter:myCustomFilter"
I am building checkboxes dynamically using Angular. I set the ng-checked value based on a number of conditions.
The checkbox is appropriately checked on pageload. I need to call the makeAddressDefault() function (currently used in ng-click) on page load when ng-checked = true.
<input type="checkbox"
id="defaultAddress{{$id}}"
ng-model="addressChecked"
name="defaultAddress"
ng-click="makeAddressDefault(add, $event)"
ng-checked="addresses.length === 1 || add.IsDefault() || isSelected(add.id)">
<label ng-init="isAddressDefault(addresses, add)" for="defaultAddress{{$id}}">
{{add.FirstName}} {{add.LastName}}<br />
{{add.Address1}}<br />
{{add.City}}, {{add.State}} {{add.ZIP}}
</label>
What is the best way to handle this? Thanks!
In your controller why don't you do something like
$scope.init = function(){
angular.forEach(addresses, function(add){
if(addresses.length === 1 || add.IsDefault() || isSelected(add.id)){
makeAddressDefault(add)
}
})
}
$scope.init()
It will automatically call your init function once the controller has been initialized.
I have tried to set up a function that can select/unselect all the checkboxes in a view. But it doesn't seem to work at all. Is there something I am missing out?
I based my implementation on the following: http://jsfiddle.net/deeptechtons/TKVH6/.
detail.html
<li class="item item-checkbox">
All Services<br>
<label class="checkbox">
<input type="checkbox" ng-model="selectedAll" ng-click="checkAll()" ng-model="selectedAll">
</label>
</li>
<li class="item item-checkbox" ng-repeat="e in inventory">
{{ e.title }}<br>
<span class="grey-text">£{{ e.price | number: 2 }}</span>
<label class="checkbox">
<input type="checkbox" ng-model="item.selected">
</label>
</li>
controller.js
$scope.checkAll = function () {
if ($scope.selectedAll) {
$scope.selectedAll = true;
} else {
$scope.selectedAll = false;
}
angular.forEach($scope.inventory, function (item) {
item.selected = $scope.selectedAll;
});
};
This part of your code makes no sense. If the value is true you set it to true, same with false, just remove it.
if ($scope.selectedAll) {
$scope.selectedAll = true;
} else {
$scope.selectedAll = false;
}
Also, you would be better off using ng-change instead of ng-click.
Couple other things: you have added ng-model twice to your first input element.
And the main problem is: you're referencing each item in your ng-repeat with e and within the loop you use item.selected. You should be using e.selected or change your ng-repeat definition:
<input type="checkbox" ng-model="e.selected">
OR
<li class="item item-checkbox" ng-repeat="item in inventory">
In case you go with the latter you have to change e.price and e.title to item.price and item.title.
If you stare really hard at the statement:
if ($scope.selectedAll) {
$scope.selectedAll = true;
} else {
$scope.selectedAll = false;
}
you might notice that the assignments are redundant: you are setting it to true if is is already true, and to false if it is already false.
In general, when angular does not do what you expect, debugging your code by setting a break point in the relevant scope method is pretty easy, and will help identify the cause of bugs such as this one.
I would rather not put a function into an input element. What you can do is do a ng-model on the selectAll checkbox and take in an boolean and use that boolean with an ng-if to check uncheck the other fields. Am I clear?
I'm relatively new to AngularJS and I've found myself slightly confused.
Here is my controller:
app.controller("calcController", function($scope) {
$scope.interests=[{time:'month',amount:0},
{time:'quarter',amount:0},
{time:'year',amount:0}];
$scope.rates=[{rate:0.01,lower:0,upper:1000,desc:'£0 - £1000'},
{rate:0.02,lower:1000,upper:5000,desc:'£1000 - £5000'},
{rate:0.03,lower:5000,upper:'none',desc:'£5000+'},];
$scope.balance=0;
$scope.updatedIntAmount=function(balance){
if(balance<0){
return 0;
}
else if(balance>=rates[1].lower && balance<rates[1].upper){
return balance*rates[1].rate;
}
else if(balance>=rates[2].lower && balance<rates[2].upper){
return balance*rates[2].rate;
}
else {
return balance*rates[3].rate;
}
}
});
What I want is some way to bind the value of interests.amount to updatedIntAmount.
Here's my HTML:
<div ng-controller="calcController" class="container">
<div class="form-group">
<label for="balInput">Balance:</label>
<input id="balInput" type="text" class="form-control" ng-model="balance">
</div>
<p ng-repeat="interest in interests">{{'per '+interest.time+':'+interest.amount}}</p>
</div>
So what I have is interests.amount dependent on calculateIntAmount, which in turn is dependent on both rates.rate and balance. How can I get these interests.amount values to change whenever balance is updated?
Thanks
Try adding ng-change to your input field like so <input id="balInput" type="text" class="form-control" ng-model="balance" ng-change="updatedIntAmount(balance)">. This will allow you to call the function on a change to the input field and thus do whatever you want within the function in your controller.
You can learn more at the official AngularJS documentation page
You can look into using $scope.$watch to observe any changes in your balance variable, and then update your interests.amount accordingly.
In your controller, you would have:
$scope.$watch('balance', function(newBalance) {
$scope.interests.amount = $scope.updatedIntAmount(newBalance);
});
Lot of errors are present in your code.
as first there is nothing like rates[1] instead you should change it to $scope.rates[1].
Second : your updatedIntAmount function contains rates[3] which is again wrong as you have rates[2] as max limit according to array index rule.
Your updatedIntAmount function return a single value based upon the balance is inserted. in that case after correcting the errors just add :
{{updatedIntAmount(balance)}}
in your html code. it will change as soon as change in balance.
$scope.updatedIntAmount=function(balance){
if(balance<0){
return 0;
}
else if(balance>=$scope.rates[0].lower && balance<$scope.rates[0].upper){
return balance*$scope.rates[0].rate;
}
else if(balance>=$scope.rates[1].lower && balance<$scope.rates[1].upper){
return balance*$scope.rates[1].rate;
}
else {
return balance*$scope.rates[2].rate;
}
}
I have a little demo on jsfiddle :
HTML:
<input type="checkbox" id="chkUsr{{usr.Id}}" ng-model="usr.Checked">
{{usr.Name}} : {{usr.Checked}}
<input type="text" ng-model="research"/>
<div ng-repeat="entity in entities | filter:research | log">
Hello {{entity.id}}!
JavaScript:
app.filter('log', function() {
return function(items) {
console.log('yo');
return items;
};
});
The log filter is called when input change (even the checkbox).
How to change that and trigger log filter only when text input change ?
That's because angular runs $digest and updates all the scope properties even if one variable in the scope changes. It is called "dirty checking".
Learn more about how angular works: http://www.sitepoint.com/understanding-angulars-apply-digest/