Angular Form Validation inside ngRepeat - angularjs

I have a series of table rows with various inputs inside of an ng-repeat and I'm trying to use angular's built in form validation to handle required fields. Everything works well except for when I delete one of the items in the array that is backing the ng-repeat.
Here is how I've set up my validation
<td ng-class="{ 'has-error' : vm.testForm.itemName{{$index}}.$invalid }">
<input type="text"
name="itemName{{$index}}"
class="form-control"
ng-model="item.name"
required />
<div class="help-block"
ng-show="vm.testForm.itemName{{$index}}.$invalid"
ng-messages="vm.testForm['itemName'+$index].$error">
<span ng-message="required">Name is required</span>
</div>
</td>
Plunker showing my issue
https://plnkr.co/edit/Q1qyqlSG69EXR2lTrsHk
In the plunker, if you delete the first table row, you'll notice that while the last row is still invalid, the errors have become attached to the row above. Then as you fill in the last row to make it valid and then empty it again, the errors still show up on the wrong row. Deleting subsequent rows just moves the errors further from the actual invalid row until they don't appear on the table at all.
Is there another way I should be going about validating these inputs?

The problem is that you you don't usally use {{}} (interpolation) in built in angular directives. So change your code to:
<td ng-class="{ 'has-error' : vm.testForm['itemName' + $index].$invalid }">
<input type="text"
name="itemName{{$index}}"
class="form-control"
ng-model="item.name"
required />
<div class="help-block"
ng-show="vm.testForm['itemName' + $index].$invalid"
ng-messages="vm.testForm['itemName'+$index].$error">
<span ng-message="required">Name is required</span>
</div>
</td>
Change:
vm.testForm.itemName{{$index}}.$invalid
To:
vm.testForm['itemName' + $index].$invalid
Made a plunker to verify:
https://plnkr.co/edit/vuQiH4D8qUY9jVoThnCy?p=preview

Related

Values are duplicated into all input field which is created using the ng-repeat and ng-model

I am using the ng-repeat to create the input fields dynamically, everything seems to be working fine and the input fields are created but due to some reason when I enter some value into that created input field then due to two-way data binding the value is entered simultaneously in all the text fields that were created.
As per my knowledge, I am assigning the different ng-model to each of the created fields so I am not sure why the values are being copied. When I try to print the values which are assigned to the input field just above the field then I see the id is different wanted to know why all the fields are taking the same value and also how can I prevent it.
HTML Code:
<tr>
<td ng-repeat="extension in ExtensionList">
<!-- Here the value displayed are different -->
{{ formdata.extension.ExtensionVlaues }}
<input type="text" class="form-control" id="extension.ExtensionVlaues" ng-model="formdata.extension.ExtensionVlaues">
</td>
</tr>
Here is the screenshot from the front-end:
Here is the example of my ExtensionList: {"Field1":"","Field2":"","Field3":1,"ExtensionVlaues":2}
As per my knowledge, the given ng-model is different but still not working. Can anyone please help me with this? I tried few things like ng-model="formdata.extension[ExtensionVlaues]" but still no luck.
I tried to assign ng-model like this but it is not working:
ng-model="formdata.extension[ExtensionVlaues]"
Based on the below answer I tried 2 different things it but it's not working and getting the same issue. Please let me know how can I resolve this:
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata.extension.key">
</td>
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata.extension.ExtensionVlaues">
</td>
You are trying to use ExtensionList as an array. You need to use it as an object with ng-repeat:
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata[extension][key]">
</td>
<td ng-repeat="(key, extension) in ExtensionList">
<input type="text" ng-model="formdata[extension][ExtensionVlaues]">
</td>
Just incase anyone else also struck at this issue:
<span ng-repeat="(key, extension) in ExtensionList" class="form-inline">
<br/>
<span>
{{ extension.NameSpace + ":" + extension.LocalName }}
</span> 
<input type="text" class="form-control" id="extension.ExtensionVlaues" ng-model="ExtensionList.extension[key].FreeText" ng-blur="ExtensionText(ExtensionList.extension[key].FreeText, extension.ExtensionVlaues)">
</span>

Angular radio button validation with an ng- repeat

I'm running into an interesting Angular validation issue. I need to make sure that radio buttons are selected (they might not have a default value when loaded). The values for the radio buttons are iterated over from an array using ng-repeat. This whole thing also happens inside another ng-repeat for a list of users.
The issue is that I have to set a unique/dynamic name attribute on each group of related radio buttons, otherwise selecting one will unselect others in a different row. The validation in based on the name attribute and I cannot seem to find a way to use this unique/dynamic name in the needed ng-class and ng-show expressions.
This is waht is not working:
ng-show="userFormRow.service-{{$index}}.$invalid"
Here's a sample of the code in context:
<table class='table table-striped table-bordered'>
<tbody>
<tr ng-repeat="u in users"
ng-form="userFormRow">
<td>
<!-- THIS is having an issue determining the name of this form item since I need to generate a dynamic one here-->
<div class="form-group"
ng-class="{'has-error': userFormRow.service-{{$index}}.$invalid }">
<label class="control-label center-block">Service</label>
<div class="radio-inline" ng-repeat="o in serviceOptions">
<label>
<!-- HERE is where I define the unique name attribute based on the index of the table repeater -->
<input type="radio"
name="service-{{$parent.$index}}"
value="{{::o}}"
ng-model="u.Service"
ng-required="!u.Service"> {{::o}}
</label>
</div>
<!-- THIS is having an issue determining the name of this form item since I need to generate a dynamic one here-->
<p class="help-block" ng-show="userFormRow.service-{{$index}}.$invalid">A service must be selected!</p>
</div>
</td>
</tr>
</tbody>
</table>
And here is a full code example.http://codepen.io/chrismbarr/pen/eNoGLJ?editors=101 Check the console for errors
You should use bracket notation to access variable object property:
ng-show="userFormRow['service-' + $index].$invalid"
and same with ngClass:
ng-class="{'has-error': userFormRow['service-' + $index].$invalid }"
Demo: http://codepen.io/anon/pen/rVbYpG?editors=100

Creating dynamic forms with angularjs

So Im creating dynamic forms with angularjs. And when the form is sent, I delete it from the array. But for some odd reason forms validation rules somehow "stick" to the next form. For example. I send the first form, The second form now gets validated if the third form has valid answers, and so on if the 4th form has valid answers the 3th form will be valid. What could be the possible reasons for this?
This is pretty much striped down code to the basics of what I have
<div ng-repeat="item in ItemsAdder.items track by $index" ng-form="item.itemForm">
<div class="form-group control-group">
<label for="category" class="col-sm-2 control-label">{{trans('adsAdd.category')}}</label>
<select ng-options="category.name for category in categories track by category.id" ng-init="item.category=categories[0]" ng-model="item.category"></select>
</div>
<div class="form-group control-group" ng-class="{ 'has-error' : item.itemForm.price.$invalid && !item.itemForm.price.$pristine }">
<label for="price" class="col-sm-2 control-label">Price</label>
<input ng-model="item.price" ng-class="{ 'has-error' : item.itemForm.price.$invalid && !item.itemForm.price.$pristine }" required type="number" ng-trim="false" name="price">
<p ng-show="item.itemForm.price.$error.number && !item.itemForm.price.$pristine" class="help-block">{{trans('items.add.priceNeedsToBeNumber')}}</p>
<p ng-show="item.itemForm.price.$error.required && !item.itemForm.price.$pristine" class="help-block">{{trans('items.add.priceNeeded')}}</p>
</div>
<div class="form-group control-group" ng-class="{ 'has-error' : item.itemForm.description.$invalid && !item.itemForm.description.$pristine }">
<label for="description" class="col-sm-2 control-label inputLabel">Description</label>
<textarea ng-minlength="3" ng-class="{ 'has-error' : item.itemForm.description.$invalid && !item.itemForm.description.$pristine }" ng-model="item.description" name="description" required class="inputInput" style="max-width: 100%;"></textarea>
<p ng-show="item.itemForm.description.$error.required && !item.itemForm.description.$pristine" class="help-block">{{trans('items.add.descriptionNeeded')}}</p>
</div>
<button ng-click="ItemsAdder.send($index)" ng-disabled="item.itemForm.$invalid">{{trans('adsAdd.send')}}</button>
</div>
And my send function:
ItemsAdderFactory.prototype.send = function ($index) {
var self = this;
var responsePromise = $http.post("",this.items[$index]);
responsePromise.success(function (data, status, headers, config) {
self.items.splice($index, 1);
});
responsePromise.error(function (data, status, headers, config) {
alert('There was an error, please try again.');
});
};
Btw I have the ng-form="" as item.ItemForm so I could access the form through items when a Send All Button is pressed, and it checks what forms are valid and only sends those. If there Is a different or a propper way of doing it, Im all ears.
So guys, I found out the answer. And I'm going to leave it here if anyone else gets this.
The problem was because I was using track by $index in the ng-repeat and I guess the form validation wanted to stick around because the index didn't change.
So don't use track by $index in ng-repeat if your having these problems.
When you delete the submitted from the array ItemsAdder.items remember that the ng-repeat has two way binding so it resets the array and now the 1st index item becomes the 0th index item.
This is best guess about the problem you are facing.

How do I dynamically set element name for form validation?

I have a form with input fields generated with an ng-repeat. The field names are set dynamically from the model. I cannot get validation to work.
Here is the input field that is repeated within ng-repeat:
<input class="form-control input" type="number" id="item.id" name="item.name" ng-change="ctrl.updateSub(item)" ng-model="item.qty" max="item.maxqty" min="0">
I am trying to validate against the max value, which is also set dynamically.
What I cannot find anywhere is how to set the name within the ng-show classes.
<div class="col-sm-2 error" ng-show="form.{{item.name}}.$invalid">
<small class="error" ng-show="form.{{item.name}}.$error.max">
You exceeded the maximum allowed
</small>
</div>
How am I supposed to handle the {{item.name}} bit?
Thanks in advance for any help or pointers.
Angular 1.3.12
Firstly form is a reserved keyword, you cannot use that as your form name.
Coming to your problem, unfortunately as of right now it is not possible to generate dynamic names for form inputs.
You can achieve what you want with the help of ng-form
Have a look at this example :
<form name="yourForm" >
<div ng-repeat="field in fields" ng-form="myInnerForm">
<input type="text" name="dynamic_input" ng-model="field.name"/>
<div ng-show="myInnerForm.dynamic_input.$dirty && myInnerForm.dynamic_input.$invalid">
The field is required.
</div>
</div>
</form>

Angular Bootstrap Directive Typeahead validation not working... Can't get updated model info

I have the following in Angular BootStrap directives: Editable is set to false, but I can't figure out how to get the $invalid value. It always shows as valid. I am using Angular-bootstrap .11 with Angular 1.3... It seems as if the model never gets updated so that $invalid becomes true...
<div class="form-group" ng-class="{ 'has-error':myForm.accountName.$invalid && myForm.accountName.$dirty }">
<form name="myForm">
...
<label>Seller: {{currentLot.sellerCode}}</label>
<input type="text" name="accountName" placeholder="" class="form-control" ng-model="currentLot.accountName"
typeahead="item.sellerCode + ' ' + item.name for item in searchGeneric('accounts',$viewValue)"
typeahead-loading="loadingAccountsSellerCode" typeahead-on-select="accountSelected($item)"
typeahead-wait-ms="2" typeahead-min-length="2" typeahead-editable="false"
typeahead-append-to-body="false" >
<div class="warning" ng-show="!myForm.accountName.$invalid && lotForm.myForm.$dirty">
<i class="icon-warning-sign"></i> Values must exist in the list...
</div>
<i ng-show="loadingAccountsSellerCode" class="glyphicon glyphicon-refresh"></i>
...
In the original code the form isn't the first element in my angular template. I moved the form declaration and it works. A bug perhaps in Angular or the directives.

Resources