I have a AngularJS directive that allows users to select a values from a list to filter on. Pretty simple concept which is represented here:
Problem is when I click one of the checkboxes they all select unintended. My directive is pretty simple so I'm not sure why this is happening. The code around the selection and checkboxes is as follows:
$scope.tempFilter = {
id: ObjectId(),
fieldId: $scope.available[0].id,
filterType: 'contains'
};
$scope.toggleCheck = function (id) {
var values = $scope.tempFilter.value;
if (!values || !values.length) {
values = $scope.tempFilter.value = [];
}
var idx = values.indexOf(id);
if (idx === -1) {
values.push(id);
} else {
values.splice(idx, 1);
}
};
$scope.valuesListValues = function (id) {
return $scope.available.find(function (f) {
return f.id === id;
}).values;
};
and the data resembles:
$scope.available = [{
id: 23,
name: 'Store'
values: [
{ id: 124, name: "Kansas" },
{ id: 122, name: "Florida" }, ... ]
}, ... ]
the view logic is as follows:
<ul class="list-box">
<li ng-repeat="val in valuesListValues(tempFilter.fieldId)">
<div class="checkbox">
<label ng-click="toggleCheck(val.id)">
<input ng-checked="tempFilter.value.indexOf(val.id) === -1"
type="checkbox"> {{val.name}}
</label>
</div>
</li>
</ul>
First off, it toggleCheck fires twice but populates the correct data ( second time given my code it removes it though ).
After the second fire, it checks all boxes... Any ideas?
Perhaps its that the local variable doesn't get reassigned to the property of the scope property used in the view. Since your values are then non-existent and not found, the box is checked.
$scope.tempFilter.value = values
I took the interface concept you were after and created a simpler solution. It uses a checked property, found in each item of available[0].values, as the checkbox model. At the top of the list is a button that clears the selected items.
JavaScript:
function DataMock($scope) {
$scope.available = [{
id: 23,
name: 'Store',
values: [{
id: 124,
name: "Kansas"
}, {
id: 122,
name: "Florida"
}]
}];
$scope.clearSelection = function() {
var values = $scope.available[0].values;
for (var i = 0; i < values.length; i++) {
values[i].checked = false;
}
};
}
HTML:
<body ng-controller="DataMock">
<ul class="list-box">
<li>
<button ng-click="clearSelection()">Clear Selection</button>
</li>
<li ng-repeat="val in available[0].values">
<div class="checkbox">
<label>
<input ng-model="val.checked"
type="checkbox" /> {{val.name}}
</label>
</div>
</li>
</ul>
</body>
Demo on Plunker
The repeat that I used to grab the values based on the id, was the problem area.
<li ng-repeat="val in valuesListValues(tempFilter.fieldId)">
removing that and simple listening and setting a static variable resolved the problem.
$scope.$watch('tempFilter.fieldId', function () {
var fId = $scope.tempFilter.fieldId;
if ($scope.isFieldType(fId, 'valuesList')) {
$scope.valuesListValues = $scope.valuesListValues(fId);
}
}, true);
});
and then in the view:
ng-repeat="value in valuesListValues"
Related
How can I filter an ng-repeat to show all items where a certain columnfield is an empty string? When I try this by typing nothing in the field it always seem to give the full list (wich is expected). I only want to see the person with id 1. How can I adjust the filter that a certain character in the inputfield makes the ng repeat filter on empty fields for the name column.
FiddleJs Example
My view:
<div class="panel-heading">
<h3>Filtered by {{filterValue}}</h3>
<input ng-change="filter()" ng-model="filterValue"/>
</div>
<div class="panel-body">
<ul class="list-unstyled">
<li ng-repeat="p in filteredPeople">
<h4>{{p.name}} ({{p.age}}) id: {{p.id}}</h4>
</li>
</ul>
</div>
Controller:
var people = [{
name: '',
age: 32,
id: 1
}, {
name: 'Jonny',
age: 34,
id: 2
}, {
name: 'Blake',
age: 28,
id: 3
}, {
name: 'David',
age: 35,
id: 4
}];
$scope.filter = function (value) {
$scope.filteredPeople = $filter('filter')(people, {
name: $scope.filterValue
});
}
$scope.people = people.slice(0);
Delete your $scope.filter() function in the controller and the ng-change="filter()" in your view. You should change var people array to $scope.people. You also need to delete the line $scope.people = people.slice(0);.
Create a filter function in your controller to only return people whose name property is empty if $scope.filterValue is empty:
$scope.emptyFilter = function(person) {
if ($scope.filterValue.length === 0) {
if (person.name.length === 0) {
return person;
}
} else {
return person;
}
};
Next, update your ng-repeat with the following:
<li ng-repeat="p in people | filter:emptyFilter | filter:{name: filterValue}">
I'm trying to apply a check box filter to a list, but the options for the check boxes should also be come the list of items only,
it works fine if i am iterating it for all the check boxes,
the problem is coming when i am trying to apply the unique filter to display check boxes options.
i have included
angular 1.4 and ui-utils
<script src="js/angular.min.js"></script>
<script src="js/ui-utils.min.js"></script>
my view and controller are defined as:
<div ng-controller="Test">
<div ng-repeat="person in persons | unique:type">
<!-- record that this person has been selected -->
<input type="checkbox" ng-checked="person.checked" ng-model="person.checked" /> {{ person.type }}
</div>
<ul>
<li ng-repeat="person in persons | selectedTypes">{{person.name}}</li>
</ul>
</div>
and script is
<script>
var app = angular.module("MyApp", ['ui.utils']);
app.controller("Test", function($scope) {
$scope.persons = [
{ type: 1, name: 'Ankit Balyan' },
{ type: 1, name: 'Abhilaksh' },
{ type: 2, name: 'Sanket Srivastav' },
{ type: 2, name: 'Sachin Sharma' },
{ type: 2, name: 'Rohan Rathor' },
{ type: 2, name: 'Jim' },
];
$scope.$watch('persons', function() {
console.log($scope.persons);
})
});
// Define our filter
app.filter('selectedTypes', function($filter) {
return function(persons) {
var i, len;
// get persons that have been checked
var checkedPersons = $filter('filter')(persons, {checked: true});
// Add in a check to see if any persons were selected. If none, return
// them all without filters
if(checkedPersons.length == 0) {
return persons;
}
//console.log(checkedPersons);
// get all the unique cities that come from these checked persons
var types = {};
for(i = 0, len = checkedPersons.length; i < len; ++i) {
// if this checked persons cities isn't already in the cities object
// add it
if(!types.hasOwnProperty(checkedPersons[i].type)) {
types[checkedPersons[i].type] = true;
}
}
// Now that we have the cities that come from the checked persons, we can
//get all persons from those cities and return them
var ret = [];
for(i = 0, len = persons.length; i < len; ++i) {
// If this person's city exists in the cities object, add it to the
// return array
if(types[persons[i].type]) {
ret.push(persons[i]);
}
}
//console.log(persons.length);
// we have our result!
return ret;
};
});
</script>
You have to put the name of the property as a string :
<div ng-repeat="person in persons | unique: 'type'">
instead of
<div ng-repeat="person in persons | unique: type">
Edit: If you don't provide quotes, you are applying a unique filter by the value of the variable type, which is not defined in your case, therefore the filter has no effect.
I would like to use a check list and show the user the boxes she has checked.
I am using this framework: http://vitalets.github.io/angular-xeditable/#checklist . See his example 'Checklist' versus his example 'Select multiple'. However, I do not want to display a link with a comma separated string, i.e., join(', '). I would like each selection to appear beneath the previous, in an ordered list or similar.
Pretty much copied from his examples, here are the guts of my controller:
$scope.userFeeds = {
feeds: {}
};
$scope.feedSource = [
{ id: 1, value: 'All MD' },
{ id: 2, value: 'All DE' },
{ id: 3, value: 'All DC' }
];
$scope.updateFeed = function (feedSource, option) {
$scope.userFeeds.feeds = [];
angular.forEach(option, function (v) {
var feedObj = $filter('filter')($scope.feedSource, { id: v });
$scope.userFeeds.feeds.push(feedObj[0]);
});
return $scope.userFeeds.feeds.length ? '' : 'Not set';
};
And here is my html:
<div ng-show="eventsForm.$visible"><h4>Select one or more feeds</h4>
<span editable-select="feedSource"
e-multiple
e-ng-options="feed.id as feed.value for feed in feedSource"
onbeforesave="updateFeed(feedSource, $data)">
</span>
</div>
<div ng-show="!eventsForm.$visible"><h4>Selected Source Feed(s)</h4>
<ul>
<li ng-repeat="feed in userFeeds.feeds">
{{ feed.value || 'empty' }}
</li>
<div ng-hide="userFeeds.feeds.length">No items found</div>
</ul>
</div>
My problem is - display works with editable-select and e-multiple, but not with editable-checklist. Swap it out and nothing is returned.
To workaround, I have tried dynamic html as in here With ng-bind-html-unsafe removed, how do I inject HTML? but I have considerable difficulties getting the page to react to a changed scope.
My goal is to allow a user to select from a checklist and then to display the checked items.
Try this fiddle: http://jsfiddle.net/mr0rotnv/15/
Your onbeforesave will need to return false, instead of empty string, to stop conflict with the model update from xEditable. (Example has onbeforesave and model binding working on the same variable)
return $scope.userFeeds.feeds.length ? false : 'Not set';
If you require to start in edit mode add the attribute shown="true" to the surrounding form element.
Code for completeness:
Controller:
$scope.userFeeds = {
feeds: []
};
$scope.feedSource = [
{ id: 1, value: 'All MD' },
{ id: 2, value: 'All DE' },
{ id: 3, value: 'All DC' }
];
$scope.updateFeed = function (feedSource, option) {
$scope.userFeeds.feeds = [];
angular.forEach(option, function (v) {
var feedObj = $filter('filter')($scope.feedSource, { id: v });
if (feedObj.length) { // stop nulls being added.
$scope.userFeeds.feeds.push(feedObj[0]);
}
});
return $scope.userFeeds.feeds.length ? false : 'Not set';
};
Html:
<div ng-show="editableForm.$visible">
<h4>Select one or more feeds</h4>
<span editable-checklist="feedSource"
e-ng-options="feed.id as feed.value for feed in feedSource"
onbeforesave="updateFeed(feedSource, $data)">
</span>
</div>
<div ng-show="!editableForm.$visible">
<h4>Selected Source Feed(s)</h4>
<ul>
<li ng-repeat="feed in userFeeds.feeds">{{ feed.value || 'empty' }}</li>
<div ng-hide="userFeeds.feeds.length">No items found</div>
</ul>
</div>
Css:
(Used to give the "edit view" a list appearance)
.editable-input label {display:block;}
Also there is the option of using a filter if you do not need to do any validation or start in edit mode.
Controller:
$scope.user = { status: [2, 3] };
$scope.statuses = [
{ value: 1, text: 'status1' },
{ value: 2, text: 'status2' },
{ value: 3, text: 'status3' }
];
$scope.filterStatus = function (obj) {
return $scope.user.status.indexOf(obj.value) > -1;
};
HTML:
<a href="#" editable-checklist="user.status" e-ng-options="s.value as s.text for s in statuses">
<ol>
<li ng-repeat="s in statuses | filter: filterStatus">{{ s.text }}</li>
</ol>
</a>
Is there any function or ng-something to check if any of the displayed Checkboxes are checked?
I have the values through the ng-click="function()" and pass the values through. I can go by foot and check my array if any value is inside.
I want to activate/deactivate the "next"-button if any Checkbox is
checked.
What's the easiest way?
If you don't want to use a watcher, you can do something like this:
<input type='checkbox' ng-init='checkStatus=false' ng-model='checkStatus' ng-click='doIfChecked(checkStatus)'>
You can do something like:
function ChckbxsCtrl($scope, $filter) {
$scope.chkbxs = [{
label: "Led Zeppelin",
val: false
}, {
label: "Electric Light Orchestra",
val: false
}, {
label: "Mark Almond",
val: false
}];
$scope.$watch("chkbxs", function(n, o) {
var trues = $filter("filter")(n, {
val: true
});
$scope.flag = trues.length;
}, true);
}
And a template:
<div ng-controller="ChckbxsCtrl">
<div ng-repeat="chk in chkbxs">
<input type="checkbox" ng-model="chk.val" />
<label>{{chk.label}}</label>
</div>
<div ng-show="flag">I'm ON when band choosed</div>
</div>
Working: http://jsfiddle.net/cherniv/JBwmA/
UPDATE: Or you can go little bit different way , without using $scope's $watch() method, like:
$scope.bandChoosed = function() {
var trues = $filter("filter")($scope.chkbxs, {
val: true
});
return trues.length;
}
And in a template do:
<div ng-show="bandChoosed()">I'm ON when band choosed</div>
Fiddle: http://jsfiddle.net/uzs4sgnp/
If you have only one checkbox, you can do this easily with just ng-model:
<input type="checkbox" ng-model="checked"/>
<button ng-disabled="!checked"> Next </button>
And initialize $scope.checked in your Controller (default=false). The official doc discourages the use of ng-init in that case.
Try to think in terms of a model and what happens to that model when a checkbox is checked.
Assuming that each checkbox is bound to a field on the model with ng-model then the property on the model is changed whenever a checkbox is clicked:
<input type='checkbox' ng-model='fooSelected' />
<input type='checkbox' ng-model='baaSelected' />
and in the controller:
$scope.fooSelected = false;
$scope.baaSelected = false;
The next button should only be available under certain circumstances so add the ng-disabled
directive to the button:
<button type='button' ng-disabled='nextButtonDisabled'></button>
Now the next button should only be available when either fooSelected is true or baaSelected is true and we need to watch any changes to these fields to make sure that the next button is made available or not:
$scope.$watch('[fooSelected,baaSelected]', function(){
$scope.nextButtonDisabled = !$scope.fooSelected && !scope.baaSelected;
}, true );
The above assumes that there are only a few checkboxes that affect the availability of the next button but it could be easily changed to work with a larger number of checkboxes and use $watchCollection to check for changes.
This is re-post for insert code also.
This example included:
- One object list
- Each object hast child list.
Ex:
var list1 = {
name: "Role A",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Write",
id: 2,
selected: false
}, {
sub: "Update",
id: 3,
selected: false
}],
};
I'll 3 list like above and i'll add to a one object list
newArr.push(list1);
newArr.push(list2);
newArr.push(list3);
Then i'll do how to show checkbox with multiple group:
$scope.toggleAll = function(item) {
var toogleStatus = !item.name_selected;
console.log(toogleStatus);
angular.forEach(item, function() {
angular.forEach(item.subs, function(sub) {
sub.selected = toogleStatus;
});
});
};
$scope.optionToggled = function(item, subs) {
item.name_selected = subs.every(function(itm) {
return itm.selected;
})
$scope.txtRet = item.name_selected;
}
HTML:
<li ng-repeat="item in itemDisplayed" class="ng-scope has-pretty-child">
<div>
<ul>
<input type="checkbox" class="checkall" ng-model="item.name_selected" ng-click="toggleAll(item)"><span>{{item.name}}</span>
<div>
<li ng-repeat="sub in item.subs" class="ng-scope has-pretty-child">
<input type="checkbox" kv-pretty-check="" ng-model="sub.selected" ng-change="optionToggled(item,item.subs)"><span>{{sub.sub}}</span>
</li>
</div>
</ul>
</div>
<span>{{txtRet}}</span>
</li>
Fiddle: example
I've a sample for multiple data with their subnode
3 list , each list has attribute and child attribute:
var list1 = {
name: "Role A",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Write",
id: 2,
selected: false
}, {
sub: "Update",
id: 3,
selected: false
}],
};
var list2 = {
name: "Role B",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Write",
id: 2,
selected: false
}],
};
var list3 = {
name: "Role B",
name_selected: false,
subs: [{
sub: "Read",
id: 1,
selected: false
}, {
sub: "Update",
id: 3,
selected: false
}],
};
Add these to Array :
newArr.push(list1);
newArr.push(list2);
newArr.push(list3);
$scope.itemDisplayed = newArr;
Show them in html:
<li ng-repeat="item in itemDisplayed" class="ng-scope has-pretty-child">
<div>
<ul>
<input type="checkbox" class="checkall" ng-model="item.name_selected" ng-click="toggleAll(item)" />
<span>{{item.name}}</span>
<div>
<li ng-repeat="sub in item.subs" class="ng-scope has-pretty-child">
<input type="checkbox" kv-pretty-check="" ng-model="sub.selected" ng-change="optionToggled(item,item.subs)"><span>{{sub.sub}}</span>
</li>
</div>
</ul>
</div>
</li>
And here is the solution to check them:
$scope.toggleAll = function(item) {
var toogleStatus = !item.name_selected;
console.log(toogleStatus);
angular.forEach(item, function() {
angular.forEach(item.subs, function(sub) {
sub.selected = toogleStatus;
});
});
};
$scope.optionToggled = function(item, subs) {
item.name_selected = subs.every(function(itm) {
return itm.selected;
})
}
jsfiddle demo
My Code - Plunker
I'm trying to changes status of all my list objects by using a master checkbox that
checks all objects and changes their properties by selecting the required status from the
select element.
The problem is that when I'm trying to apply change on all elements by using the "Check All" 'checkbox' it is not working.
e.g.
When I check manually all the checkboxes without using the master checkbox it is working.
My Code
var webApp = angular.module('webApp', []);
//controllers
webApp.controller ('VotesCtrl', function ($scope, Votes) {
$scope.votes = Votes;
$scope.statuses = ["Approved","Pending","Trash","Spam"];
$scope.expand = function(vote) {
console.log("show");
$scope.vote = vote;
$scope.ip = vote.ip;
$scope.date = vote.created;
};
$scope.change = function() {
for(var i = 0; i < $scope.votes.length; i++) {
if($scope.votes[i].cb) {
$scope.votes[i].status = $scope.votes.status;
$scope.votes[i].cb = false;
}
$scope.show = false;
}
};
});
//services
webApp.factory('Votes', [function() {
//temporary repository till integration with DB this will be translated into restful get query
var votes = [
{
id: '1',
created: 1381583344653,
updated: '222212',
ratingID: '3',
rate: 5,
ip: '198.168.0.0',
status: 'Pending',
},
{
id: '111',
created: 1381583344653,
updated: '222212',
ratingID: '4',
rate: 5,
ip: '198.168.0.1',
status: 'Spam'
},
{
id: '2',
created: 1382387322693,
updated: '222212',
ratingID: '3',
rate: 1,
ip: '198.168.0.2',
status: 'Approved'
},
{
id: '4',
created: 1382387322693,
updated: '222212',
ratingID: '3',
rate: 1,
ip: '198.168.0.3',
status: 'Spam'
}
];
return votes;
}]);
My HTML
<body ng-controller='VotesCtrl'>
<div>
<ul>
<li class="check" ng-click=>
<input type="checkbox" ng-model="master"></input>
</li>
<li class="created">
<a>CREATED</a>
</li>
<li class="ip">
<b>IP ADDRESS</b>
</li>
<li class="status">
<b>STATUS</b>
</li>
</ul>
<ul ng-repeat="vote in votes">
<li class="check">
<input type="checkbox" ng-model="vote.cb" ng-checked="master"></input>
</li>
<li class="created">
{{vote.created|date}}
</li>
<li class="ip">
{{vote.ip}}
</li>
<li class="status">
{{vote.status}}
</li>
</ul>
</div>
<br></br>
<div class="details">
<h3>Details:</h3>
<div>DATE: {{date|date}}</div>
<div>IP: {{ip}}</div>
<div>STATUS:
<select ng-change="change()" ng-init="votes.status='Approved'"
ng-model="votes.status"
ng-options="status for status in statuses">
</select>
<p>{{vote.status|json}}</p>
</div>
</div>
</body>
Why is master checkbox not working?
I changed your method change a bit to make it work.
From Plunker you can see that on master change all children still have old value. So I added onMasterChange method
HTML
<input type="checkbox"
ng-model="master"
ng-change="onMasterChange(master)"></input>
I created as default: $scope.master = false;
....
$scope.master = false;
$scope.onMasterChange = function(master){
for(var i = 0; i < $scope.votes.length; i++) {
$scope.votes[i].cb = master;
}
};
$scope.change = function(value) {
for(var i = 0; i < $scope.votes.length; i++) {
//if($scope.votes[i].cb == undefined){
// $scope.votes[i].cb = false;
// }
if($scope.master == true){
$scope.votes[i].cb = $scope.master;
$scope.votes[i].status = value;
}
else if( $scope.votes[i].cb == true) {
$scope.votes[i].status = value;
}
}
};
See Plunker
Hope it will help,
It is working, but I believe ng-model is taking precedence over ng-checked. If you remove ng-model from the checkboxes, ng-checked is working as expected.
<input type="checkbox" ng-checked="master"></input>
http://plnkr.co/edit/q35JlhOVSGxmu6QW8e98?p=preview
It is important to note, however, that ng-checked does not update your model, it only changes the presentation of the checkbox. A way of tackling this would be to remove the master binding, and call a method with ng-click on your master checkbox which changes .cb on each box.
Edit: Working version using a watch on the master checkbox.
http://plnkr.co/edit/3NwGtp1FX8g9bfMrbWU5?p=preview