How to select parent checkbox based on child checkboxes in angular.js? - angularjs

I've been playing with Angular.js recently and decided to check all the checkboxes once parent checkbox is checked, which I've done using ng-model and ng-checked directives.
<div ng-app>
<div ng-controller="Ctrl">
<input type="checkbox" ng-model="parent"/> Select All<br/>
<input type="checkbox" ng-model="child_1" ng-checked="parent" ng-click="getAllSelected()"/> First<br/>
<input type="checkbox" ng-model="child_2" ng-checked="parent" ng-click="getAllSelected()"/> Second<br/>
<input type="checkbox" ng-model="child_3" ng-checked="parent" ng-click="getAllSelected()"/> Three<br/>
<input type="checkbox" ng-model="child_4" ng-checked="parent" ng-click="getAllSelected()"/> Four<br/>
<input type="checkbox" ng-model="child_5" ng-checked="parent" ng-click="getAllSelected()"/> Five<br/>
</div>
</div>
Now I'm trying select all the parent checkbox once all the child checkboxes are checked but facing some issues.
function Ctrl($scope) {
$scope.getAllSelected = function () {
var chkChild = document.querySelectorAll('input[ng-model^="child_"]').length;
var chkChildChecked = document.querySelectorAll('input[ng-model^="child_"]:checked').length;
if (chkChild === chkChildChecked) $scope.parent= true;
else $scope.parent= false;
}
}
Demo: http://jsfiddle.net/codef0rmer/QekpX/
Can we make the above code more robust?

The ng-checked attribute in the checkbox takes in an expression. So you can give an expression with and/or conditions, as mentioned below to make it checked based on an expression.
<input type="checkbox" ng-checked="child_1 && child_2 && child_3 && child_4 && child_5" ng-model="parent"/> Select All<br/>
You dont have to write a seprate function to do the computation when every child checkbox is clicked.
Here is example in jsfiddle

Visually it updates all the checkboxes checked with ng-selected, but then when you go to actually submit the data the data doesn't match up with what was selected. I suggest doing a ng-click() which sucks, but if your data is dynamic.
http://codepen.io/stirlingw/pen/gpwXWM
//Random JSON object
$scope.labels =
[{
label: 'Death Row Records',
selected: false,
artists: [
{artist: 'MC Hammer', selected: false},
{artist: 'Andre "Dr. Dre" Young', selected: false},
{artist: 'Snoop Dogg', selected: false},
{artist: 'Tupac Shakur', selected: false},
{artist: 'Nate Dogg', selected: false},
]
}];
//JS
$scope.clicker = function(label){
label.artists.forEach(function(e){
e.selected = !label.selected;
});
};
//html
<ul>
<li ng-repeat="label in labels"> <input type="checkbox" ng-model="label.selected" ng-click="clicker(label)"> {{label.label}}
<ul>
<li ng-repeat="artist in label.artists">
<input type="checkbox" ng-model="artist.selected" > {{artist.artist}}
</li>
</ul>
</li>
</ul>

Related

Binding values from ng-repeat in Angularjs

I'm loading data from a database and save it to an array as follows:
$scope.allGroups = [{id: 1, groupName: group1}, {id: 2, groupName: group2}, ..]
In my view, I'm trying to select multiple group names as follows:
<div
class="drag-container avaliable-groups-connect groups-container schedule-container"
>
<div class="group" ng-repeat="m in allGroups">
<input type="checkbox" ng-model="m.selected" />
<span>{{ m.groupName }}</span>
</div>
</div>
I want to save selected items (m.selected) to an array one-by-one and bind that array to ng-model="schedule.selectedGroups"
How can I perform that? Thank you..
<div class="group" ng-repeat="m in allGroups" ng-init="m.selected = false">
<input type="checkbox" ng-model="m.selected" />
<span>{{ m.groupName }}</span>
</div>
Now your checkbox ng-model with modify the variable to true or false and in your js code
you can do like below.
$scope.schedule.selectedGroups = $scope.allGroups.filter(function (data) {
return data.selected === true;
});

Angular setting default radio selection in ng-repeat

I am presenting a simple yes/no answer question to my users and I want to have a default radio button selected. The problem is that I could have any number of these questions presented to the user.
This is my code:
<div ng-form ng-repeat="i in offers track by $index" name="messageForm[$index]">
<div data-ng-repeat="option in closeListingOptions" class="radio">
<label>
<input type="radio" name="close" ng-model="i.close" value="{{option.id}}" ng-checked="option.checked" />{{option.name}}</strong>
</label>
</div>
</div>
My options are set as follows:
$scope.closeListingOptions = [
{
id: "1",
name: "Yes please",
checked: true
},
{
id: "0",
name: "No thanks",
checked: false
}
];
The above example works and check/sets "yes" as the default answer. However unless I manually select an option via a mouse click the value is not binding to the model.
I have read that ng-select should not be used with ng-options but i am not sure how else I can achieve this goal? It seems that I need something like:
i.close = "1":
But how can I set this for an unknown quntity since I don't know how many question will be presented?
1- Instead of value={{something}} you can use ng-value directive.
2- Each input pack should have its specific name.
Here is a working example:
var app = angular.module("app", []);
app.controller("MainCtrl", function($scope) {
$scope.offers = [{
number: 1
},
{
number: 2
}
];
$scope.closeListingOptions = [{
id: "1",
name: "Yes please",
checked: true
},
{
id: "0",
name: "No thanks",
checked: false
}
];
});
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-form ng-repeat="i in offers" name="messageForm[$index]">
<div data-ng-repeat="option in closeListingOptions" class="radio">
<label>
<input type="radio" name="close-{{i.number}}" ng-model="i.close" ng-value="option.id" ng-checked="option.checked" />{{option.name}}</strong>
</label>
</div>
{{i}}
<hr>
</div>
</body>
</html>
You should try this:
<input type="radio" name="close"
ng-model="option.checked" value="{{option.id}}"
ng-checked="option.checked" />{{option.name}}</strong>
This ng-model will updated the checked key of your data array and then you can fetch all the radio button selection as per their ids from that single variable.
Hope this helps.
I was able t achieve my goal with the following:
<div data-ng-repeat="option in closeListingOptions" class="radio" ng-init="i.closeListing = 1">
<label>
<input type="radio" name="close-{{i.id}}" ng-model="i.closeListing" ng-value="{{option.id}}" />
<strong>{{option.name}}</strong>
</label>
</div>
The solution was to use ng-init

Angularjs checkbox group validation (dynamic options)

Let's say I have this question object:
$scope.question = {
id: 1,
question: 'q?',
required: true,
control: {
type: 'multiple_check',
options: [{
value: '1st option'
}, ... ]
}
}
And this form:
<form name="s.form" novalidate>
<h1>{{ question.question }}</h1>
<label ng-repeat="option in question.control.options">
<input type=" name="xxx"
ng-model="question.answer[$index]" ng-required="question.required" />
{{ option.value }}
</label>
</form>
And I'm stuck now with validation.
I've created a pen for this.
{{ s.form.$valid }} should give me true when form is valid, but it returns true only when all checkboxes in group are checked
{{ s.form['xxx'].$valid }} should give me true when at least one checkbox is checked, but it actually returns true only when the last checkbox is checked
I want to be able to choose at least one checkbox (one or more) from the group. When at least one is checked the form and group will be valid.
How can I achieve that? Already tried many things but can't make this to work.
Thanks.
You can make your own validation by watching checkbox as bellow
$scope.$watch( "question.control.options" , function(n,o){
var trues = $filter("filter")( n , {value:true} );
$scope.flag = trues.length;
}, true );
Notice: The value of a checkbox must be a boolean so change your question's value as
$scope.question = {
id: 1,
question: 'q?',
required: true,
control: {
type: 'multiple_check',
options: [{
value: false,
label: '1st option'
}, ... ]
}
}
and your view as
<form name="s.form" novalidate>
<h1>{{ question.question }}</h1>
<label ng-repeat="option in question.control.options">
<input type=" name="xxx"
ng-model="question.control.options.value" ng-required="question.required" />
{{ option.label }}
</label>
</form>
here is a fiddle make this approach
This was actually hard but I think it works now.
Here's working demo
For those who want to know I've included this module in my app.
Then changed the HTML code to:
<form name="s.form" novalidate>
form valid? <b>{{ s.form.$valid | json }}</b>
<div ng-repeat="question in questions">
<h1>{{ question.question }}</h1>
<label ng-repeat="option in question.control.options">
<input type="checkbox" name="xxx_{{ question.id }}"
checklist-model="question.answer" checklist-value="option" ng-required="question.required && !question.answer.length" />
{{ option.value }}
</label>
<p>question valid? {{ s.form['xxx_'+question.id].$valid | json }}</p>
answer: {{ question.answer }}
</div>
</form>

ng-model not working for angular radio buttons with JSON object as values

I have a simple filter where I want to select a particular value and type and make other calls using that. Here is a close representation of what I have in my view (I am not using ng-repeat to create these radio buttons).
<form>
<label>
<input type="radio" name="ptf" ng-value="cntrl.filter.today_filter" ng-model="cntrl.filter.filter_value"> Today
</label>
<label>
<input type="radio" name="ptf" ng-value="cntrl.filter.hour_filter" ng-model="cntrl.filter.filter_value"> Last
</label>
<select ng-model="cntrl.filter.hours" ng-options="num for num in cntrl.filter.time_range"></select>
<label> hours</label>
<label>
<input type="radio" name="ptf" ng-value="cntrl.filter.date_filter" datepicker="" ng-model="cntrl.filter.filter_value">Select a Date
</label>
<button class="btn btn-info float-right" ng-click = "cntrl.refreshData()" >Update</button>
</form>
This basically gives me 3 options where I can select either today, or last n hours or another date. Here is the relevant code from my cntrl.
vm.filter = {
today_filter: {
type: 'today',
value: 1
},
date_filter: {
type: 'date',
value: 2
},
hour_filter: {
type: 'hours',
value: 3
},
hours: 8,
today: getFormattedDate(vm.current_date),
time_range: [],
filter_value: {
type: 'today',
value: 1
}
};
Now I have filter_value (ng-model) set as an object with type today and value 1. So I am expecting that by default my radio button with value of cntrl.filter.today_filter should be selected. And as I select other radio buttons, the filter_value object should update accordingly. Of course this is not happening. Is there some basic thing that is wrong here? Any pointers on how to fix this or maybe a different approach on this?
There are a few things wrong with your code:
You use the wrong aliasses for your controller in your view (spDashboardCntrl and cntrl)
Use the actual same object as the initial value instead of a new object that just looks the same.
For clarity, see the working example below.
angular.module('app',[])
.controller('myController', function() {
var vm = this;
vm.filter = {
today_filter: {
type: 'today',
value: 1
},
date_filter: {
type: 'date',
value: 2
},
hour_filter: {
type: 'hours',
value: 3
},
hours: 8,
today: '', //getFormattedDate(vm.current_date),
time_range: []
};
vm.filter.filter_value = vm.filter.today_filter;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="myController as vm">
<form>
<label>
<input type="radio" name="ptf" ng-value="vm.filter.today_filter" ng-model="vm.filter.filter_value">Today
</label>
<label>
<input type="radio" name="ptf" ng-value="vm.filter.hour_filter" ng-model="vm.filter.filter_value">Last
</label>
<select ng-model="vm.filter.hours" ng-options="num for num in vm.filter.time_range"></select>
<label>hours</label>
<label>
<input type="radio" name="ptf" ng-value="vm.filter.date_filter" datepicker="" ng-model="vm.filter.filter_value">Select a Date
</label>
<button class="btn btn-info float-right" ng-click="vm.refreshData()">Update</button>
</form>
{{vm.filter.filter_value}}
</div>
</div>

How to render an array as a list of radio buttons?

I would like to loop through an array that I define in my Javascript and render a list of radio buttons. My code which isn't working currently, and it is as follows (also on jsfiddle):
<div data-bind="foreach: options" >
<div>
<input type="radio" name="optionsGroup" data-bind="checked: selected" />
<span data-bind="text: label"></span>
</div>
</div>
var optionsList = [
{"value": "a","label": "apple"},
{"value": "b","label": "banana"},
{"value": "c","label": "carrot"}
];
function viewModel() {
var self = this;
self.options = optionsList;
self.selected = ko.observable("a");
self.selected.subscribe(function(newValue) {
alert("new value is " + newValue);
});
}
ko.applyBindings(new viewModel());
If my array is part of the html then it works fine, see this (or jsfiddle):
<div>
<input type="radio" name="optionsGroup" value="a" data-bind="checked: selected" />Apple
</div>
<div>
<input type="radio" name="optionsGroup" value="b" data-bind="checked: selected" />Banana
</div>
<div>
<input type="radio" name="optionsGroup" value="c" data-bind="checked: selected" />Carrot
</div>
<div data-bind="text: selected">
</div>
function viewModel() {
var self = this;
self.selected = ko.observable("a");
self.selected.subscribe(function(newValue) {
alert("new value is " + newValue);
});
}
ko.applyBindings(new viewModel());
I got this to work by generating the all of the html within my javascript and have this working using checkboxes, but am stumped generating a group of radiobuttons using the foreach iterator.
Has anyone gotten an example like my first one to work?
Here's one way to do this. Note that the attr binding should come before the checked binding.
var optionsList = [
{"value": "a", "label": "apple"},
{"value": "b", "label": "banana"},
{"value": "c", "label": "carrot"}
];
function viewModel() {
this.options = optionsList;
this.selected = ko.observable("a");
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h3>Fruits</h3>
<div data-bind="foreach: options" >
<label>
<input type="radio"
name="optionsGroup"
data-bind="attr: {value: value}, checked: $root.selected" />
<span data-bind="text: label"></span>
</label>
</div>
<h3>Selected value:</h3>
<pre data-bind="text: ko.toJSON($root.selected)"></pre>
Your code is giving this error:
Message: ReferenceError: selected is not defined;
Bindings value: checked: selected
You defined selected on the view model level, but you are referencing it inside foreach so Knockout.js is looking for it on the options level.
Change:
<div><input type="radio" name="optionsGroup" data-bind="checked: selected" />
to:
<div><input type="radio" name="optionsGroup" data-bind="checked: $root.selected" />
$root.selected will look for the selected property on the view model level.
HERE is the modified code.
For more information about pseudo-variables (like $root) see 3. Access to parent binding contexts.
To be able to have the entire object it's better to use checkedValue instead of attr: {value} like that :
Fruits
<div data-bind="foreach: options" >
<div><input type="radio" name="optionsGroup" data-bind="checkedValue: $data, checked: $root.selected" />
<span data-bind="text: label"></span></div>
</div>
You code is works. Just include knockout.js in your jsfiddle and set document "onDomready".

Resources