Exclusive options in AngularJS - angularjs

I have a list of checkbox and one of checkbox has value ALL. Whenever ALL is checked then the others of checkbox will be disabled and unchecked (value is empty). How should I do this in AngularJS? A list of checkbox is generated by using a loop over a data list as following:
<div ng-repeat="item in moduleService.elements">
<input type="checkbox">{{item.value}}
</div>

You could do something like this:
HTML:
<div ng-repeat="item in moduleService.elements">
<input id="{{'checkbox' + $index}}" type="checkbox" ng-disabled="allChecked" ng-model="item.isChecked">
<label for="{{'checkbox' + $index}}">{{item.value}}</label>
</div>
<div>
<input id="allCheckbox" type="checkbox" ng-model="allChecked" ng-click="onAllClicked()">
<label for="allCheckbox">All</label>
</div>
Controller:
var myCtrl = function(moduleService, $scope) {
$scope.onAllClicked = function() {
if(!$scope.allChecked) {
for (var i = 0; i < moduleService.elements.length; i++) {
moduleService.elements[i].isChecked = false;
}
}
}
}
Here is a JSFiddle with similar code: http://jsfiddle.net/robianmcd/W3xeP/

You can use ng-checked, and it makes it sexy.
<input type="checkbox" id="masterCheckbox" ng-model="master">
<div ng-repeat="item in moduleService.elements">
<input type="checkbox" ng-checked="master">{{item.value}}
</div>
Nice and easy and clean. Check out the JSFiddle

you would also need ng-disabled with Zack's answer
<input type="checkbox" id="masterCheckbox" ng-model="master">
<div ng-repeat="item in moduleService.elements">
<input type="checkbox" ng-disabled="master" ng-checked="master">{{item.value}}
</div>

Related

Angular directive for horizontal Bootstrap form

I'm trying to build a directive for my Angular to help with the integration of form fields. I've implemented Scott Allens solution from his Angular playbook, and it works fine for a normal stacked form.
I need however to adapt it to a horizontal form instead. Here's my code:
Markup
<div form-group>
<label for="name">Name</label>
<input type="text" id="name" ng-model="vm.name">
</div>
formGroup directive
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
var label = element.querySelector("label");
label.classList.add("control-label");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
}
element.classList.add("form-group");
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
The output becomes:
<div form-group="" class="form-group">
<label for="name" class="control-label">Name</label>
<input type="text" id="name" ng-model="vm.name" class="form-control">
</div>
And that's fine for stacked form. Since I need a horizontal form, my output needs to look like this:
<div form-group="" class="form-group">
<label for="name" class="control-label col-sm-3">Name</label>
<div class="col-sm-9">
<input type="text" id="name" ng-model="vm.name" class="form-control">
</div>
</div>
I've tried many solutions and I can get it work with single elements like an input, textarea or a select. It becomes much more tricky when I have something like two radio buttons inside my markup like this:
<div form-group>
<label>Active</label>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
</label>
</div>
</div>
The desired output of the above mentioned code should be:
<div form-group class="form-group">
<label class="control-label col-sm-3">Active</label>
<div class="col-sm-9">
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
</label>
</div>
</div>
</div>
Please notice that the input(s) in the form-group is not fixed. It can be either a single input, textarea, select, a group of radio buttons or checkboxes. I'm lost for how I can make that happen. Any help is appreciated. Thanks!
UPDATE
I made some small changes to Mark Veenstra's code to make it (sort of) working:
function setupDom(element) {
element.classList.add("form-group");
var label = element.querySelector("label");
label.classList.add("control-label", "col-sm-3");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
angular.element(input).wrap(angular.element('<div class="col-sm-9"></div>'));
}
var div_radio = element.querySelector("div[class='radio']");
angular.element(div_radio).wrap(angular.element('<div class="col-sm-9"></div>'));
}
This does not work completely as intended with multiple radio inputs since it only wraps the <div> on the first radio input element.
The output from radio button example in my original post using Marks code is:
<div form-group="" class="form-group">
<label class="control-label col-sm-3">Active</label>
<div class="col-sm-9">
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active" value="true"> Yes
</label>
</div>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active" value="false"> No
</label>
</div>
</div>
SOLUTION
Check out the Plunker with the final result: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview
In the angular.element documentation you can find the method wrap() to be able to wrap HTML around a selected element. Or see this direct link.
So what you could do in your directive is change the setupDom() function to match your requirements per type of form element.
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
element.classList.add("form-group");
var label = element.querySelector("label");
label.classList.add("control-label col-sm-3");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
input.wrap(angular.element('<div class="col-sm-9"></div>'));
}
var div_radio = element.querySelectorAll("div[class='radio']");
div_radio.wrap(angular.element('<div class="col-sm-9"></div>'));
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
NOTE: This code is not tested, maybe there are some minor mistakes, but I guess you'll get the point now.
Mark's suggestion came close, but it didn't solve my problem completely. I ended up using the following code in my formGroup directive:
(function (module) {
"use strict";
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
element.classList.add("form-group");
var children = angular.element(element).children();
var labels = children.splice(0, 1);
// Set label classes
labels[0].classList.add("control-label", "col-sm-3");
// Wrap children in div
angular.element(children).wrapAll(angular.element("<div class='col-sm-9'></div>"));
// Handle inputs
var inputs = element.querySelectorAll("input, textarea, select");
for (var i = 0, len = inputs.length; i < len; i++) {
var input = inputs[i],
type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox") {
input.classList.add("form-control");
}
}
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
module.directive("formGroup", formGroup);
}(angular.module("app.core")));
Check out this Plunker to see it in action: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview

Mutual effect of model variables

I have a simple GUI I would like to implement via pure AngularJS - there are two (or more) groups of checkboxes like here: http://jsbin.com/cemitubo/2/edit
Here is the code from the link below:
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js"></script>
<div ng-app="myApp" ng-controller="MyController">
<div class="group">
<input type="checkbox" ng-model="tag.aaa"/>
<input type="checkbox" ng-model="tag.ccc"/>
</div>
<div class="group">
<input type="checkbox" ng-model="tag.zzz"/>
</div>
{{tag}}
</div>
JS:
angular.module('myApp', [])
.controller('MyController', function($scope){
$scope.tag = {aaa: true};
});
Once A checkbox of one of the groups is checked all the checkboxes of the other groups should be unchecked (and the change obviously should be reflected in the model).
I tried to $watch the tag model variable and setting false to the variables of the other groups in $watch callback. The problem is that it fires the $watch callback each time tag is changed by $watch callback.
What is the proper AngularJS solution?
Thanks in advance!
Use ng-change instead $watch:
Something like:
$scope.changed = function(item, type){
console.log(item);
if((type == 'aaa' || type == 'ccc') ){
$scope.tag.zzz = !item;
}
else if(type == 'zzz'){
$scope.tag.aaa = !item;
$scope.tag.ccc = !item;
}
}
HTML
<div class="group">
<input type="checkbox" ng-model="tag.aaa" ng-change="changed(tag.aaa,'aaa')"/>
<input type="checkbox" ng-model="tag.ccc" ng-change="changed(tag.ccc,'ccc')"/>
</div>
<div class="group">
<input type="checkbox" ng-model="tag.zzz" ng-change="changed(tag.zzz, 'zzz')"/>
</div>
Demo JSBIN

Advice on the correct use of ngModel

I' new to AngularJS and have a following ambiguity with usage of ngModel. I want to give to the user possibility to generate unlimited number of "name": "value" pairs. So I generating div with ng-repeat for every element from pair. Here is my html:
<div ng-app>
<div ng-controller="TestCtrl">
<input type="button" value="+" ng-click="addNewRow();"/>
<div ng-repeat="a in range(itemsNumber)"><input type="text" name="key"/> : <input type="text" name="value"/></div>
</div>
</div>
And the JavaScript:
function TestCtrl($scope) {
$scope.itemsNumber = 1;
$scope.range = function() {
return new Array($scope.itemsNumber);
};
$scope.addNewRow = function () {
$scope.itemsNumber++;
}
};
Here is working js fiddle:
http://jsfiddle.net/zono/RCW2k/
I want to have model for this generating items but not sure how to do it.
I would appreciate any ideas and tips.
Best regards.
Edit:
I have create other solution. It can be viewed in this fiddle
http://jsfiddle.net/zono/RCW2k/8/
But is this solution is good idea?
Here is a fiddle: http://jsfiddle.net/RCW2k/13/
You should just create an array on the scope and it's also your model:
controller:
function TestCtrl($scope) {
$scope.items = [{key:"hello",value:"world"}]
$scope.addNewRow = function () {
$scope.items.push({key:"",value:""});
}
};
html:
<div ng-controller="TestCtrl">
<input type="button" value="+" ng-click="addNewRow();"/>
<div ng-repeat="item in items">
<input type="text" name="key" ng-model="item.key"/> :
<input type="text" name="value" ng-model="item.value"/>
</div>
</div>

AngularJS: ng-model inside ng-repeat?

I'm trying to generate form inputs with ng-repeat.
Note: 'customFields' is an array of field names: ["Age", "Weight", "Ethnicity"].
<div class="control-group" ng-repeat="field in customFields">
<label class="control-label">{{field}}</label>
<div class="controls">
<input type="text" ng-model="person.customfields.{{field}}" />
</div>
</div>
What is the best/correct way to set 'ng-model'? I would like to send it to the server as person.customfields.'fieldname' where fieldname comes from 'field in customFields'.
<div ng-app ng-controller="Ctrl">
<div class="control-group" ng-repeat="field in customFields">
<label class="control-label">{{field}}</label>
<div class="controls">
<input type="text" ng-model="person.customfields[field]" />
</div>
</div>
<button ng-click="collectData()">Collect</button>
</div>
function Ctrl($scope) {
$scope.customFields = ["Age", "Weight", "Ethnicity"];
$scope.person = {
customfields: {
"Age": 0,
"Weight": 0,
"Ethnicity": 0
}
};
$scope.collectData = function () {
console.log($scope.person.customfields);
}
}
You can try it here.
Updated:
For the validation, the trick is to put <ng-form> inside the repeater. Please try.
It should be:
<input type="text" ng-model="person.customfields[field]" />
Any on who ends up here For ng-model inside ng-repeat
http://jsfiddle.net/sirhc/z9cGm/
the above link has good description on how to use it with examples
Try this
ng-model="person.customfields."{{field}}
Moved the double quotes

AngularJS - Trigger when radio button is selected

I searched and tried many ng-xxxx kind of options but couldn't find the one..
I just want to call some function in the controller when radio button is selected.
So it might be similar to following..(Of course, below code is not working)
<input type="radio" ng-model="value" value="one" ng-click="checkStuff()"/>
Is there any way to achieve what I want?
There are at least 2 different methods of invoking functions on radio button selection:
1) Using ng-change directive:
<input type="radio" ng-model="value" value="foo" ng-change='newValue(value)'>
and then, in a controller:
$scope.newValue = function(value) {
console.log(value);
}
Here is the jsFiddle: http://jsfiddle.net/ZPcSe/5/
2) Watching the model for changes. This doesn't require anything special on the input level:
<input type="radio" ng-model="value" value="foo">
but in a controller one would have:
$scope.$watch('value', function(value) {
console.log(value);
});
And the jsFiddle: http://jsfiddle.net/vDTRp/2/
Knowing more about your the use case would help to propose an adequate solution.
Should use ngChange instead of ngClick if trigger source is not from click.
Is the below what you want ? what exactly doesn't work in your case ?
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.value = "none" ;
$scope.isChecked = false;
$scope.checkStuff = function () {
$scope.isChecked = !$scope.isChecked;
}
}
<div ng-controller="MyCtrl">
<input type="radio" ng-model="value" value="one" ng-change="checkStuff()" />
<span> {{value}} isCheck:{{isChecked}} </span>
</div>
In newer versions of angular (I'm using 1.3) you can basically set the model and the value and the double binding do all the work this example works like a charm:
angular.module('radioExample', []).controller('ExampleController', ['$scope', function($scope) {
$scope.color = {
name: 'blue'
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<html>
<body ng-app="radioExample">
<form name="myForm" ng-controller="ExampleController">
<input type="radio" ng-model="color.name" value="red"> Red <br/>
<input type="radio" ng-model="color.name" value="green"> Green <br/>
<input type="radio" ng-model="color.name" value="blue"> Blue <br/>
<tt>color = {{color.name}}</tt><br/>
</form>
</body>
</html>
For dynamic values!
<div class="col-md-4" ng-repeat="(k, v) in tiposAcesso">
<label class="control-label">
<input type="radio" name="tipoAcesso" ng-model="userLogin.tipoAcesso" value="{{k}}" ng-change="changeTipoAcesso(k)" />
<span ng-bind="v"></span>
</label>
</div>
in controller
$scope.changeTipoAcesso = function(value) {
console.log(value);
};
Another approach is using Object.defineProperty to set valueas a getter setter property in the controller scope, then each change on the value property will trigger a function specified in the setter:
The HTML file:
<input type="radio" ng-model="value" value="one"/>
<input type="radio" ng-model="value" value="two"/>
<input type="radio" ng-model="value" value="three"/>
The javascript file:
var _value = null;
Object.defineProperty($scope, 'value', {
get: function () {
return _value;
},
set: function (value) {
_value = value;
someFunction();
}
});
see this plunker for the implementation
i prefer to use ng-value with ng-if,
[ng-value] will handle trigger changes
<input type="radio" name="isStudent" ng-model="isStudent" ng-value="true" />
//to show and hide input by removing it from the DOM, that's make me secure from malicious data
<input type="text" ng-if="isStudent" name="textForStudent" ng-model="job">
<form name="myForm" ng-submit="submitForm()">
<label data-ng-repeat="i in [1,2,3]"><input type="radio" name="test" ng-model="$parent.radioValue" value="{{i}}"/>{{i}}</label>
<div>currently selected: {{radioValue}}</div>
<button type="submit">Submit</button>
</form>

Resources