AngularJS - dynamic naming using $index in expression - angularjs

I have a directive in which i create many ng-form's and i name each form based on the $index (from the ng-repeat). My problem is that i want to show an error container (that contains the error message) when the form is invalid but i cant find how to properly reference the form.
Here is my code:
<ng-form name="innerForm{{$index}}">
<label ... ><input name="input" ... />
<div class="error-container" ng-show="'innerForm'+$index.input.$invalid">
// show the error message
</div>
</ng-form>
I want 'innerForm'+$index.input.$invalid to be evaluated as innerForm5.input.$invalid for example.
I have made many attempts but i can't get it to work. What is the correct way to reference my dynamically named form?

please see here : http://jsbin.com/jocane/4/edit
<div ng-controller="firstCtrl">
<div ng-repeat="object in allMyObjects">
<ng-form name="innerForm{{$index}}">
<input type="text" ng-model="object.name" required name="userName">
<div ng-show="{{'innerForm'+$index}}.userName.$invalid">
error
</div>
</form>
</div>

First thing is that, ng-form creates an isolated scope. So if you give a ng-form static name, it will be ok. No need to append $index or anything.
ng-show="innerForm.input.$invalid"
Code snippet to find form controller using element
var elmParent = $(domEle).parents('form, ng-form, [ng-form]');
var formCtrl = null;
var elemCtrl = null;
if (elmParent.length) {
var elm = angular.element(domEle);
formCtrl = elm.scope().$parent[elmParent.attr('name') || elmParent.attr('ng-form')];
}
Hope this will help

Related

AngularJS protractor test in nested ng-repeat

I have nested ng-repeat like this. I am the doing protractor test. So I want to access the first index of parent repeater.
<div ng-repeat="pChild in mainParent | orderBy : 'order'" ng-show="checkfor.qm">
<div ng-repeat="cChild in Child ">
<input id="checkbox-01" type="checkbox" class="ng-valid ng-dirty ng-valid-parse ng-touched ng-not-empty">
</div>
</div>
So, I want to select first child of mainParent and inside it, I want to calculate length of Child.
I am new to protractor test. Please help me. Thanks in Advance
I am assuming that you want to calculate the length of cChild of first child of mainParent. You can use below code.
<div ng-repeat="pChild in mainParent| orderBy : 'order'" ng-show="checkfor.qm" ng-init="parentIndex = $index">
<div ng-repeat="cChild in pChild| orderBy:'iseeit__Priority__c' ">
<span ng-if="parentIndex === 0">cChild.length<span>
</div>
</div>
instantiate the parent index with some local variable, and check if it's the first element.
Let me know, if you are looking for something else.
var elm = element(by.repeater("pChild in mainParent").row(0)).all(by.repeater("cChild in Child")).map(function (elm) {
return elm.element(by.css("input#checkbox-01")).getAttribute("class");
}).then(function (links) {
console.log(links.length);
})
links.length have provided the length.

Angular form name is passed as string when passed as parameter

I'm simply trying to reset a form using the angular functions $setPristine & $setUntouched (several forms are created with ng-repeat).
I assign the form name dynamically by using the syntax {{ someName }} (the name is build on the server side and is passed as json (string)).
The name of the form is correctly assigned in the markup and validations are working as expected. The problem arrises when I pass that name as a parameter in the ng-click="reset(someName)" function.
When debugging the name comes as a string and not as the form object which causes the error. I did a quick test by hard-coding the name and pass that same name and it works fine.
My assumption is, the name coming from json is a string and the type is forwarded to the function as is, instead of the object.
So the question is: is there a way to convert that name so it is interpretated correctly by the controller. Or maybe there is something else I'm missing...
Here is the markup ( notice the name of the form uses {{ resto.contactForm }} ):
<form novalidate name="{{ resto.contactForm }}" ng-submit="submit(restoContact, resto.contactForm.$valid)" class="sky-form">
<div class="form-group">
<label class="checkbox state-success">
<input type="checkbox" ng-model="restoContact.sameAsUser" name="sameAsUser" id="sameAsUser" value="true" ng-click="contactAutoFill()"><i></i>Contact name is same as current user.
<input type="hidden" name="sameAsUser" value="false" />
</label>
</div>
<div class="form-group">
<label class="control-label" for="contactName">Contact Name</label>
<input type="text" ng-model="restoContact.contactName" name="contactName" id="contactName" placeholder="John, Doe" class="form-control" required />
<div ng-show="{{ resto.contactForm }}.contactName.$error.required && !{{ resto.contactForm }}.contactName.$pristine" class="note note-error">Please enter a name or check the box 'Same as current user'.</div>
</div>
<div class="form-group">
<label class="control-label" for="contactPhoneNumber">Contact Phone Number</label>
<input type="text" ng-model="restoContact.contactPhoneNumber" name="contactPhoneNumber" id="contactPhoneNumber" placeholder="+1 555-1234-567" class="form-control" required ng-pattern="phoneNumberPattern" />
<div ng-show="({{ resto.contactForm }}.contactPhoneNumber.$error.required || {{ resto.contactForm }}.contactPhoneNumber.$error.pattern) && !{{ resto.contactForm }}.contactPhoneNumber.$pristine" class="note note-error">Please enter a valid phone number.</div>
</div>
<div class="margin-leftM19">
<button class="btn btn-primary">Save Changes </button>
<button class="btn btn-default" ng-click="reset(resto.contactForm)">Cancel </button>
</div>
</form>
Here is the reset function in the controller (form comes as "contactForm1" which is the correct name but is a string and not the object):
$scope.reset = function (form) {
if (form) {
form.$setPristine();
form.$setUntouched();
}
//$scope.user = angular.copy($scope.master);
};
I have not implemented th submit method but I'm sure I will be running into the same issue.
Any suggestions or advices are welcome.
Thanks in advance...
Here is the fidle.js. the variable data is an exact response from the server.
[http://jsfiddle.net/bouchepat/v0mtbxep/]
SOLUTION:
http://jsfiddle.net/bouchepat/v0mtbxep/3/
I removed $setUntouched as it throws an error.
You can't dynamically name a <form> or <ng-form>.
Although what you want, is make the form usable in the controller. You could do the following:
// in controller
$scope.form = {};
$scope.reset = function() {
$scope.form.contact.$setPristine();
$scope.form.contact.$setUntouched();
};
// in html
<form name="form.contact">
This is happening because resto.contactForm is a string defined on the scope. The angular directive for form is just creating a variable on the scope with the same name. To get the variable by a string, use $eval. This should work:
$scope.reset = function (formName) {
var form = $scope.$eval(formName);
if (form) {
form.$setPristine();
form.$setUntouched();
}
//$scope.user = angular.copy($scope.master);
};

Dynamic ng-model name in AngularJS form

I have an AngularJS form where I'm using ng-repeat to build the fields of the form dynamically based on another selection. I'm trying to generate the model name dynamically and am running into problems.
<div class="form-group" ng-repeat="parameter in apiParameters">
<label for="{{parameter.paramName}}" class="col-sm-2 control-label">{{parameter.paramTitle}}</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="{{parameter.paramName}}" id="{{parameter.paramName}}" ng-model="formData.parameters.{{parameter.paramName}}" placeholder="{{parameter.paramTitle}}">
</div>
</div>
What I need is it to eval to something like ng-model="formData.parameters.fooId" or ng-model="formData.parameters.barId"
The above results in an error:
Error: [$parse:syntax] Syntax Error: Token '{' is an unexpected token at column 21 of the expression [formData.parameters.{{parameter.paramName}}] starting at [{{parameter.paramName}}].
In my controller I have:
$scope.formData = {};
$scope.formData = {
settings:
{
apiEndpoint: '',
method: 'get'
},
parameters: {}
};
I've also tried ng-model="formData.parameters.[parameter.paramName]" (based on this question How can I set a dynamic model name in AngularJS?), but that doesn't eval and fails to set the ng-model value. I'm not sure if what I'm trying to accomplish is even possible. I'm assuming I need to go through the controller to achieve what I want, but any help would be appreciated.
You just need to use hash key as model:
<div class="form-group" ng-repeat="parameter in apiParameters">
<label for="{{parameter.paramName}}" class="col-sm-2 control-label">{{parameter.paramTitle}}</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="{{parameter.paramName}}" id="{{parameter.paramName}}" ng-model="formData.parameters[parameter.paramName]" placeholder="{{parameter.paramTitle}}">
</div>
</div>
There is many other approaches, but this one are simpler than others.
I have tested this solution but it has a problem for my self. The problem is that the parameter "formData" is assigned to each iteration individually. In the other words if you insert a pre tag in every iteration you will see the value of each iteration individually and you cannot get all the formData in your controller after submitting the form.
For this I have found a very simple solution and it is the ng-init !!!!
Simply add this code to your form and before your repetitive tag. For the example of this question we will have:
<form ng-init="formData = []">
<div class="form-group" ng-repeat="parameter in apiParameters">
<label for="{{parameter.paramName}}" class="col-sm-2 control-label">{{parameter.paramTitle}}</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="{{parameter.paramName}}" id="{{parameter.paramName}}" ng-model="formData.parameters[parameter.paramName]" placeholder="{{parameter.paramTitle}}">
</div>
</div>
</form>

How do I add a class to a div if the item in ng-repeat is not valid?

I am trying to use $invalid to add a class to a div when the input within the div is not filled out. However I just cannot work out the syntax.
<div ng-app ng-controller="miniC">
<form name="persondetails" novalidate>
<div ng-repeat="account in myAccounts" ng-class="{'has-error': account.amount.$invalid}" >
<input name="{{account.name}}" ng-model="account.amount" required="required"/>
</div>
</form>
</div>
function Account(nameArg, amountArg){
this.name = nameArg;
this.amount = amountArg;
}
The surrounding div needs to have class="has-error" when the inner item is not filled in. I have done similar on single bound elements just cant seem to get this working....
$invalid flag is not attached to the ngModel, but to the form controller (ngFormController).
In your example you're not demonstrating that you're even using ngForm, and you should, if you want angular to validate your form.
Next, you're trying to dynamically set the input's name attribute. ngForm won't like it because ngFormControll will be initialized before your expression is evaluated.
Workaround is to wrap each ng-repeat iteration inside new ngForm (ngForm can be nested and it will just work) and name your inputs with some static string value (e.g. name="amount").
So, you'll want your view written as:
<div
ng-repeat="account in myAccounts"
ng-class="{'has-error': form.amount.$invalid}"
ng-form="form"
>
<input name="amount" ng-model="account.amount" required="required"/>
</div>

How to pass custom directive name dynamically in class attribute of div tag?

I am very new to AngularJS. I have made a custom directive user and I want to call it dynamically in class attribute by using a variable.
e.g. $scope.dirName = "user";
When i use this variable in below code:
<div class = {{dirName}}></div>
Its result must show two input fields with specified values. But it is not doing so. When I replace {{dirName}} with user. It is working fine, means two input fields are shown with values as specified. Can anybody tell, what mistake I am doing?
This is index.html
<div ng-controller = "Ctrl">
<form name = "myForm">
<div class = {{dirName}}></div>
<hr>
<tt>userName : {{user}}</tt>
</form>
This is script.js
<pre>var app = angular.module('App',[]);
app.controller('Ctrl', function($scope){
$scope.user = {name:'adya',last:'Rajput'};
$scope.dirName = "user";
});
app.directive('user',function(){
return{
restrict:'C',
templateUrl:'template.html'
};
});</pre>
template.html contains:
UserName : <input type='text' name='userName' ng-model='user.name' required>
LastName : <input type='text' name='lastName' ng-model='user.last'>
Unfortunately, you cannot save names of directives in string variables and access them in the HTML. You can, however, save a string in a variable in $scope and use ng-switch to select the correct directive:
<div ng-switch="dirName">
<div ng-switch-when="ng-user">
<div ng-user></div>
</div>
<div ng-switch-when="...">
...
</div>
</div>
However, now it might be better to use something more descriptive than ng-user to switch over.
Sidenote: Do not use ng- prefix in your own directives. Angular uses that prefix so that it does not collide with other namespaces. You should use your own prefix for your directives.
Update: For the updated question as to why <div class="{{dirName}}"></div> does not work, it happens because angular $compiles the directive only once. If you first $interpolate the content of the template (which will replace {{dirName}} with ng-user) and then explicitly $compile it before entering it in the HTML, it should work.

Resources