Check/uncheck all feature not working in AngularJS - javascript

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?

Related

angular js - function firing multiple times with ng-checked inside ng-repeat

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

Remove items from Array skips certain items

I have the following page (simple):
As you can see, at the top I have an input, then <ul> and finally a button to save changes. My <ul> is bound to a array of items. Once user clicks Uloz zmeny (Save Changes) I am triggering ng-click="vm.SaveChanges()" which looks like following:
vm.SaveChanges = function () {
angular.forEach(vm.items, function (value, key) {
if (value.toRemove == true) {
//remove item from the list
var iIndex = vm.items.indexOf(value);
vm.items.splice(iIndex, 1);
};
});
};
where vm is defined as following at the beginning of my code:
(function () {
"use strict";
angular.module("app-shopping").controller("itemsController", itemsController);
function itemsController($http) {
var vm = this;
vm.items = [];.....more code after here
Every item under my '' has the following structure:
{
"id": 2,
"orderId": 2,
"text": "Item 2",
"toRemove": true
},
Finally, when user checks an item under the <li> I am triggering vm.toggleCompleted() which simply looks like this (it simply changes a boolean state of current item from true to false or vice versa):
vm.toggleCompleted = function (sItem) {
sItem.toRemove = !sItem.toRemove;
};
Here comes the question: Why when I run this code it does not remove all checked items in the array? For example in this specific case (see image above) it would only remove Item 2 and skip Item 3. I believe that the problem is caused by the fact that when Item 2 is remove from the list, Item 3 takes the index of already existing Item 2 and therefore is skipped. Is this assumption correct? If yes, how do I need to change the code to make this run?
P.S. Edit to my code as recommended:
<li class="list-group-item" ng-repeat="sItem in vm.items">
<div class="checkbox checkbox-success">
<input id="ListItem{{$index}}" type="checkbox" placeholder="test placeholder" ng-model="sItem.toRemove" ng-click="sItem.toRemove=!sItem.toRemove" />
<label for="ListItem{{$index}}">{{sItem.text}}</label>
</div>
</li>
I have changed the code the following way and it is working now:
vm.SaveChanges = function () {
for (var i = vm.items.length - 1; i > -1; i--)
{
if (vm.items[i].toRemove == true)
{
vm.items.splice(i, 1);
}
}
};
Instead of using toggleCompleted use below at the place of check-box input
<input type="checkbox" ng-model="item.toRemove" ng-click="item.toRemove=!item.toRemove" />
And Use your new saveChanges method .. this should work fine..
Yes, to bypass this issue, just revert your array traversal, because this way you can guarantee that no position of unchecked elements changes during deletion.
So, you code at the end should be like that:
Template:
<li class="list-group-item" ng-repeat="sItem in vm.items">
<div class="checkbox checkbox-success">
<input id="ListItem{{$index}}" type="checkbox" placeholder="test placeholder" ng-click="sItem.toRemove = !sItem.toRemove" />
<label for="ListItem{{$index}}">{{sItem.text}}</label>
</div>
</li>
<button class="btn btn-success" ng-click="SaveChanges()"> Save</button>
Controller:
$scope.SaveChanges = function () {
for (var i = $scope.vm.items.length - 1; i > -1; i--){
if ($scope.vm.items[i].toRemove) {
$scope.vm.items.splice(i, 1);
};
}
}

Angular ng-class not updating after ng-change is fired

EDIT: It seems my issue is more complex than the simple typo in the code below. I have 3rd party components interacting and raising change events on the inputs which angular is picking up when I don't want it to. The problem is somewhere in there. I will try to find a simple fiddle and update the question if I manage it.
I have a pair of inputs, which have an ng-model and share an ng-change function. The ng-change sets a boolean value in the controller which is supposed to update the class(es) on the inputs through an ng-class directive. However the first of the two inputs never seems to get any updates to it's class. Here is a simplified version:
View:
<div ng-controller='TestCtrl'>
<input type="text" ng-class="{ 'invalid': firstInvalid }" ng-model="firstValue" ng-change="doOnChange()"></input>
<input type="text" ng-class="{ 'invalid': secondInvalid }" ng-model="secondValue" ng-change="doOnChange()"></input>
</div>
Controller:
function TestCtrl($scope) {
$scope.firstInvalid = false;
$scope.secondInvalid = false;
$scope.firstValue = '';
$scope.secondValue = '';
$scope.doOnChange = function () {
console.log('change fired');
$scope.firstInValid = !$scope.firstInvalid;
$scope.secondInvalid = !$scope.secondInvalid;
};
};
Codepen:
http://codepen.io/Samih/pen/ZGXQPJ
Notice how typing in either input, the second input updates with the class just as I would expect, however the first never gets the 'invalid' class.
Thanks in advance for your help.
Check your code for typos:
This line
$scope.firstInValid = !$scope.firstInvalid;
should be
$scope.firstInvalid = !$scope.firstInvalid;
It should be $scope.firstInvalid, not $scope.firstInValid.
It seems to work for me :
Just edited 'firstInvalid'.
View:
<div ng-controller='TestCtrl'>
<input type="text" ng-class="{ 'invalid': firstInvalid }" ng-model="firstValue" ng-change="doOnChange()"></input>
<input type="text" ng-class="{ 'invalid': secondInvalid }" ng-model="secondValue" ng-change="doOnChange()"></input>
</div>
Controller:
function TestCtrl($scope) {
$scope.firstInvalid = false;
$scope.secondInvalid = false;
$scope.firstValue = '';
$scope.secondValue = '';
$scope.doOnChange = function () {
console.log('change fired');
$scope.firstInvalid = !$scope.firstInvalid;
$scope.secondInvalid = !$scope.secondInvalid;
};
};
Codepen: http://codepen.io/vikashverma/pen/BNwKmQ

How to preselect Section in knockout-postbox.js so that the content is visible

After looking into Ryan Niemeyer's brilliant knockout-postbox.js
I made some minor adapting and want the menu to open with a preselected section. Hence I added a variable to the initialization of the selectedSection observable as seen below.
var MenuModel = function() {
var preselected = "Profile";
this.name = ko.observable().subscribeTo("nickName");
this.sections = ["Profile", "Notifications","Users","Projects"];
this.selectedSection = ko.observable(preselected).publishOn("section");
};
This selects the desirable section, however the contents of the section remains invisible.
Here's the preselected sections viewModel:
var ProfileModel = function() {
//apply a filter to turn the value that we receive into a boolean
this.visible = ko.observable().subscribeTo("section", function(newValue) {
return newValue === "Profile";
});
//some more code - syncing and subscribing/publishing observables.
};
The HTML goes like this:
<div id="menu">
<h2><span data-bind="text: name"></span>'s Settings</h2>
<ul class="nav nav-pills" data-bind="foreach: sections">
<li data-bind="css: { active: $root.selectedSection() === $data }">
</li>
</ul>
</div>
<div id="profile" data-bind="visible: visible">
<h3>Profile</h3>
<label>Nick name: <input id="nick" data-bind="value: nickName" /> </label>
<label>Email: <input data-bind="value: emailAddress" /></label>
</div>
The question is, how can I trigger the visible observable of the ProfileModel with a preselected setting of MenuModel's selectedSection observable, such that the contents are shown?
Full fiddle: http://jsfiddle.net/AsleG/b6gwn6ak/
The subscribeTo helper can take a boolean as a second argument to indicate that you want to initialize it using the last published value. Your compare function can then be passed as the third arg. It would look like:
//apply a filter to turn the value that we receive into a boolean
this.visible = ko.observable().subscribeTo("section", true, function(newValue) {
return newValue === "Profile";
});

How to get selected checkboxes on button click in angularjs

I want to do something like this
<input type="checkbox" ng-model="first" ng-click="chkSelect()"/><label>First</label>
<input type="checkbox" ng-model="second" ng-click="chkSelect()"/><label>Second</label>
<input type="checkbox" ng-model="third" ng-click="chkSelect()"/><label>Third</label>
<input type="checkbox" ng-model="forth" ng-click="chkSelect()"/><label>Forth</label>
<button>Selected</button>
On button click I want to display selected checkbox labelname.
$scope.chkSelect = function (value) {
console.log(value);
};
Because the checkboxes are mapped, you can reference $scope.first, $scope.second, etc in your chkSelect() function. It's also possible to have a set of checkboxes mapped as a single array of data instead of having to give each checkbox a name. This is handy if you are generating the checkboxes, perhaps from a set of data.
I agree with Bublebee Mans solution. You've left out a lot of detail on why you're trying to get the label. In any case if you REALLY want to get it you can do this:
$scope.chkSelect = function (value) {
for(var key in $scope){
var inputs = document.querySelectorAll("input[ng-model='" + key + "']");
if(inputs.length){
var selectedInput = inputs[0];
var label = selectedInput.nextSibling;
console.log(label.innerHTML);
}
};
};
You can mess around with it to see if it's indeed selected.
fiddle: http://jsfiddle.net/pzz6s/
Side note, for anybody who knows angular please forgive me.
If you are dealing with server data, you might need isolated html block and deal with data in controller only.
You can do it by creating array in controller, maybe your data from response, and use ngRepeat directive to deal independently in html code.
Here is what I am telling you:
HTML:
<form ng-controller="MyCtrl">
<label ng-repeat="name in names" for="{{name}}">
{{name}}
<input type="checkbox"
ng-model="my[name]"
id="{{name}}"
name="favorite" />
</label>
<div>You chose <label ng-repeat="(key, value) in my">
<span ng-show="value == true">{{key}}<span>
</label>
</div>
</form>
Javascript
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.names = ['pizza', 'unicorns', 'robots'];
$scope.my = { };
}
You want to have something like the following in your controller (untested, working from memory):
$scope.checkBoxModels = [ { name: 'first', checked: false }, { name: 'second', checked: false }, { name: 'third', checked: false }, { name: 'fourth', checked: false } ];
Then in your view:
<input ng-repeat"checkboxModel in CheckBoxModels" ng-model="checkBoxModel.checked" ng-click="chkSelect(checkBoxModel)" /><label>{{checkBoxModel.name}}</label>
Then update your function:
$scope.chkSelect = function (checkBoxModel) {
console.log(checkBoxModel.name);
};

Categories

Resources