How to create a "other" or "catch all" radio button? - angularjs

Suppose I want to present a few default options for the user but also want to allow them to enter their own value.
e.g.
Please select one of the following:
[ ] apple
[ ] pear
[ ] other: ___________
I want it so that if "other" is selected, then the input field that follows should be enabled and allow typing.
Html might look like this:
<input type="radio"
name="fruit"
ng-model="fruit"
value="apple"
> apple
<input type="radio"
name="fruit"
ng-model="fruit"
value="pear"
> pear
<input type="radio"
name="fruit"
ng-model="???"
value="???"
> Other:
<input type="text"
class="form-control"
ng-model="fruit"
ng-disabled="???">
What would you do here?
I had an implementation where the default options trigger an action on ng-change such that it changed a $scope.isOther to true, which would enable the input box and check the other radio box like so
<input type="radio"
name="fruit"
ng-model="fruit"
value="apple"
ng-change="isOther=true"
> apple
<input type="radio"
name="fruit"
ng-model="fruit"
value="pear"
ng-change="isOther=true"
> pear
<input type="radio"
name="fruit"
ng-model="isOther"
ng-change="fruit=null"
value="true"
> Other:
<input type="text"
class="form-control"
ng-model="fruit"
ng-disabled="!isOther">
That sort of works. But when I reload the page/data, if I have an "other" value entered, it doesn't know to automatically check "other", although my "other" value is in the input box. I could write some more code change the isOther value when I'm loading the data but I'm wondering if I'm even going about this the right way or whether there's a "catch all" that allows a radio box to be checked if it doesn't match any other values.

You can do it this way:
var myapp = angular.module('myApp', []);
myapp.controller('MyController', function($scope) {
$scope.fruits = ['apple', 'orange', 'plum', 'pear'];
$scope.fruit = {selectedOption: 'orange'};
$scope.isDisabled = function() {
if (_.contains($scope.fruits, $scope.fruit.selectedOption)) {
return true;
}
return false;
};
$scope.add = function() {
if($scope.x !== undefined && $scope.x !== '') {
$scope.fruits.push($scope.x);
$scope.fruit.selectedOption = $scope.x;
$scope.x = '';
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div id='main' ng-app='myApp'>
<form name="myForm" ng-controller="MyController">
<div ng-repeat="f in fruits track by $index">
<input type="radio" name="fruit" ng-model="fruit.selectedOption" value={{f}}> {{f}}
</div>
<input type="radio" name="fruit" ng-model="fruit.selectedOption" value="other"> Other:
<input type="text" ng-model="x" ng-disabled="isDisabled()" ng-blur="add()">
<br> fruit = {{fruit.selectedOption}}
</form>
</div>
JSFiddle

I don't have a perfect answer, but I ended up doing it this way
html:
<div id='main' ng-app='myApp'>
<form name="myForm" ng-controller="MyController">
<div ng-repeat="f in fruits track by $index">
<input type="radio" name="fruit" ng-model="fruit.value" ng-value="f" ng-change="disableOther()"> {{f}}
</div>
<input type="radio" name="otherfruit" ng-change="fruit.value=null" ng-model="isOther" ng-value="true"> Other:
<input type="text" ng-model="fruit.value" ng-change="checkOther()" ng-class="{'disabled-input':isStandardFruit()}">
<br> fruit = {{fruit.value}}
</form>
</div>
code:
var myapp = angular.module('myApp', []);
myapp.controller('MyController', function($scope) {
$scope.fruits = ['apple', 'orange', 'plum', 'pear'];
$scope.fruit = {value:'orange'};
$scope.isStandardFruit = function() {
for(var i=0; i<$scope.fruits.length; i++) {
if( $scope.fruits[i] == $scope.fruit.value ) {
return true;
}
}
return false;
}
$scope.disableOther = function() {
$scope.isOther = false;
}
$scope.checkOther = function() {
if( !$scope.isStandardFruit() ) {
$scope.isOther = true;
}
}
});
JSFiddle
The one drawback is that if you type a standard fruit into the "other" input box, then it selects multiple radio buttons. I could uncheck the "other" button but then you couldn't type a fruit that contained the standard fruit as a prefix (e.g. you wouldn't be able to type "apple pear")
If anyone can come up with a better answer, I will change my vote and answers

Related

angularjs checkbox Incorrect response in console

I have two criteria:
1) Only allow one of two boxes selected at one time.
2) Capture the name of the box that is selected.
However, when I print out the list of checkbox objects they are correct, but when I check in the console they are not correct. For example,
HTML:
<div ng-repeat="treatment in treatment_list">
<input type="checkbox" value="{{treatment.name}}"
ng-model="treatment.checked"
ng-click="updateTreatment($index, treatment_list);
checkedTreatment(treatment_list)">
<label>Treatment {{treatment.name.toUpperCase()}}</label></input><br>
</div>
{{treatment_list}}
Controller:
$scope.treatment_list = [
{
name: 'a',
checked: false
}, {
name: 'b',
checked: false
}
];
$scope.updateTreatment = function(position, treatment_list) {
console.log(treatment_list);
angular.forEach(treatment_list, function(treatment, index) {
console.log(treatment.name, treatment.checked);
if (position != index) {
treatment.checked = false;
}
});
};
$scope.$watch('treatment.checked', function (treatment) {
console.log(treatment);
});
Plunker:
https://plnkr.co/edit/Hkb4IeKxi0TRqHRJA4JN?p=preview
Inorder to fullfill your requirement you should just use a radio box whith ng-model, it will work out of the box for you.
Use radio buttons instead:
angular.module("app",[])
.controller('ExampleController', function($scope) {
$scope.color = {
name: 'blue'
};
$scope.specialValue = {
"id": "12345",
"value": "green"
};
$scope.colorChange = function(color) {
console.log(color);
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<form name="myForm" ng-controller="ExampleController">
<label>
<input type="radio" ng-model="color.name" value="red"
ng-change="colorChange(color.name)" />
Red
</label><br/>
<label>
<input type="radio" ng-model="color.name" ng-value="specialValue"
ng-change="colorChange(color.name)" />
Green
</label><br/>
<label>
<input type="radio" ng-model="color.name" value="blue"
ng-change="colorChange(color.name)" />
Blue
</label><br/>
<tt>color = {{color.name | json}}</tt><br/>
</form>
Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
</body>
For more infomation, see AngularJS input type=radio Directive API Reference

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

All checkboxes get checked when one is checked (should be just the one checked) - inputs generated with angular js

I am making app with angular js. It goes like this.
User creates groups and adds group names
User creates 'websites' and for each website he can check groups that are created in previous step
Problem is that all groups checkboxes get checked when he checks just one.
Here is the code that generates the checkboxes:
<p>Groups:
<label ng-repeat='group in groups'>
<input type="checkbox" ng-model="newSite.groups" name="group-check" value="{{group.id}}"/>
{{group.name}}</label></p>
Here is the code that is outputed:
<label ng-repeat="group in groups" class="ng-scope ng-binding">
<input type="checkbox" ng-model="newSite.groups" name="group-check" value="0" class="ng-valid ng-dirty">
first group</label>
<label ng-repeat="group in groups" class="ng-scope ng-binding">
<input type="checkbox" ng-model="newSite.groups" name="group-check" value="1" class="ng-pristine ng-valid">
second group</label>
Thanks!
edit: here is the plunker link http://beta.plnkr.co/edit/OVBoTDY2YmXgSy8TAbIW
This (plunker) is how I would do it. The idea is to create a model to keep track of checked groups.
JS
app.controller("WebsitesCtrl", function($scope) {
$scope.newSite = {};
$scope.newGroup = {};
$scope.checkedGroupIds = {};
$scope.sites = [];
var groupMap = {};
$scope.groups = [];
var siteIdSeq = 0;
function createSite(newSite, groups) {
$scope.sites.push(newSite);
newSite.id = siteIdSeq;
newSite.groups = groups;
siteIdSeq++;
return newSite;
}
var groupIdSeq = 0;
function createGroup(newGroup) {
$scope.groups.push(newGroup);
newGroup.id = groupIdSeq;
groupMap[newGroup.id] = newGroup;
groupIdSeq++;
return newGroup;
}
$scope.submitSite = function() {
var groups = [];
angular.forEach($scope.checkedGroupIds, function(checked, id) {
if(checked) {
groups.push(groupMap[id]);
}
});
createSite($scope.newSite, groups);
$scope.newSite = {};
$scope.checkedGroupIds = {};
};
$scope.submitGroup = function() {
createGroup($scope.newGroup);
$scope.newGroup = {};
};
//test data
$scope.newSite.url = 'http://www.my-site.com';
var all = createGroup({name:'All'});
var home = createGroup({name:'Home'});
var fav = createGroup({name:'Fav'});
createSite({url:'http://www.stackoverflow.com'}, [all, fav]);
createSite({url:'http://www.google.com'}, [fav]);
createSite({url:'http://www.plnkr.co'}, [home]);
});
HTML
<div id="website-form">
Sites:
<ul>
<li ng-repeat="site in sites">{{site}}</li>
</ul>
<form ng-submit="submitSite()">
<label>Site url: <input type="url" ng-model="newSite.url" /></label>
<p>Groups:
<label ng-repeat='group in groups'>
<input type="checkbox" name="group-check" value="{{group.name}}" id="{{group.id}}"
ng-model="checkedGroupIds[group.id]" />
{{group.name}}
</label>
</p>
<input type="submit" id="submitWebsite" value="Save Changes" ng-disabled="!newSite.url" />
</form><!-- end submitSite() -->
</div>
<div id="group-form">
<form ng-submit="submitGroup()">
<label>Name of the group: <input ng-model="newGroup.name" /></label>
<input type="submit" class="btn btn-primary" id="submitGroup" value="Save Changes"
ng-disabled="!newGroup.name"/>
</form><!-- end submitGroup() -->
</div>

How to get data from ngform in angularjs?

HTML:
<div ng-controller="TestController" >
<form name="test_form" ng-submit="submit()">
<input type="text" name="some_name" ng-model="form_data.some_name" required>
<ng-form ng-repeat="key in keys" name="keyForm">
<input type="text" name="data_input" ng-model="form_data.data_input" required>
</ng-form>
<a ng-click="addKey()">NEW KEY</a>
</form>
</div>
JS:
app.controller('TestController', function TestController($scope){
$scope.keys = [];
$scope.addKey = function() {
$scope.keys.push({});
}
$scope.submit = function() {
console.log($scope);
}
});
In submit function I can get the value of "some_name" input:
$scope.submit = function() {
console.log($scope.form_data.some_name);
}
But I can't get the values of "data_input" inputs (they are inside ngform tag). How to do that?
(ngform tag is using for ability to validate each new added input separately)
Each input inside the ng-repeat needs its own unique ng-model property -- they all can't use form_data.data_input. Here is one way to solve your problem:
<ng-form ng-repeat="key in keys" name="keyForm">
<input type="text" name="data_input" ng-model="key.data" required>
</ng-form>
$scope.addKey = function () {
$scope.keys.push({ data: ''});
}
Fiddle.
See also https://stackoverflow.com/a/14379763/215945

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