AngularJS: How can I select multiple checkboxes using shift and mouse click? - javascript

Is it possible to use shift and mouse click to select multiple elements on a table using AngularJS?
I have a table in which the first column is a checkbox and I would like to use SHIFT key and mouse click in order to select multiple rows continuously and can do things like delete, edit them etc.
Example by steps:
Click on 1st row's checkbox.
Hold down SHIFT key.
Click on 10th row's checkbox.
Result: the first 10 rows will be selected.
Does anyone know how this can be done using AngularJS?

I had a similar requirement. And while it's true that the proper way to update the checkboxes is by directly updating the model I wanted a more general solution.
So I built a pair of directives so I can reuse it on any checkbox list. Basically you wrap all the checkboxes with <multi-checkbox-container> and then add a multi-checkbox attribute to each checkbox. The code does the rest. Simple and easy.
angular
.module('app', [])
.controller('MainController', function($scope) {
var vm = this;
$scope.checks = {};
$scope.botigues = [1, 2, 3, 4, 5, 6];
})
.component('multiCheckboxContainer', {
controller: function () {
var ctrl = this;
var checkboxes = [];
var checkboxModels = [];
var previousClickedCheckbox = null;
ctrl.addCheckbox = addCheckbox;
ctrl.onCheckboxClick = onCheckboxClick;
function addCheckbox(checkbox, checkboxModelCtrl) {
checkboxes.push(checkbox);
checkboxModels.push(checkboxModelCtrl);
}
function onCheckboxClick(checkbox, shiftKey) {
var start, end, i, checking;
if (shiftKey && previousClickedCheckbox) {
checking = checkbox.prop('checked')
start = checkboxes.indexOf(previousClickedCheckbox);
end = checkboxes.indexOf(checkbox);
if (start > end) {
start = start + end;
end = start - end;
start = start - end;
}
for (i = start; i <= end; i++) {
checkboxes[i].prop('checked', checking);
checkboxModels[i].$setViewValue(checking);
}
}
previousClickedCheckbox = checkbox;
}
}
})
.directive('multiCheckbox', function () {
return {
restrict: 'A',
require: ['^^multiCheckboxContainer', 'ngModel'],
link: function (scope, element, attrs, controllers) {
var containerCtrl = controllers[0];
var ngModelCtrl = controllers[1];
containerCtrl.addCheckbox(element, ngModelCtrl);
element.on('click', function (ev) {
containerCtrl.onCheckboxClick(element, ev.shiftKey);
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController as vm">
<multi-checkbox-container>
<div ng-repeat="botiga in botigues">
<input type="checkbox" multi-checkbox ng-model="checks[botiga]">
<label>Botiga {{botiga}}</label>
</div>
</multi-checkbox-container>
<p>checks = {{ checks }}</p>
</div>

Not a complete solution, but this should work for you.
http://jsfiddle.net/AvGKj/705/
just keep track of each lastChecked checkbox, and on shift+click, mark all the checkboxes as checked.
<input type="checkbox" ng-checked = 'appObj.checked' ng-click="checked($index, $event)">
$scope.checked = function($index, $event){
if($scope.lastChecked && $event.shiftKey){
for(i=$scope.lastChecked; i<$index;i++){
$scope.myAppObjects[i].checked=true;
}
}
$scope.myAppObjects[$index].checked=true;
$scope.lastChecked = $index;
}
this code would only work if you ckeck from 0 - positive integer , but not reverse, you wuld require to do some modification to make it work completely.
hope this helps

Below presented the solution (Special thanks to Naeem Shaikh):
http://jsfiddle.net/dmakris/AvGKj/709/
HTML code:
<div ng-controller="MyCtrl">
<table class='table table-bordered'>
<tr ng-repeat="obj in myObjects">
<td>{{obj.id}}
<input type="checkbox" ng-checked = 'obj.checked' ng-click="checked($index, $event)">
</td>
<td>test {{obj.id}}</td>
- </tr>
</table>
</div>
Javascript (AngularJS) code:
function MyCtrl($scope) {
$scope.myObjects = [{id: 1, checked:false}, {id: 2, checked:false},
{id: 3, checked:false}, {id: 4, checked:false},
{id: 5, checked:false}, {id: 6, checked:false},
{id: 7, checked:false}];
$scope.checked = function($index, $event){
if(typeof $scope.lastChecked !='undefined' && $event.shiftKey){
for(i=$scope.lastChecked; i<=$index; i++){
$scope.myObjects[i].checked=true;
}
}
$scope.lastChecked = $index;
$scope.myObjects[$index].checked=true;
}
}

Related

Angular with multiple selects that must have unique options selected

I have an angular page that will have a dynamic number of select elements on it. Each select will have the same option collection but once an option is selected from one, that option should be removed from all of the subsequent select elements.
I found this: http://jsfiddle.net/Zv5NE/63/ which works exactly how I'd like (when an option is selected from one select, it's removed from the others and then if that same select is changed, it adds the previously selected option back to the others).
The problem is, this is using a hard coded number of select elements and also using hard coded filters for each select element...that won't work for my purposes because, as I said, my users are going to need to be able to dynamically add n number of select elements.
I've done some playing around trying to create my own filter to accommodate for this, but I'm super green to angular (angular 1 btw) and I've hit the wall.
this is a small snippet from what I've tried. Essentially I've just tried creating an array and adding selected items to that array then checking against the values in the array for the filter (I would have to add some logic for changing options obviously, but I'm really not sure this is the right direction to go):
$scope.filter = function (item) {
for (i = 0; i < $scope.names.length; i++) {
if (item == $scope.names[i]) {
return false;
}
}
return true;
};
any guidance would be greatly appreciated.
I shelved this for a while but came back to it this morning. I was able to come up with a working solution.
Here's what I wrote up. May not be the most elegant way to do it, but it works for my purposes:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta name="viewport" content="width=device-width" />
<title>AngularTest</title>
</head>
<body ng-controller="HellowWorldCtrl">
<select ng-model="selectname0" ng-options="item as item.name for item in classes | customFilter:'selectname0':this">
<option value="">- select -</option>
</select>
<div id="selectsDiv"></div>
<br />
<input type="button" value="Add Select" ng-click="addSelect()" ng-show="cnt < classes.length -1" />
<script src="~/Scripts/angular.js"></script>
<script type="text/javascript">
var app = angular.module('app', []).controller('HellowWorldCtrl', function ($scope, $compile) {
$scope.cnt = 0;
$scope.selectsAdded = [];
$scope.selectsAdded.push('selectname0');
$scope.addSelect = function () {
$scope.cnt++;
$scope.selectsAdded.push('selectname' + $scope.cnt);
var newSelect = $compile('<div><select ng-model="selectname' + $scope.cnt + '" ng-options="item as item.name for item in classes | customFilter:\'selectname' + $scope.cnt + '\':this"><option value="">- select -</option></select></div>')($scope);
angular.element(document.getElementById('selectsDiv')).append(newSelect);
};
$scope.classes = [
{
id: 1,
name: 'Biology 101',
courseid: '12345'
},
{
id: 2,
name: 'Chemistry 101',
courseid: '12374'
},
{
id: 3,
name: 'Psychology 101',
courseid: '32165'
},
{
id: 4,
name: 'Geology 101',
courseid: '78945'
},
{
id: 5,
name: 'Math 101',
courseid: '65478'
}
];
});
app.filter('customFilter', function () {
return function (items, which, scope) {
var alreadySelectedCourses = [];
var courses = [];
for (i = 0; i < items.length; i++) { // loop over all of the items in the class array...cwc
for (j = 0; j < scope.selectsAdded.length; j++) { // loop over all of the selects added to the page...cwc
if (which == scope.selectsAdded[j]) { // check if the calling select is the same one in the loop...cwc
if (scope['selectname' + j] && scope['selectname' + j].id) { // check if the calling select has alraedy been selected...cwc
if (scope['selectname' + j].id == items[i].id) { // check if the selected value of the calling select is the same as the item in the iteration and add it to the return array if so...cwc
courses.push(items[i]);
alreadySelectedCourses.push(items[i]);
}
}
} else { // not the calling select so find out the value and don't add it to the return array...cwc
if ((scope['selectname' + j] && scope['selectname' + j].id)) { // other selects (not calling select) have values selected so add them to the alreadyselectedarray...cwc
if (scope['selectname' + j].id == items[i].id) {
alreadySelectedCourses.push(items[i]);
}
}
}
}
if (alreadySelectedCourses.indexOf(items[i]) > -1) {
continue;
} else {
courses.push(items[i]);
}
}
return courses;
}
});
</script>
</body>
</html>

How to get second value variable in an array from dropdown selection in controller?

I'm looking to get the variable of the second value in an array list that a user selects via dropdown in my controller to do some math functions.
$scope.dropdown = [
{name:'Name', value:'123'}]
So I'm looking to pull the 123 when a user selects "Name" from a dropdown. I'm aware of how I can do this on the front-end like:
<p> {{dropdown.value}} </p>
But I can't seem to figure out how to do it in the controller.
I've tried:
var variable = $scope.dropdown.value;
var variable = $scope.dropdown[0].value;
var variable = $scope.dropdown.value[0];
But none of it seems to work.
An example of what I'm working with right now is:
$scope.input1 = "";
var rate = 2;
$scope.dropdown = [
{name:'Name', value:'123'}
];
var activities = (Number($scope.input1) * $scope.dropdownSelection.value * rate) / 2;
console.log(activities);
So basically, I'm looking to perform math functions in the controller from user inputs and user dropdown selections along with defined variables and I can't seem to figure out how to get the users selected dropdown value in the array.
The angular js way would be ideally using $filter and your controller code could be something like below
app.controller('MainCtrl', ['$scope', '$filter', function($scope, $filter) {
//array
var items = [{ id: "5", country: "UAE" }, { id: "4", country: "India" }];
//search value
var id2Search = "4";
//filter the array
var foundItem = $filter('filter')(items, { id: id2Search }, true)[0];
//get the index
var index = items.indexOf(foundItem );
}]);
I assume you are looking to find the value that a user selected from the dropdown that is populated using a dropdown array at the controller.
Here is what something you could do.
var app = angular.module("sampleApp", []);
app.controller("sampleController", ["$scope",
function($scope) {
$scope.input1 = 1;
$scope.dropdownValues = [{
name: 'Name',
value: 123
}, {
name: 'Name-1',
value: 456
}, {
name: 'Name-2',
value: 789
}];
$scope.dropdownValue = 123;
var rate = 10;
$scope.$watch(function() {
return $scope.dropdownValue
}, function() {
$scope.activities = (Number($scope.input1) * $scope.dropdownValue * rate) / 2;
});
}
]);
div.value {
padding-top: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<div ng-app="sampleApp">
<div ng-controller="sampleController">
<select ng-model="dropdownValue">
<option ng-repeat="item in dropdownValues" ng-value="item.value">
{{item.name}}
</option>
</select>
<div class="value">
Selected Value : {{dropdownValue}}
</div>
<div>
Activities : {{activities}}
</div>
</div>
</div>

How to reset nested ng-repeat

I have a nested ng-repeat with a filter to group by.
I have created this
fiddle.
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl',['$scope', function($scope) {
$scope.data =[
{subject: 'English',module: 'Literature', score: 95},
{subject: 'Maths',module: 'Calculus', score: 90}
];
$scope.dropScore = function() {
if ($scope.data[0].score > 0) {
$scope.data[0].score -= 8;
}
}
$scope.origData = angular.copy($scope.data)
$scope.reset = function () {
$scope.data = angular.copy($scope.origData);
};
}])
.filter('groupBy', function() {
return _.memoize(function(items, field) {
return _.groupBy(items, field);
}
);
});
When you press the button hit score the score of English drops but clicking reset will reset the $scope.data value but not show the updated data on the screen.
Can someone help with this
Working Demo
Just use this reset method:
$scope.reset = function () {
$scope.data[0].score = angular.copy($scope.origData[0].score);
};
You need to call $scope.$apply() to refresh your scope :
$scope.dropScore = function() {
if ($scope.data[0].score > 0) {
$scope.data[0].score -= 8;
$scope.$apply();
}
}
http://jsfiddle.net/zewaeqpx/
When you do ng-repeat=(subject, value) in data | groupBy: 'subject', you are creating another array for ng-repeat to use. So either you would need to assign each parameter within or not use the filter in the ng-repeat.
Option 1:
If you want to keep how you are using the ng-repeat, you could to this:
$scope.reset = function () {
_.forEach($scope.data, function(subject, idx){
subject.score = $scope.origData[idx].score;
});
};
Option 2:
or you could simplify your ng-repeat so that it works like this where the template becomes:
<li ng-repeat="subject in data">
<span ng-bind="subject.subject"></span>
<ul>
<li ng-bind="subject.module + ' - ' + subject.score">
</li>
</ul>
</li>
You should use ng-bind because in some browsers, when the data attribute is not loaded, it will flash {{}} for a split second before it loads.

Issue with <select ng-options="..." />

I tried to achieve a simple feature with AngularJS as below. In item list, when user clicks an item and click the Remove button then the item will be removed.
html:
<div ng-app="app" ng-controller="MainController">
<div>
<select ng-options="item.name for item in items" ng-model="currentItem" size="5" style="width: 200px"></select>
</div>
<button ng-click="removeItem()">Remove</button>
</div>
and script is like below:
angular.module('app', [])
.controller('MainController', function($scope) {
$scope.items = [{
name: 'item1'
}, {
name: 'item2'
}, {
name: 'item3'
}];
$scope.currentItem = $scope.items[0];
$scope.removeItem = function() {
var index = $scope.items.indexOf($scope.currentItem);
$scope.items.splice(index, 1);
};
});
The problem is when I tried to remove an item (i.e. item2), the list always shows an empty item in the first position. When I click 'item1' or 'item3', the empty item disappears.
I know that this is caused by ng-model="currentItem" in html. The item that currentItem points to get removed, currentItem points to null. So I changed the function removeItem as below to solve this issue.
$scope.removeItem = function() {
var index = $scope.items.indexOf($scope.currentItem);
$scope.items.splice(index, 1);
/* PART 1 begin */
if ($scope.items.length === 0) {
$scope.currentItem = null;
} else {
if (index >= 0 && index <= $scope.items.length - 1) {
$scope.currentItem = $scope.items[index];
} else {
$scope.currentItem = $scope.items[$scope.items.length - 1];
}
}
/* PART 1 end */
};
I would like to know whether there is any simple way (like a directive) in AngularJS to do the action in PART 1 automatically.
There is simple way in which you can prevent that is just include
<option value="" ng-show="false"></option>
in select like as shown below
<select ng-options="item as item.name for item in items" ng-model="currentItem" size="5" style="width: 200px">
<option value="" ng-show="false"></option>
</select>
Working Demo
UPDATE 1
I have resolved the issue of not highlighting the last item, Take a look the working demo
$scope.removeItem = function () {
var index = $scope.items.indexOf($scope.currentItem);
$scope.items.splice(index, 1);
index === $scope.items.length ? $scope.currentItem = $scope.items[index - 1] : $scope.currentItem = $scope.items[index];
};
Working Demo

Programatically unchecking ng-checkbox in AngularJS on removal from array

I am using a number of checkboxes to push their values to an array to be used as arguments for the filtering of a dataset.
The requirement is to:
Show child filters on selection of the parent category.
If parent is unchecked all its children should be unchecked (false) automatically (otherwise they will be hidden and still true)
Display active filters
remove active filter on click (also uncheck their corresponding checkbox programatically).
Clear all filters and uncheck all checkboxes.
My current code is below, however see attached fiddle.
$scope.IsChecked = false;
$scope.ActiveFilters = [];
$scope.clearFilters = function() {
$scope.ActiveFilters = [];
};
$scope.ModifyFilter = function (IsChecked, Filter) {
console.log(IsChecked);
if (IsChecked) {
$scope.ActiveFilters.push(Filter);
}
else {
var indexz = $scope.ActiveFilters.indexOf(Filter);
$scope.ActiveFilters.splice(indexz, 1);
}
};
A fiddle with a semi-working demonstration is here
-- EDIT --
Further explanation: When a checkbox is checked it pushed its value to the array. if I remove this from the array by clicking on its name in the 'Active Filters' section at the bottom of the fiddle then it does not uncheck the checkbox. Neither does clicking on 'Clear Filters'.
The problem is in your html bindings. so please post this code here.
You using for "IsChecked" variable. This is local scope variable created for each iteration of loop. You not change it from your script code.
Updated html:
<body ng-app="DemoApp" ng-controller="DashboardCtrl">
<div ng-repeat="group in Filters">
<input type="checkbox" value="{{group.title}}" ng-model="CheckboxParent" />
{{group.title}}
<div ng-show="CheckboxParent" class="animate-show" ng-repeat="filter in group.filters">
<input type="checkbox" class="filterChild" value="{{filter.name}}" ng-model="filter.checked" ng-change="ModifyFilter(filter.checked,filter)"/>
{{filter.name}}
</div>
</div>
<div>
<h4>Active Filters</h4>
<p class="clear-filters" ng-click="clearFilters()">Clear Filters</p>
<ul ng-repeat="activeFilter in ActiveFilters">
<li ng-model="activeFilter.checked" ng-click="removeFilter(ModifyFilter(activeFilter.checked,activeFilter))">{{activeFilter.name}}</li>
</ul>
</div>
</body>
Updated script:
var app = angular.module("DemoApp", [])
app.controller("DashboardCtrl", function($scope) {
$scope.clearFilters = function() {
angular.forEach($scope.Filters[0].filters, function(filter) {
filter.checked = false;
});
$scope.ActiveFilters = [];
};
$scope.IsChecked = false;
$scope.ActiveFilters = [];
$scope.ModifyFilter = function (IsChecked, Filter) {
console.log(IsChecked);
if (IsChecked) {
$scope.ActiveFilters.push(Filter);
}
else {
var indexz = $scope.ActiveFilters.indexOf(Filter);
$scope.ActiveFilters.splice(indexz, 1);
}
};
$scope.Filters =
[
{
"title": "Equipment",
"filters": [
{ name: "Rope", checked: false },
{ name: "Cables", checked: false },
{ name: "Dowel", checked: false },
{ name: "Ball", checked: false }
]
}
];
});
Look into my solution on js fiddle: JsFiddle

Categories

Resources