AngularJS: single select between multiple checkbox - javascript

I am trying to force a single-selection on checkboxes, similar to a html "select"
I have a html simple table:
<tr ng-repeat="subscription in entities">
<td>
<input type="checkbox" ng-checked="isChecked(subscription)" ng-click="toggleSelection(subscription)"/>
</td>
</tr>
Then I have some simple controller functions for those directives above:
$scope.isChecked = function(entity) {
return $scope.checkedEntity === entity;
};
$scope.toggleSelection = function(entity) {
entity.checked = !entity.checked;
if (entity.checked) {
$scope.checkedEntity = entity;
} else {
$scope.checkedEntity = null;
}
};
Unfortunately it doesn't work, and I think I just discovered why.... the ng-click has 0 priority, vs 100 for ng-checked.
Is there an elegant solution for this problem?

Bind ng-model to subscription.checked, and have ng-click uncheck all subscriptions except the one clicked. Since these are checkboxes, the one clicked will toggle itself.
<tr ng-repeat="subscription in entities">
<td>
<input ng-model="subscription.checked" ng-click="updateSelection($index, entities)" type="checkbox" />
</td>
</tr>
You can use a plain for loop, but angular's forEach allows us to alias each item as subscription and improve readability:
$scope.updateSelection = function(position, entities) {
angular.forEach(entities, function(subscription, index) {
if (position != index)
subscription.checked = false;
});
}
Here is a working demo: http://plnkr.co/edit/XD7r6PoTWpI4cBjdIZtv?p=preview

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script>
angular.module('app', []).controller('appc', ['$scope',
function($scope) {
$scope.selected = 'other';
}
]);
</script>
</head>
<body ng-app="app" ng-controller="appc">
<label>SELECTED: {{selected}}</label>
<div>
<input type="checkbox" ng-checked="selected=='male'" ng-true-value="'male'" ng-model="selected">Male
<br>
<input type="checkbox" ng-checked="selected=='female'" ng-true-value="'female'" ng-model="selected">Female
<br>
<input type="checkbox" ng-checked="selected=='other'" ng-true-value="'other'" ng-model="selected">Other
</div>
</body>
</html>

I have used the below code to achieve the similar functionality. Trick is to use ng-click which can yield this pretty nicely. checkbox-1 & checkbox-2 are boolean in nature.
<form>
<input type="checkbox" ng-click="checkbox-2 = false" ng-model="checkbox-1" >
<input type="checkbox" ng-click="checkbox-1 = false" ng-model="checkbox-2" >
</form>

Off the top of my head, I'd suggest one of three options for you.
Write a directive that will set up the checkboxes for you and manage the state within them. This isn't ideal, because you're turning a rule about your model (only one subscription should be checked) into DOM manipulation problem.
Structure your model such that only one subscription is ever marked active. This is tricky, because modifying the model on a change will kick off another digest cycle, not what you want.
Use radio buttons instead of checkboxes. That will get you the modality you want. :-P
I'd go with option 3--I'm always a fan of taking advantage of native input elements.
<tr ng-repeat="subscription in entities">
<td><input type="radio"
ng-model="selection"
name="subscriptionRadio"
value="{{subscription}}"/>
</td>
</tr>

Related

How to use checked input values used for math as part of cloned div elements

I have written a script that clones a certain div as required by the user. Within the div there are three checkbox input options and each option as a numeric value. I want the script to allow the user to select a checkbox and then the value will be reflected in another input space and each value that are added will be separated by a comma.
The tricky part is that it should be done for each clone, and that each checkbox has the same class name to which the script should be written. I realize that using unique id's would be better, but I would like it that a for loop could do it for any number of checkboxes under the specific class.
Here is the html script:
<style>
.hidden {
display: none;
}
</style>
<body>
<h2>Test</h2>
<button id="add">Add</button>
<div class="test hidden">
<div class="user_input1">
<label>Input1</label>
<input class="input1" type="text" required>
<label>Input2</label>
<input type="text" name="value2" required>
<div class="user_input2">
<table>
<thead>
<tr>
<th>Pick Option</th>
</tr>
</thead>
<tbody>
<tr id="append">
<td><input class="test" type="checkbox" name="test" value="1">Test1</td>
<td><input class="test" type="checkbox" name="test" value="2">Test2</td>
<td><input class="test" type="checkbox" name="test" value="3">Test3</td>
</tr>
</tbody>
</table>
<input type="text" id="insert" name="check">
<button class="hidden" id="testbtn">Calc</button>
</div>
</div>
</div>
<form action="server/server.php" method="POST">
<div class="paste">
</div>
<button type="submit" name="insert_res">Submit</button>
</form>
</body>
And my attempt for the jQuery:
$(document).ready(function() {
var variable = 0
$("#add").click(function() {
var element = $(".test.hidden").clone(true);
element.removeClass("hidden").appendTo(".paste:last");
});
});
$(document).ready(function(event) {
$(".test").keyup(function(){
if ($(".test").is(":checked")) {
var test = $(".test").val();
};
$("#insert").val(test);
});
$("#testbtn").click(function() {
$(".test").keyup();
});
});
I think a for loop should be used for each checkbox element and this to specify each individual clone, but I have no idea where or how to do this. Please help!
I am assuming you already know how to get a reference to the dom element you need in order to append, as well as how to create elements and append them.
You are right in that you can loop over your dataset and produce dom elements with unique id's so you can later refer to them when transferring new values into your input.
...forEach((obj, index) => {
(produce tr dom element here)
(produce three inputs, give all unique-identifier)
oneOfThreeInputs.setAttribute('unique-identifier', index); // can set to whatever you want, really
(proceed to creating your inputs and appending them to the tr dom element)
targetInputDomElementChild.setAttribute('id', `unique-input-${index}`); // same here, doesn't have to be class
});
Observe that I am using template strings to concat the index number value to the rest of the strings. From then on, you can either reference the index to refer to the correct input or tr using jquery in your keyUp event handler:
function keyUpEventHandler($event) {
const index = $(this).attr('unique-identifier');
const targetInput = $(`#unique-input-${index}`)
// do stuff with targetInput
}
I have created a fiddle to show you the route you can take using the above information:
http://jsfiddle.net/zApv4/48/
Notice that when you click an checkbox, in the console you will see the variable number that designates that set of checkboxes. You can use that specific number to get the input you need to add to and concat the values.
Of course, you still need to validate whether it is being checked or unchecked to you can remove from the input.

Angularjs $scope with parameter

I'm new in Angular and I can't achieve something really simple, lets say we have several checkbox input loops followed by an input text field like this:
<body ng-app="ngToggle">
<div ng-controller="AppCtrl">
<input type="checkbox" ng-repeat="btn in btns" ng-model="changing" ng-change="change(btn.value, 'other')" class="here" value="{{btn.value}}">
<input type="text" class="uncheck" ng-disabled="other">
<!-- Here comes the other one -->
<input type="checkbox" ng-repeat="btn in btns" ng-model="changing" ng-change="change(btn.value, 'other2')" class="here" value="{{btn.value}}">
<input type="text" class="uncheck" ng-disabled="other2">
</div>
</body>
And the idea in here is this: once the checkbox with value "other" is checked it will enable the corresponding text field determinate in the field parameter of the change function, so I develop a function change(value, field) and works like this:
$scope.btns = [
{value:'one'},
{value:'two'},
{value:'other'}
];
$scope.other = true;
$scope.other2 = true;
$scope.change = function(value, field) {
if(value == 'other'){
if($scope.field == false){
$scope.field = true;
} else {
$scope.field = false;
}
}
};
But the problem is, this does not work, I don't know if I making this in the right way, the $scope.field is the problem, how can I pass a parameter to the $scope
Hope someone can help me.
Thanks.
You can use ng-model with the checkbox, then use ng-disabled to listen to that model belonging to the checkboxes.
<input type="checkbox" value="Other" ng-model="myCheckboxes['other']" />
<input type="text" ng-disabled="myCheckboxes['other']" />

Set a radio button in an ng-repeat to checked

Evening all. Could anyone tell me how to set a radio button to checked in an ng-repeat when it loads? In the table below I have a list of prices with a sort function I created. It sorts from the lowest price down. Is there a way I can set the lowest price radio button to 'checked' each time the table loads?
<table>
<tr ng-repeat="prices in productVariant.prices | orderBy: tcoSort">
<td><strong>{{prices.code }}</strong></td>
<td ng-click="displayFullPricing(prices)">
<input type="radio" name="{{variant.code}}" ng-click="displayFullPricing(prices, $index)">
</td>
</tr>
$scope.tcoSort = function (productVariant) {
return productVariant.nonRecurring.retailPrice + (productVariant.monthly.retailPrice * $scope.productAttributesObj.Term);
};
Thanks
Very simple used $scope.modelName and set its value to any of the value(attribute) of radio button you want to set by default for example:-
Male
Female
now set $scope.one="male" in controller you will get the default value to radio button.
In case of ng-repeat concept is same but you just need to use $parent with the model of ng-repeat because ng-repeat creates its own scope that can not be accessed from controller.
Below is small simulated example:-
<div ng-repeat="check in people">
<input type="radio" name="hello" value="{{check.name}}" ng-model="$parent.human"/> {{check.name}}
</div>
$scope.people=[
{
'name':'rachit'
},
{
'name':'gulati'
},
{
'name':'rocks'
}
]
$scope.human='gulati';//You need to set it to the lowest price from your array .
Here is fiddle
<input type="radio" name="{{variant.code}}" ng-click="displayFullPricing(prices, $index)" checked>
??
An example using checkboxes that you could use:
<tr ng-repeat="content in contents" ng-class="{danger: currentContent.resource_uri === content.resource_uri || checkboxes.isSelected[content.id]}" name="content[{{$index}}]" ng-click="selectContent(content, $index);">
<td class="grid-checkbox"><input type="checkbox" ng-model="checkboxes.isSelected[content.id]" name="checkbox[{{content.id}}]" ng-checked="checkboxes.isSelected[content.id]" ng-change="changeCheckbox(content, $index)"/></td></tr>
this is a snippet from my code I guess what you need is
<input type="checkbox" ng-model="checkboxes.isSelected[content.id]" name="checkbox[{{content.id}}]" ng-checked="checkboxes.isSelected[content.id]" ng-change="changeCheckbox(content, $index)"/>
hope it helps

function find() with variable as a parameter returns empty object

I'm refactoring a code on a generated web page and there is a div (tab) which can occur multiple times. There is a small section with check-boxes on each and every such div, which lets you choose other divs that will be shown.
Since there is a chance for other divs to be added to the page I wanted to make the code modular. Meaning that every checkbox id is identical to the class of the div, which it should toggle, with added "Div" at the end. So I use checked id, concat it with "." and "Div" and try to find it in closest fieldset.
Here is the almost working fiddle: http://jsfiddle.net/ebwokLpf/5/ (I can't find the way to make the onchange work)
Here is the code:
$(document).ready(function(){
$(".inChecks").each(function(){
changeDivState($(this));
});
});
function changeDivState(element){
var divClassSel = "." + element.attr("id") + "Div";
var cloField = element.closest("fieldset");
if(element.prop("checked")){
cloField.find(divClassSel).toggle(true);
} else {
cloField.find(divClassSel).toggle(false);
}
}
Aside for that not-working onchange, this functionality does what it's intended to do. However only on the jsfiddle. The same code does not work on my page.
When I used log on variables from the code, the result was as this
console.log(divClassSel) => inRedDiv
console.log($(divClassSel)) => Object[div.etc.]
console.log(cloField) => Object[fieldset.etc.]
//but
console.log(cloField.find(divClassSel)) => Object[]
According to firebug the version of the jQuery is 1.7.1
Since I can't find any solution to this is there any other way how to make it in modular manner? Or is there some mistake I'm not aware of? I'm trying to avoid writing a function with x checks for element id, or unique functions for every check-box (the way it was done before).
Remove the inline onchange and also you don't need to iterate on the elements.
Just write one event on class "inCheckes" and pass the current element reference to your function:
HTML:
<fieldset id="field1">
<legend>Fieldset 1</legend>
<table class="gridtable">
<tr>
<td>
<input id="inRed" class="inChecks" type="checkbox" checked="checked" />
</td>
<td>Red</td>
</tr>
<tr>
<td>
<input id="inBlue" class="inChecks" type="checkbox" />
</td>
<td>Blue</td>
</tr>
</table>
<div class="inDivs">
<div class="inRedDiv redDiv"></div>
<div class="inBlueDiv blueDiv" /></div>
</fieldset>
<fieldset id="field2">
<legend>Fieldset 2</legend>
<table class="gridtable">
<tr>
<td>
<input id="inRed" class="inChecks" type="checkbox" />
</td>
<td>Red</td>
</tr>
<tr>
<td>
<input id="inBlue" class="inChecks" type="checkbox" checked="checked" />
</td>
<td>Blue</td>
</tr>
</table>
<div class="inDivs">
<div class="inRedDiv redDiv"></div>
<div class="inBlueDiv blueDiv" /></div>
</fieldset>
JQUERY:
$(document).ready(function () {
$(".inChecks").change(function () {
changeDivState($(this));
})
});
FIDDLE:
http://jsfiddle.net/ebwokLpf/4/
As gillesc said in the comments changing the javascript code to something like this made it work.
$(document).ready(function(){
$(".inChecks").each(function(){
changeDivState($(this));
});
$(".inChecks").on("change", function() {
changeDivState($(this));
});
});
function changeDivState(element){
var divClassSel = "." + element.attr("id") + "Div";
var cloField = element.closest("fieldset");
if(element.prop("checked")){
cloField.find(divClassSel).toggle(true);
} else {
cloField.find(divClassSel).toggle(false);
}
}
You asked for an other way how to make it in modular manner:
You can create a jQuery plugin which handles the logic for one fieldset including changing the color when clicking different checkboxes.
This way all logic is bundled in one place (in the plugin) and you can refine it later on.
For example you can decide later on that the plugin should create the whole html structure of the fieldset (like jQuery UI slider plugin creates the whole structure for the slider element) and therefore change the plugin.
The code for the (first version) of your jQuery plugin could look something like this:
$.fn.colorField = function() {
var $colorDiv = this.find('.colorDiv'),
$inputs = this.find('input'),
$checked = $inputs.filter(':checked');
if($checked.length) {
// set initial color
$colorDiv.css('background', $checked.attr('data-color'));
}
$inputs.change(function() {
var $this = $(this),
background = '#999'; // the default color
if($this.prop('checked')) {
// uncheck the other checkboxes
$inputs.not(this).prop('checked', false);
// read the color for this checkbox
background = $(this).attr('data-color');
}
// change the color of the colorDiv container
$colorDiv.css('background', background);
});
};
The plugin uses the data-color-attributes of the checkboxes to change the color of the colorDiv container. So every checkbox needs an data-color attribute, but multiple divs for different colors are not necessary anymore.
The HTML code (for one fieldset):
<fieldset id="field1">
<legend>Fieldset 1</legend>
<table class="gridtable">
<tr><td><input id="inRed" class="inChecks" type="checkbox" checked="checked" data-color='#ff1005' /></td><td>Red</td></tr>
<tr><td><input id="inBlue" class="inChecks" type="checkbox" data-color='#00adff' /></td><td>Blue</td></tr>
</table>
<div class="colorDiv"></div>
</fieldset>
Now you can create instances with your colorField-plugin like this:
$(document).ready(function(){
$('#field1').colorField();
$('#field2').colorField();
});
Here is a working jsFiddle-demo

how can I validate multiple ng-repeated ng-form

I have a variable form list and want to display validation error messages outside of the list elements.
consider this template:
<form name="gForm">
<table>
<tr ng-repeat="invoice in invoices" ng-form="invoiceForm">
<td><input name="amount" type="number" max="200" /></td>
</tr>
</table>
<div ng-show="gForm.invoiceForm.amount.$invalid">you must the money</div>
<button ng-click="addInvoice()">add invoice</button>
</form>
the validation error would only be displayed when the last ng-repeat is invalid. Put another way, gForm.invoiceForm points to the lastly created form in ng-repeat.
I've seen other questions related to this problem but they only suggest repeating the validation messages inside the ng-repeat. I need the message to be outside and displayed once only.
The way you have it, gForm.invoiceForm does refer to the last <tr> in ng-repeat.
If you want to display the error when any of the amounts is invalid, you can use gForm.$invalid. In fact there is no need to use ng-form="invoiceForm" unless there are more requirements not evident from the current question's code.
Another problem is that, in order for Angular to recognize the input and apply its directive (and its magic consequently), the ng-model directive is required as well.
Adding the ng-model directive and changing the condition to gForm.$invalid solves the problem:
...
<tr ng-repeat="invoice in invoices">
<td><input name="amount" type="number" max="200"
ng-model="invoice.amount" /></td>
</tr>
...
<div ng-show="gForm.$invalid">you must the money</div>
...
See, also, this short demo.
Are you looking for something like this? Yes you need to use ng-model, but also you need a unique name:
<div ng-app="pageModule"
ng-controller="parentCtrl">
<form name="gForm">
<table>
<tr ng-repeat="invoice in invoices" ng-form="invoiceForm">
<td>{{invoice.name}}: <input name="invoice.name" required type="number" max="200" ng-model="invoice.amount" /></th>
</tr>
</table>
<div ng-show="gForm.$invalid && showError">you must the money</div>
<button ng-click="addInvoice()">add invoice</button>
</form>
</div>
<script>
var pageModule = angular.module('pageModule',[])
.controller('parentCtrl',function($scope) {
$scope.invoices = [
{ name : 'ford' },
{ name : 'chevy' },
{ name : 'honda' },
]
$scope.showError = false;
$scope.addInvoice = function() {
$scope.showError = true;
console.log('add invoice');
}
})
</script>

Categories