Check/ Uncheck All checkboxes - AngularJS ng-repeat - angularjs

I have a structure which looks like this: http://jsfiddle.net/deeptechtons/TKVH6/
<div>
<ul ng-controller="checkboxController">
<li>Check All
<input type="checkbox" ng-model="selectedAll" ng-click="checkAll()" />
</li>
<li ng-repeat="item in Items">
<label>{{item.Name}}
<input type="checkbox" ng-model="item.Selected" />
</label>
</li>
</ul>
</div>
angular.module("CheckAllModule", [])
.controller("checkboxController", function checkboxController($scope) {
$scope.Items = [{
Name: "Item one"
}, {
Name: "Item two"
}, {
Name: "Item three"
}];
$scope.checkAll = function () {
if ($scope.selectedAll) {
$scope.selectedAll = true;
} else {
$scope.selectedAll = false;
}
angular.forEach($scope.Items, function (item) {
item.Selected = $scope.selectedAll;
});
};
});
When check all is selected or deselected, all other checkboxes are selected. But when "Check All" is selected, and I deselect one of the items, I want "Check All" to be deselected.
Thanks in advance!
(PS: Thanks to deeptechtons for the JSFiddle)

If you are using Angular 1.3+ you can use getterSetter from ng-model-options to solve this and avoid manually keeping track of the allSelected state
HTML:
<input type="checkbox" ng-model="allSelected" ng-model-options="{getterSetter: true}"/>
JS:
var getAllSelected = function () {
var selectedItems = $scope.Items.filter(function (item) {
return item.Selected;
});
return selectedItems.length === $scope.Items.length;
}
var setAllSelected = function (value) {
angular.forEach($scope.Items, function (item) {
item.Selected = value;
});
}
$scope.allSelected = function (value) {
if (value !== undefined) {
return setAllSelected(value);
} else {
return getAllSelected();
}
}
http://jsfiddle.net/2jm6x4co/

You can use this function to check if all records are checked whenever a checkbox changes:
$scope.checkStatus= function() {
var checkCount = 0;
angular.forEach($scope.Items, function(item) {
if(item.Selected) checkCount++;
});
$scope.selectedAll = ( checkCount === $scope.Items.length);
};
The view code:
<input type="checkbox" ng-model="item.Selected" ng-change="checkStatus();"/>
http://jsfiddle.net/TKVH6/840/

working example: http://jsfiddle.net/egrendon/TKVH6/845/
view:
<input type="checkbox" ng-model="item.Selected" ng-click="setCheckAll(item)" />
controller:
angular.module("CheckAllModule", [])
.controller("checkboxController", function checkboxController($scope) {
$scope.Items = [{
Name: "Item one"
}, {
Name: "Item two"
}, {
Name: "Item three"
}];
$scope.checkAll = function () {
if ($scope.selectedAll) {
$scope.selectedAll = true;
} else {
$scope.selectedAll = false;
}
angular.forEach($scope.Items, function (item) {
item.Selected = $scope.selectedAll;
});
};
$scope.setCheckAll = function (item) {
//
// Check if checkAll should be unchecked
//
if ($scope.selectedAll && !item.Selected) {
$scope.selectedAll = false;
}
//
// Check if all are checked.
//
var checkCount = 0;
angular.forEach($scope.Items, function(item) {
if(item.Selected) checkCount++;
});
$scope.selectedAll = ( checkCount === $scope.Items.length);
};
});

Here is a neat little way of doing this
http://jsfiddle.net/TKVH6/850/
Use a little bit of undescore (just the reduce) and ng-checked
<input type="checkbox" ng-model="selectedAll" ng-click="checkAll()" ng-checked="allSelected()"/>
$scope.allSelected = function () {
var allSelect = _.reduce($scope.Items, function (memo, todo) {
return memo + (todo.Selected ? 1 : 0);
}, 0);
return (allSelect === $scope.Items.length);
};
You could use javascripts built in .reduce if you did not want to use underscore.

This answer has similar logic to Kmart2k1's answer. It puts the responsibility of updating the master checkbox on each child in the ng-repeat. Instead of using array.forEach, I use array.some to more quickly check if any of the items are unselected.
HTML:
<li ng-repeat="item in Items">
<label>{{item.Name}}
<input type="checkbox" ng-model="item.Selected" ng-change="notifyMaster()"/>
</label>
</li>
Javascript
$scope.notifyMaster = function () {
$scope.selectedAll = !$scope.Items.some(function (item) {
return !item.Selected; //check if ANY are unchecked
});
}
Forked fiddle

Related

Scope from controller does not pass to directive

I have a html like this :
<div id="create-group" ng-controller="groupCreateController">
<div id="container">
<h1>Create group</h1>
<div class="row">
<div class="col-md-4"><input placeholder="Group Name.." ng-model="group.name"></div>
<div class="col-md-8">
<label>Group Description : </label>
<textarea ng-model="group.description"> </textarea>
</div>
</div>
<br/>
<div class="row">
<div class="col-sm-6">
<usermgr-permission-list group="group"></usermgr-permission-list>
<button type="button" class="btn btn-md btn-primary" ng-click="btnSave_click($event)">SAVE</button>
</div>
<div class="col-sm-6">
<usermgr-user-list group="group"></usermgr-user-list>
</div>
</div>
</div>
</div>
My controller is :
(function (module) {
'use strict';
module.controller('groupCreateController', function ($scope, $rootScope, $routeParams, $location, userGroupService, $mdDialog) {
$scope.group = [];
$scope.init = function () {
if ($routeParams.hasOwnProperty('id')) {
//edit mode
// $scope.trans.heading = 'Edit Release';
// $scope.trans.saveBtn = 'Save';
var id = parseInt($routeParams.id);
getUserGroup(id);
} else {
$scope.group[0].id = 0;
$scope.group[0].permissions = [];
$scope.assignedPermissions = [];
$scope.enrolledUsers = [];
$scope.group[0].users = [];
$scope.group[0].name = '';
$scope.group[0].description = '';
}
};
function getUserGroup(id) {
userGroupService.getbyid(id).then(function (info) {
if (info !== undefined && info.id === id) {
$scope.group[0].id = info.id;
$scope.group[0].name = info.name;
$scope.group[0].description = info.description;
console.log($scope.group);
// $rootScope.$broadcast('rCube-user-mgt-users-list', info.id);
// $rootScope.$broadcast('rCube-user-mgt-permissions-list', info.id);
}
else {
}
}).catch(function (exception) {
console.error(exception);
});
}
$scope.init();
});
})(angular.module('r-cube-user-mgt.user-group'));
I have two custom directives in the first block of code for user permissions and users. The group scope that i pass with the directive does not contain the values i put in the getUserGroup(id) function. The group name and group description shows up so the scope.group in the controller is filled, however thats not the case once i pass it to my directives. here is the directives code as well :
permissions list :
(function (module) {
'use strict';
module.directive('usermgrPermissionList', function () {
return {
restrict: 'E',
scope:{
group: '='
},
controller: function ($scope, permissionService) {
$scope.updatedPermissions=[];
console.log($scope.group); //it doesnt have the values from the controller ..
if (!$scope.group.hasOwnProperty('permissions')) {
$scope.group.permissions = [];
}
function getData() {
console.log("inside getDAta for permission list" + $scope.group.id;
permissionService.getPermissionsFiltered($scope.group.id).then(function (info) {
if (info && info.length > 0) {
console.log(info);
$scope.group.permissions = info.map(function (a, index, array) {
return {
id: a.id,
name: a.name,
description: a.description,
assigned: a.assigned
};
});
}
}).catch(function (exception) {
console.error(exception);
});
} //end of getData()
$scope.init = function () {
getData();
};
$scope.init();
},
templateUrl: 'r-cube-user-mgt/permission/list/list.tpl.html'
};
});
})(angular.module('r-cube-user-mgt.permission'));
can anyone help?
you cannot assign property to an array like this $scope.group.id = 0;
either make $scope.group object
$scope.group = {};
or add properties to an index
$scope.group = [];
$scope.init = function () {
if ($routeParams.hasOwnProperty('id')) {
//edit mode
// $scope.trans.heading = 'Edit Release';
// $scope.trans.saveBtn = 'Save';
var id = parseInt($routeParams.id);
getUserGroup(id);
} else {
$scope.group[0].id = 0;
$scope.group[0].permissions = [];
$scope.assignedPermissions = [];
$scope.enrolledUsers = [];
$scope.group[0].users = [];
$scope.group[0].name = '';
$scope.group[0].description = '';
}
};
So I solved the issue by adding broadcast to send the id when the directive loads. This worked!
in the Group controller i add broadcast and send the group.id
function getUserGroup(id) {
userGroupService.getbyid(id).then(function (info) {
if (info !== undefined && info.id === id) {
$scope.group.id = info.id;
$scope.group.name = info.name;
$scope.group.description = info.description;
console.log($scope.group);
$rootScope.$broadcast(rCubeTopics.userMgtPermissionLoadData, $scope.group.id);
}
}).catch(function (exception) {
console.error(exception);
});
}
and in the permission directive get that broadcast :
$scope.$on(rCubeTopics.userMgtPermissionLoadData, function (event, id) {
console.log($scope.group.id);
getData();
});

$scope.watch does not work between two different angular pages

the below code is working within one page with different controlloers, but scope.watch does not work when I pass value from one page to another. How can you do that? below is my code.
.factory('Data', function () {
var data = {
LastName: '',
}
return {
getLastName: function () {
return data.LastName;
},
setLastName: function (lastname) {
data.LastName = lastname;
}
}
}
//FIRST CONTROLLER
$scope.lastname = '';
$scope.$watch('lastname', function (newValue2, oldValue2) {
if (newValue2 !== oldValue2)
Data.setLastName(newValue2);
});
//GET FIRST CONTROLLER INTO SECOND
$scope.$watch(function () {
return Data.getLastName();
}, function (newValue2, oldValue2) {
if (newValue2 !== oldValue2)
$scope.lastname = newValue2;
});
//form
//Firstcontroller
< input type = "text"
name="lastname"
placeholder = "Suhr"
ng-model="lastname"
ng-minlength="3" required />
Porting all your code and it works!
angular.module("test", [])
.controller('Test1', Test1)
.controller('Test2', Test2)
.factory('Data', Data);
function Test1($scope, Data) {
$scope.lastname = '';
$scope.$watch('lastname', function(newValue2, oldValue2) {
if (newValue2 !== oldValue2)
Data.setLastName(newValue2);
});
}
function Test2($scope, Data) {
$scope.$watch(function() {
return Data.getLastName();
}, function(newValue2, oldValue2) {
if (newValue2 !== oldValue2)
$scope.lastname = newValue2;
});
}
function Data() {
var data = {
LastName: '',
}
return {
getLastName: function() {
return data.LastName;
},
setLastName: function(lastname) {
data.LastName = lastname;
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="Test1">
<input type="text" name="lastname" ng-model="lastname" placeholder="Suhr" ng-minlength="3" required>
</div>
<div ng-controller="Test2">
{{lastname}}
</div>
</div>

Get ng-model in ng-repeat in ng-repeat with Protractor

How can I get the ng-model in ng-repeat in ng-repeat with protractor ?
<div ng-repeat="field in master.linker | orderBy:'country.name'">
<div>
<p> {{ field.country_name }} </p>
<label ng-repeat="user in user_list">
<input type="checkbox" ng-model="selected_user">
<span ng-bind="user.name"></span>
</label>
</div>
</div>
I use filter() to check my ng-repeat :
var fields = element.all(by.repeater('field in master.linker'));
fields.filter(function (field) {
return field.element(by.binding("field.country_name")).getText().then(function (country) {
return country === "en";
});
}).then(function (filteredFields) {
var fields2 = filteredFields[0].element.all(by.repeater('user in user_list'));
return fields2.filter(function (field2) {
return field2.element(by.binding('user.name')).getText().then(function (value) {
return value === user;
});
}).then(function (filteredFields) {
var myuser = filteredFields[0].element(by.model('user_name'));
self.current_step.expect(input.getAttribute('value')).to.eventually.equal('');
});
});;
I have this error in my console :
TypeError: filteredFields[0].element.all is not a function
Use .all() instead of .element.all():
filteredFields[0].all(by.repeater('user in user_list'));
You can also simplify things using first():
var fields = element.all(by.repeater('field in master.linker'));
var filteredUser = fields.filter(function (field) {
return field.element(by.binding("field.country_name")).getText().then(function (country) {
return country === "en";
});
}).first().all(by.repeater('user in user_list')).filter(function (userField) {
return userField.element(by.binding('user.name')).getText().then(function (value) {
return value === user;
});
}).first();
var myuser = filteredUser.element(by.model('user_name'));
self.current_step.expect(myuser.getAttribute('value')).to.eventually.equal('');
You may also look into the column() and row() repeater API.

Returning hidden object with Angular ng-repeat filter

I am trying to show the first value of a group and hide all other similar values within an ng-repeat. I am getting the if/else to work but it's literally returning "false" as a string. Here is what I have so far.
app.filter('dateSort', function() {
var prevVal = null;
return function(input) {
if (prevVal !== input.uniquedate) {
prevVal = input.uniquedate;
return moment(input.jsdatetime).format('dddd,\n MMMM Do');
} else {
return false;
}
};
});
I've also tried this as a directive, but with no luck.
app.directive('hideon', function() {
var prevVal = $index[-1].uniquedate;
return function(scope, element, attrs) {
scope.$watch(attrs.hideon, function(value, oldValue) {
if(element.uniquedate !== prevVal) {
element.show();
} else {
element.hide();
}
}, true);
}
});
Any help is appreciated. I am assuming that the best way to do this is with a directive. For the filter itself with the returned objects (including the false), the values don't hold after I sort any of the data. That's when I started trying to use it with $watch.
Here's a Codepen with what I've started- http://codepen.io/drewbietron/pen/dKjhe
Here is the fix to blank box display.
app.filter('uniqueDate', function() {
var prevVal = null, prevSeq = null;
return function(input, seq) {
input.show = false;
if (prevVal !== input.month || prevSeq == seq-1) {
prevVal = input.month;
input.show = true;
return input.day + " " + input.month;
}
};
});
Have made code changes to filter the events on selecting types.
var selectedTypes = ["Math", "Science", "Writing"];
$scope.updateSelectedTypes = function(type){
var found = _.some(selectedTypes, function(selType){
return selType == type;
});
if(!found){
selectedTypes.push(type);
}
else{
selectedTypes = _.difference(selectedTypes, type);
}
filterEvents();
};
var filterEvents = function(){
var result = _.filter(Events, function(event){
return _.some(selectedTypes, function(type){
return event.type == type;
});
});
result = _.each(result, function(item){
item.show = false;
});
$scope.events = angular.copy(result);
};
HTML:
<li><b>Filter By:</b></li>
<li class="filter" ng-click="updateSelectedTypes('Math')" ng-class="{ active: Math }">Math</li>
<li class="filter" ng-click="updateSelectedTypes('Science')" ng-class="{ active: Science }">Science</li>
<li class="filter" ng-click="updateSelectedTypes('Writing')" ng-class="{ active: Writing}">Writing</li>
<input type="search" ng-model="instructor.instructor" placeholder="Search By Instructor..." />
Here is the updated codepen link

how to move filter outside of controller in angular

I'm new in angular . i managed to filter using angular and it work fine but now i want to move filter outside of the controller which is quite challenging for me .
here is my html page :
<div >
<input type="checkbox" ng-click="itemType('CAR')"/> CAR
<input type="checkbox" ng-click="itemType('BIKE')"/> BIKE
<input type="checkbox" ng-click="itemType('CYCLE')"/> CYCLE
</div>
<table>
<tbody>
<tr ng-repeat="item in items | filter:filterItem">
<td >{{item.name}}</td>
<td >{{item.type}}</td>
</tr>
</tbody>
</table>
and controller :
app.controller('MainCtrl', function($scope) {
$scope.items = [
{name: 'bmw', type:'CAR' },
{name: 'ducati',type:'BIKE'},
{name: 'airbas',type:'CYCLE' }
];
$scope.typeArray = [];
$scope.itemType = function(type) {
var i = $.inArray(type, $scope.typeArray);
if (i > -1) {
$scope.typeArray.splice(i, 1);
} else {
$scope.typeArray.push(type);
}
}
$scope.filterItem = function(item) {
if ($scope.typeArray.length > 0) {
if ($.inArray(item.type, $scope.typeArray) < 0){
return false;
}
}
return item;
}
});
how can i move filtering from controller to app.filter().
Thanks.
Yes, the custom filter is a separate module and you can write it as:
iApp.filter('myfilter', function() {
return function( items, types) {
var filtered = [];
var looping = function(name){
angular.forEach(items, function(item) {
if(item.type === name){
filtered.push(item);
}
});
}
if(types.car == true){
looping('CAR');
}
if(types.bike == true){
looping('BIKE');
}
if(types.cycle == true){
looping('CYCLE');
}
return filtered;
};
});
Controller:
$scope.types = {car: false, bike:false, cycle: false};
$scope.items = [
{name: 'bmw', type:'CAR' },
{name: 'ducati',type:'BIKE'},
{name: 'airbas',type:'CYCLE' }
];
Demo 1 Plunker
[EDIT]
If you want to show all cells when no checkbox is selected, add this to filter:
var flag = true;
angular.forEach(types, function(type){
flag = flag & !type; // if one of flags will be false, we get flag=false
});
if(flag == true){
return items;
}
Demo 2 Plunker
FYI: You can see that filters do not use $scope. If you want to pass additional argument the syntax should be:
<tr ng-repeat="item in items | filter:myfilter:types">
where types is some object

Resources