Problem setting indeterminate value of parent checkbox in group - javascript

I have a JSON file with hierarchical data:
[
{
"id": 0,
"label": "Gap Inc.",
"value": "Gap Inc",
"checked": false,
"children": [
{
"id": 0,
"label": "Banana Republic",
"value": "Banana Republic",
"checked": false
},
{
"id": 1,
"label": "Gap",
"value": "Gap ",
"checked": false
},
{
"id": 2,
"label": "Hill City",
"value": "Hill City",
"checked": false
},
{
"id": 3,
"label": "Old Navy",
"value": "Old Navy",
"checked": false
}
]
},
{
"id": 1,
"label": "Home Depot",
"value": "Home Depot",
"checked": false,
"children": []
},
{
"id": 2,
"label": "TJX",
"value": "TJX Companies",
"checked": false,
"children": [
{
"id": 0,
"label": "TJ Maxx",
"value": "TJ Maxx ",
"checked": false
},
{
"id": 1,
"label": "Marshalls",
"value": "Marshalls",
"checked": false
},
{
"id": 2,
"label": "Home Goods",
"value": "Home Goods",
"checked": false
}
]
}
]
I am using it to create a set of nested HTML lists with checkboxes:
<ul class="list-unstyled">
<li *ngFor="let parent of accountList; let q = index">
<div class="form-check">
<input id="parent-{{q}}"
class="form-check-input"
type="checkbox"
[(ngModel)]="parent.checked"
(click)="selectChildren(parent, $event)"
[indeterminate]="selectedChildren.length">
<label for="parent-item" class="form-check-label">
{{parent.label}}
</label>
</div>
<ul class="list-unstyled pl-4">
<li *ngFor="let child of parent.children; let i = index;">
<div class="form-check">
<input id="child-{{i}}"
class="form-check-input"
type="checkbox"
[(ngModel)]="child.checked"
(click)="selectChild(child, $event)">
<label for="child-{{i}}" class="form-check-label">
{{child.label}}
</label>
</div>
</li>
</ul>
</li>
</ul>
What's supposed to happen
If you click a parent checkbox, it checks all the children.
If you click only one child, its parent is set to indeterminate.
What DOES happen
If you click a parent checkbox, it checks all the children.
If you click only one child, ALL parents are set to indeterminate.
I assume I screwed up something somewhere. I think I have mapped the indeterminate attribute to the length of any selected children (here selectedChildren instead of only the children of the given parent. Not sure how to fix that...
Here is the typescript:
selectedChildren = [];
selectChildren = function(data, event) {
const parentChecked = !data.checked;
data.children.forEach((child, key2) => {
child.checked = parentChecked;
});
};
selectChild = (data, event) => {
if (this.selectedChildren.indexOf(data.id) === -1) {
this.selectedChildren.push(data.id);
data.checked = true;
} else {
this.selectedChildren.splice(this.selectedChildren.indexOf(data.id), 1);
data.checked = false;
}
};

Related

Check/Uncheck all checkbox in group Vue.js

I have an Array of checkbox which already divided in to groups and I need to check all child checkbox if parent is checked and uncheck if parent is uncheck and then update all their state in Array. This wayyy over my head since I'm realy new to Vue.
I setup a Codepen here, and I can't change Array's structure since it's a JSON response from server.
Js
let tree = [
{
"text": "AccountController",
"id": 1,
"state": {
"opened": false,
"selected": true,
"disabled": false
},
"children": [
{
"text": "Index",
"id": 2,
"state": {
"opened": false,
"selected": true,
"disabled": false
},
"children": null
},
{
"text": "Login",
"id": 3,
"state": {
"opened": false,
"selected": true,
"disabled": false
},
"children": null
},
...
]
},
{
"text": "BaseController",
"id": 19,
"state": {
"opened": false,
"selected": true,
"disabled": false
},
"children": [
{
"text": "GetErrorListFromModelState",
"id": 20,
"state": {
"opened": false,
"selected": true,
"disabled": false
},
"children": null
},
{
"text": "GetErrorFromModelState",
"id": 21,
"state": {
"opened": false,
"selected": true,
"disabled": false
},
"children": null
},
...
]
}
]
let app = new Vue({
el : '#clone',
data : {
items : tree,
},
methods : {
submitForm() {
console.log(tree);
}
}
});
Html
<div id="clone">
<button #click="submitForm">click</button>
<div class="dd">
<ol class="dd-list">
<li v-for="(item, index) in items"
v-bind:class="[item.state.opened ? 'dd-item open' : 'dd-item']">
<div class="dd-handle"
#click="item.state.opened = !item.state.opened">
<input type="checkbox"
:disabled="item.state.disabled"
:name="item.text"
:checked="item.state.selected"
#click="item.state.selected = !item.state.selected">
<label :for="item.text">{{item.text}}</label>
</div>
<ol v-if="item.children.length != 0" class="dd-list">
<li v-for="(children, index) in item.children"
:data-id="children.id" class="dd-item">
<div class="dd-handle">
<input type="checkbox"
:name="children.text"
:checked="children.state.selected"
:disabled="children.state.disabled"
#click="children.state.selected = !children.state.selected">
<label :for="children.text">{{children.text}}</label>
</div>
</li>
</ol>
</li>
</ol>
</div>
</div>
Can someone enlighten me please. Thank in advance!
In the template,
<input type="checkbox"
:disabled="item.state.disabled"
:name="item.text"
:checked="item.state.selected"
#click="item.state.selected = !item.state.selected"
#change="onChange(item, item.state.selected)">
And add the method,
methods : {
submitForm() {
console.log(tree);
},
onChange(item, state){
for(let child of item.children){
child.state.selected = state
}
}
}
Updated pen.

populate multiple dropdown select boxes from JSON data in angular

Firstly, after numerous searches this question does not answer my issue.
I am using AngularJS Dropdown Multiselect and wish to populate a number of dropdowns that are extracted from my JSON data. An example of this data is below:
{
"results": [
{
"title": "Difficulty",
"icon": "difficulty-icon",
"filters": [{
"name": "Easy",
"checked": false
}, {
"name": "Medium",
"checked": false
}, {
"name": "Hard",
"checked": false
}, {
"name": "Expert",
"checked": false
}]
},
{
"title": "Direction of Movement",
"icon": "direction-icon",
"filters": [{
"name": "Flexion",
"checked": false
}, {
"name": "Ulnar Deviation",
"checked": false
}, {
"name": "Radial Deviation",
"checked": false
}]
},
{
"title": "Exercise Aim",
"icon": "aim-icon",
"filters": [{
"name": "Power",
"checked": false
}, {
"name": "Strength",
"checked": false
}]
}, {
"title": "Muscle Group ",
"icon": "muscle-icon",
"filters": [{
"name": "Foot & Shin",
"checked": false
}]
},
{
"title": "Joint",
"icon": "joint-icon",
"filters": [{
"name": "Foot and Ankle",
"checked": false
}, {
"name": "Knee",
"checked": false
}]
}
]
}
When any of these items are selected I need to push the value to an array (the array is not specific to the dropdown it has come from therefore can share the same model).
I would like to run an ng-repeat that will create 5 dropdowns that are populated from the data above with the custom button text displaying the Title from my JSON data.
Is this possible? If not how can I run a function in my controller in order to seporate this data in order to expose each section of JSON data to the $scope
UPDATE
So far I have managed to get this: however unsure how I can get the Title property
<div ng-repeat="select in Filters">
<div ng-dropdown-multiselect="" options="Filters[$index].filters" selected-model="example1model" extra-settings="filterSettings"></div>
</div>
Here is a snippet with an example of MultiSelect dropdown in a ngRepeat loop:
var app = angular.module('myApp', ['angularjs-dropdown-multiselect']);
app.controller("appController", function($scope) {
$scope.filterSettings = {
displayProp: 'name',
idProp: 'name'
};
$scope.all_data = {
"results": [
{
"title": "Difficulty",
"icon": "difficulty-icon",
"filters": [{
"name": "Easy",
"checked": false
}, {
"name": "Medium",
"checked": false
}, {
"name": "Hard",
"checked": false
}, {
"name": "Expert",
"checked": false
}]
},
{
"title": "Direction of Movement",
"icon": "direction-icon",
"filters": [{
"name": "Flexion",
"checked": false
}, {
"name": "Ulnar Deviation",
"checked": false
}, {
"name": "Radial Deviation",
"checked": false
}]
},
{
"title": "Exercise Aim",
"icon": "aim-icon",
"filters": [{
"name": "Power",
"checked": false
}, {
"name": "Strength",
"checked": false
}]
}, {
"title": "Muscle Group ",
"icon": "muscle-icon",
"filters": [{
"name": "Foot & Shin",
"checked": false
}]
},
{
"title": "Joint",
"icon": "joint-icon",
"filters": [{
"name": "Foot and Ankle",
"checked": false
}, {
"name": "Knee",
"checked": false
}]
}
]
};
angular.forEach($scope.all_data.results, function(item, key) {
item.model = [];
});
});
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.14.3.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<script src="//rawgit.com/dotansimha/angularjs-dropdown-multiselect/master/src/angularjs-dropdown-multiselect.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="appController">
<div>
<div ng-repeat="mydata in all_data.results">
<div ng-dropdown-multiselect="" options="mydata.filters" selected-model="mydata.model" extra-settings="filterSettings"></div>
<pre>mydata.model = {{mydata.model | json}}
</pre>
</div>
</div>
</body>
</html>
The models which receive user input (selection) for each multiselect is added to data in the Angular.forEach loop.

Applying filter on parent if it has been applied on children AngularJS

I have a list with items and have applied filter on those items by value from text box. I want to get new filtered list with parent and its children if parent has at least one filtered item.If doesn't, that parent should not be displayed.
For an example: If I enter "3D" in the text box I don't want to get "Day parts" and "Week parts" listed below as they don't have children anymore after filtering.
<div ng-controller="Ctrl">
<input type="text" data-ng-model="inputValue.name"/>
<ul data-ng-repeat="condition in conditions">
<div data-ng-click="setHeadingStatus(condition)">
<div>
{{condition.name}}
</div>
<li ng-repeat="item in condition.items | filter:inputValue">
<div class="custom-typeahead-list-title">{{item.name}}</div>
<div class="custom-typeahead-list-desc">{{item.description}}</div>
</li>
</div>
</ul>
function Ctrl($scope , $filter){
$scope.countryCode = 1;
$scope.conditions = [
{
"id": 1,
"name": "Experiences",
"expanded": false,
"items": [
{
"id": 1,
"name": "3D"
},
{
"id": 2,
"name": "Imax"
},
{
"id": 3,
"name": "D-Box"
},
{
"id": 4,
"name": "THX"
}
]
},
{
"id": 2,
"name": "Day parts",
"expanded": false,
"items": [
{
"id": 1,
"name": "Early Bird"
},
{
"id": 2,
"name": "Matinee"
},
{
"id": 3,
"name": "Last Night"
}
]
},
{
"id": 3,
"name": "Week parts",
"expanded": false,
"items": [
{
"id": 1,
"name": "Monday"
},
{
"id": 2,
"name": "Wednesday"
},
{
"id": 3,
"name": "Weekend"
}
]
}
]
}
Here is the example of it
http://jsfiddle.net/KJ3Nx/48/
You can put an ng-show on the div that displays each block, and set the expression to the filtered length;
<div data-ng-show="(condition.items | filter:inputValue).length > 0" data-ng-click="setHeadingStatus(condition)">
Updated fiddle

Count items in array where expression is true

I´m an angular newbie
I have a directive with a template that looks something like this:
<div class="multiselect-styled">
<a href="javascript:void(0);" class="link" id="link-{{ model.ControlId }}">
<span class="options-selected" id="selected-options-{{ model.ControlId }}">TEXT</span>
<span class="select-arrows"></span>
</a>
</div>
My model contains an array called Options which contains n number of selectable options. Each option object has a boolean property; Selected. I would like to replace TEXT with a string saying n options selected. So, how do I get the number of selected options?
Of course, using a loop one would do something like this but is there some way I kind bind directly to n in this case (Find n with an expression)?
var n = 0;
angular.forEach(model.Options, function(option) {
if (option.Selected)
n++;
});
UPDATE 1
Options look like this:
"Options": [
{
"Id": "ApprovedAndClosed",
"Value": "ApprovedAndClosed",
"Text": "ApprovedAndClosed",
"Selected": false,
"Code": "ApprovedAndClosed"
},
{
"Id": "Cancel",
"Value": "Cancel",
"Text": "Cancel",
"Selected": false,
"Code": "Cancel"
},
{
"Id": "Done",
"Value": "Done",
"Text": "Done",
"Selected": false,
"Code": "Done"
},
{
"Id": "Init",
"Value": "Init",
"Text": "Init",
"Selected": false,
"Code": "Init"
},
{
"Id": "ReadyForProcessing",
"Value": "ReadyForProcessing",
"Text": "ReadyForProcessing",
"Selected": false,
"Code": "ReadyForProcessing"
},
{
"Id": "Rejected",
"Value": "Rejected",
"Text": "Rejected",
"Selected": false,
"Code": "Rejected"
},
{
"Id": "Reminder",
"Value": "Reminder",
"Text": "Reminder",
"Selected": false,
"Code": "Reminder"
}
]
You can try using a filter:
{{(model.Options | filter:{'Selected': true}).length}}
You could use lodash for this:
var countSelected = _.filter(Options, { 'Selected': true});
console.log(countSelected.length);
See: codepen example

How to exclude a specific id from an array?

I have an array with sports names, with the option to check and uncheck them.
The items with checked = false are being saved in a DB. By default all the items are in checked = true, BUT, I want to exclude one of the items in the array and put it to checked = false instead.
at first, the array looks like this
[
{
"id": 26,
"name": "LIVE Betting",
"priority": 0
},
{
"id": 8,
"name": "NBA",
"priority": 1
},
{
"id": 24,
"name": "College Basketball",
"priority": 2
},
{
"id": 42,
"name": "Women College Basketball",
"priority": 3
},
{
"id": 9,
"name": "NHL",
"priority": 4
},
{
"id": 6,
"name": "MLB",
"priority": 5
}
]
and this is the function working over here
_this.getSportChecked(customer).then(function(sportChecked) {
var sportIds = _.pluck(sports, 'id'),
intersectedSports = _.intersection(sportIds, sportChecked.sport);
if (sports.length) {
//HERE I NEED TO EXCLUDE THE ID 9
sports = _.map(sports, function(sport) {
sport.checked = !_.includes(intersectedSports, sport.id);
return sport;
});
}
});
which returns this objects
{
"id": 26,
"name": "LIVE Betting",
"priority": 0,
"checked": true
}
{
"id": 8,
"name": "NBA",
"priority": 1,
"checked": true
}
{
"id": 24,
"name": "College Basketball",
"priority": 2,
"checked": true
}
{
"id": 42,
"name": "Women College Basketball",
"priority": 3,
"checked": true
}
{
"id": 9,
"name": "NHL",
"priority": 4,
"checked": true
}
{
"id": 6,
"name": "MLB",
"priority": 5,
"checked": true
}
so as you can see all those items are checked = true by default, but I want to set up the id = 9 to checked = false by default until the user switches it to true in case that he wants.
What are your suggestions here? read the note in my code please, there is the part where I need to exclude it.
Well, you can simply find the sport by id and uncheck it manually.
if (sports.length) {
//HERE I NEED TO EXCLUDE THE ID 9
sports = _.map(sports, function(sport) {
sport.checked = !_.includes(intersectedSports, sport.id);
// Special case for id 9
if (sport.id === 9) {
sport.checked = false;
}
return sport;
}

Categories

Resources