how can I validate multiple ng-repeated ng-form - javascript

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>

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.

bind a textbox value if checkbox is checked

how to pass a textbox value to controller based on condition .
if checkbox is checked then bind the textbox with object value and pass to the controller other wise just leave it blank and pass the user input to controller.. what i am doing is not working. what is wrong with my code it is working in the case if checkbox is checked.
$scope.Product = [
{"ProductID":12,"LNumber":"hrx",weght:"2"},
{"ProductID":13,"LNumber":"pty",weght:"1"}
]
<div>
<div>
<input type="checkbox" data-ng-model="Copyknotes" />
<span >Copy notes from</span>
</div>
<table data-ng-repeat="Item in Product track by $index">
<tr >
<td>
<input type="radio" name="groupName_{{Item.ProductID}}" data-ng-model ="Item.isSelected" />
</td>
<td data-ng-if="Copyknotes == true">
<input type="text" data-ng-model="Item.LNumber">
</td>
<td data-ng-if="Copyknotes == false" id="hi">
<input type="text" data-ng-model="Item.LNumber=""">
</td>
</tr>
</table>
</div>
Just use
data-ng-init=""
instead of
data-ng-model="Item.LNumber="""
use
data-ng-model="Item.LNumber"
<table data-ng-repeat="Item in Product track by $index">
<tr >
<td>
<input type="radio" name="groupName_{{Item.ProductID}}" data-ng-model ="Item.isSelected" />
</td>
<td data-ng-if="Copyknotes == true">
<input type="text" data-ng-model="Item.LNumber">
</td>
<td data-ng-if="Copyknotes == false" id="hi">
<input type="text" data-ng-model="Item.LNumber" data-ng-init="">
</td>
</tr>
</table>
Use scope.function
<input type="checkbox" data-ng-model="Copyknotes" ng-change="changeValue(Copyknotes)" />
//Code should be inside Angular js controller
$scope.changeValue = function(Copyknotes){
if(Copyknotes)
{
//Manipulate text box value here
$scope.Item.LNumber = 'whatever';
}
}
Here is an example:
https://plnkr.co/edit/3Vtl6roWfL1ZqaR2nEvf
<td data-ng-if="Copyknotes == false">
<input type="text" data-ng-model="Item.NNumber" ng-init="Item.NNumber = ''">
</td>
The expression was wrong - data-ng-model="Item.LNumber=""" - if you want to assign a new value, you can use Item.LNumber = "''" (two single quotes within double quotes) to avoid interference with tag attribute "" symbols. I've made a live example of how it could be done. Don't know if your controller need original values of input, so new values (when checkbox is unchecked) are saving to NNumber instead. You can freely change them to LNumber if you want. Also, ng-init directive is used to initiate NNumber parameter of object when inputs are rendered into view.
Also you should define Copyknotes to compare. Or write your conditions like ng-if="Copyknotes", ng-if="!Copyknotes".
<input type="checkbox" data-ng-model="Copyknotes" ng-change="changeValue(Copyknotes)" />
first of all remove the data-ng-model and use following:
//Code should be inside Angular js controller
var oninput = null;
$scope.changeValue = function(Copyknotes){
if(Copyknotes)
{
var oninput = document.getElementById("textbox").onchange =function(){
$scope.item.LNumber = this.value;
}
//Manipulate text box value here
}else{
$scope.Item.LNumber = '';
oninput = null
}
}

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

AngularJS: single select between multiple checkbox

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>

Input field blurs when inline editing in ng-repeat

I am trying to do inline editing on a table of data (See the plunkr)
<table class="table table-bordered">
<tr ng-repeat="data in dataset" >
<td ng-repeat="(key, value) in data" >
<div class="key-block">
<strong >{{key}}</strong>
</div>
<div class="val-block" inline-edit="data[key]" on-save="updateTodo(value)" on-cancel="cancelEdit(value)">
<input type="text" on-enter="save()" on-esc="cancel()" ng-model="model" ng-show="editMode">
<button ng-click="cancel()" ng-show="editMode">cancel</button>
<button ng-click="save()" ng-show="editMode">save</button>
<span ng-mouseenter="showEdit = true" ng-mouseleave="showEdit = false">
<span ng-hide="editMode" ng-click="edit()">{{model}}</span>
<a ng-show="showEdit" ng-click="edit()">edit</a>
</span>
</div>
</td>
</tr>
I can see in many places that we have to use a . in ng-model inside ng-repeat to avoid the scope issue. As I dont know the key already I am doing like data[key] for the model.
The input field blurs after I enter a single character.
The behavior you described is normal. If you look closely you will see that both the input value and the directive are bound to the same object i.e data[key]. When you change the value of the text input the model get updated ultimately triggering a refresh of the directive and you are back to the "list" view.
One easy solution to fix this is to use an intermediate variable between the directive and the input value and update the model only when the save button is clicked. Something like that :
//Directive
scope.newValue = null;
scope.edit = function() {
scope.editMode = true;
scope.newValue = scope.model;
$timeout(function() {
elm.find('input')[0].focus();
}, 0, false);
};
//Template
<input type="text" on-enter="save()" on-esc="cancel()" ng-model="newValue" ng-show="editMode">
You can see a modified plunker here.

Categories