Validate a form field with a dynamically given name in angular - javascript

I create a form dynamically in the view by iterating through an object that has the different questions to be asked to the user. One of the attributes of every question is formFieldName which is a a random string I use to give each form field a different name.
<form name="includedForm.newRequestForm" class="form-horizontal" role="form" novalidate>
<div ng-if="message.question.attributes.structure.type == 'object'">
<div ng-repeat="(index,objField) in message.question.attributes.structure.properties">
<div ng-if="objField.type == 'array'" class="form-group" show-errors>
<label for="{{objField.formFieldName}}" class="control-label col-sm-6">{{objField.title}}
<br /><i><small>{{objField.description}}</small></i></label>
<div class="col-sm-6">
<select class="form-control" name="{{objField.formFieldName}}" multiple ng-model="objField.userValue" ng-required="objField.required">
<option ng-repeat="option in objField.items.enum" value="{{option}}">{{option}}</option>
</select>
</div>
</div>
<div ng-if="objField.type == 'boolean'" class="form-group" show-errors>
<label for="{{objField.formFieldName}}" class="control-label col-sm-6">{{objField.title}}</label>
<div class="col-sm-6">
<input class="form-control" name="{{objField.formFieldName}}" ng-model="objField.userValue" type="checkbox" ng-value="option" ng-checked="message.question.attributes" />
</div>
</div>
</div>
</div>
<div class="col-sm-12">
<button ng-click="markAsDone(message)" class="btn btn-primary">Done</button>
</div>
<form>
In the controller I'm able to get the formFieldName attribute but I can't figure out how to use it to do the validation.
var MarkAsDone = function(message) {
$scope.includedForm = {};
var formField = message.question.attributes.formFieldName;
if ($scope.includedForm.newRequestForm.{{formField}}.$valid){
//submit the form
}
}

to answer you question:
first, {{}} is === $scope so you don't use that anywhere other than HTML. You use $scope in your JS and {{}} in HTML which creates a pipe (2-way binding) so that $scope.variable.property has bidirectional binding to {{variable.property }} in HTML.
$scope.includeForm.email === {{ includeForm.email }} === ng-model="includeForm.email" === ng-bind="includeForm.email"
if you set anyone of those all are set so if you set $scope it will show up in HTML and obviously as user input gets captured it is already in $scope ... all connected
when attempting to get the value from HTML back into JS you would need create and set a $scope i.e so if you create $scope.dataModel.dataProperty and use that in ng-model=dataModel.dataProperty (example) you again have two way binding ... you don't need to do anything as angular is taking care of the data pipeline. So if you want to extract the value to var, which is probably a waste as the $scope is already set as soon as the user checks the box
var formField = $scope.dataModel.dataProperty;
// but like I said no need as $scope.dataModel.dataProperty; is your var
In JS if you want to use a dynamic property as an object property key you would place the dynamic value in [] e.g.
$scope.variable[dynamicProperty].method;
// you can set a static property as a key with dot notation i.e.
$scope.variable.staticProperty = val;
Hope that helps

Related

How to restrict ngModel changes effect to other inside ng-template in angular7?

This is my code
<ng-template #rowDetailsTmpl let-row="row">
<div class="row" style="padding: 10px 30px;">
<div class="col-sm-5 form-group">
<label> Add Operator </label>
<input type="string" id={{row.DeskId}} name={{row.DeskId}} (ngModelChange)="onChangeOperator($event)" class="form-control"
placeholder="Search Operator" [(ngModel)]="selectedOperatorEmail">
</div>
#ViewChild('rowDetailsTmpl', { static: true }) rowDetailsTmpl: TemplateRef<any>;
this._dataTableService.rowDetailsTemplate = this.rowDetailsTmpl;
In my code input text field using inside ng-template , i set id and name dynamically , but when i change value in textbox it automatically reflect to other input fields. so how to solve this problem in angular7.
in component define the model like array:
selectedOperatorEmail: Array<any> = [];
in html define ngModel define like this:
[(ngModel)]="selectedOperatorEmail[row.DeskId]"

Add class dynamically from controller

I'm trying to add a class to my element based on whether it's required and empty field. Not sure what I'm doing wrong. My console.log is getting printed but the class is not assigned.
This is my html
<div ng-repeat="supplier in vm.exportSuppliers" class="row">
<div class="col-xs-5">
<label ng-show="vm.exportSuppliers[$index].exportSupplier" class="control-label" for="es{{$index}}+Ref">Agreement Reference *</label>
<label ng-hide="vm.exportSuppliers[$index].exportSupplier" class="control-label" for="es{{$index}}+Ref">Agreement Reference</label>
<input class="form-control" id="es{{$index}}+Ref" type="text"
ng-model="supplier.agreementReference" ng-change="vm.addExportSupplier()"
ng-blur="vm.requiredField('es'+$index+'+Ref')"
ng-required="vm.exportSuppliers[$index].exportSupplier">
</div>
</div>
and my function to make sure field is required and not filled in
vm.requiredField = requiredField;
function requiredField (id) {
if($scope.vm.exportSupplier.form.$error.required) {
for (var i = 0; i < $scope.vm.exportSupplier.form.$error.required.length; i++) {
if ($scope.vm.exportSupplier.form.$error.required[i].$$attr.id == id) {
console.log('invalid required field');
var myEl = angular.element( document.querySelector( id ) );
myEl.addClass('top40');
}
}
}
}
What's wrong with my addClass?
Have a look at the below question. I think you could improve your approach by making use of the ternary operators in ng-class.
angular ng-class if-else expression
You can add conditional classes by using ng-class and form properties provided by angularjs
You need to add "name" attribute on form and specify novalidate on it
<input class="form-control" id="es{{$index}}+Ref" type="text"
name="inputName"
ng-class="{'error-class': formName.inputName.$invalid && formName.inputName.$touched}"
ng-model="supplier.agreementReference" ng-change="vm.addExportSupplier()"
ng-required="vm.exportSuppliers[$index].exportSupplier">
I think this will solve your purpose. No need to write blur function in controller.
There are two ways to handle this situation.
Option 1:
<div ng-class="{'class1': obj.success, 'classB': obj.error}"></div>
Option 2:
<div class="{{obj.classes}}"></div>
The only problem with this approach is that updating the template could become an issue. Sometimes the controller may not update the template. If that is the case, use $scope.$apply(); after the change has been made.

How to clear a form within an angular app that appears on click?

I have seen several tutorials on how to clear angular (version 1.4.0) forms, however.. none of them seem to work in my case. I am puzzled. The following form is display: none; to begin with, but comes into existence when the 'Add' button is clicked.
<div id="..." class="displayNone ...">
<form class="..." name="addFooForm">
<div class="...">
<div class="row">
<div class="col-md-offset-3 col-md-6">
<br>
<div class="form-group">
<label>Foos</label>
<input type="text" class="..." id="..." ng-model="foo.value">
</div>
</div>
</div>
</div>
<div class="...">
...
<input type="button" value="Cancel" class="..." ng-click="resetAddFooForm()">
</div>
</form>
</div>
And in my controller, I have the following.
$scope.resetAddFooForm = function () {
// XXX:
console.log('about to reset the form');
$scope.foo = {};
// $scope.addFooForm.$setValidity();
// $scope.addFooForm.$setPristine();
// $scope.addFooForm.$setUntouched();
};
But, although the console.log message is being displayed. The form field is not being cleared. I have have even tried doing it directly as follows.
$scope.resetAddFooForm = function () {
// XXX:
console.log('about to reset the form');
$scope.fooVal = '';
// $scope.addFooForm.$setValidity();
// $scope.addFooForm.$setPristine();
// $scope.addFooForm.$setUntouched();
};
.. with the above html modified as follows.
<div class="form-group">
<label>Foos</label>
<input type="text" class="..." id="foo" ng-model="fooVal">
</div>
But nothing seems to be working. Is it perhaps because the $scope is different due to the form being dynamically inserted? But then how do I tackle that?
I just want to be able to clear the fields (and also clear the angular properties like dirty/pristine, etc) of this dynamically generated form.
Update:
I was able to send this from the html back to the controller, and then use it do what I wanted. So it appears that the problem may actually be to do with differing scopes.
<div class="...">
...
<input type="button" value="Cancel" class="..." ng-click="resetAddFooForm(this)">
</div>
And then have this retrieved the controller.
$scope.resetAddFooForm = function (elem) {
elem.foo = {};
elem.$setValidity();
elem.$setPristine();
elem.$setUntouched();
};
Any hints? Why do I need to send this, when I should really be able to simply use $scope. Why doesn't that work?
Initially declare empty object on your controller
$scope.foo = {}
then try empty it on reset
You can manually reset fields value.

Incorporate directive's functionality into Angular form validation

I have two questions that are related:
First: I have the following directive, who's purpose is to validate whether an input[type=file] is valid or not, however I have no idea how it does it least of all, what the actual code means, here it is:
angular.module('sccateringApp')
.directive('fileModel', ['$parse', function($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
Like I said, I have no idea what the above code actually does, the explanation I got from the forum where I copied that was that it validated an input type file. Is this correct? (So far I haven't been able to verify if it works or not since it doesn't work with the code I'm using at the moment to validate my forms).
Second: Having the form below, using angular form validation it doesn't allow the submit button to be clicked until the actual inputs inside the form match the validation rules (enter a name for the category, and the description should have a max length of 144 characters). I included the directive into the file input, however the actual ng-model for the form ignores the required in the input type file and just verifies the rules are met for the first two inputs.
Here is my form:
<form method="post" role="form" name="newCategoryForm" ng-submit="submitForm()" enctype="multipart/form-data" novalidate>
<div class="row">
<div class="row">
<div class="col s12">
<div input-field>
<input type="text" name="cat-name" id="cat-name" ng-class="{ 'ng-invalid' : newCategoryForm.catname.$invalid && !newCategoryForm.catname.$pristine }"
ng-model="catname" required>
<label>Nombre</label>
</div>
</div>
</div>
<div class="row">
<div class="col s12">
<div input-field>
<textarea class="materialize-textarea" name="cat-description" id="cat-description" length="144"
ng-model="catdescription" ng-maxlength="144" required></textarea>
<label>Descripción</label>
</div>
</div>
</div>
<div class="row">
<div class="col s12">
<h6>Imagen de Fondo</h6>
<div class="file-field input-field">
<div class="btn pink darken-2 waves-effect waves-light">
<span>Archivo</span>
<input type="file" name="cat-bgimg" id="cat-bgimg"
file-model="variable" required>
</div>
<div class="file-path-wrapper">
<input class="file-path" type="text">
</div>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-large pink darken-2 waves-effect waves-light center-button" ng-disabled="newCategoryForm.$invalid">Crear Categoría</button>
</form>
The first two inputs get validated correctly, the third one (file input) doesn't and I don't really know why since the directive got included on the input (I know natively, ngModel doesn't validate file inputs).
Any ideas or suggestions of how can I fix this? I'm really new to Angular, and all the tutorials are pretty much useless. I come from 5 years of experience working on jQuery, and the transition to Angular hasn't been easy at all.
The directive posted above is used to make the submit get the data found in the <input type="file"></input>.
Also, a variable should be initialized in the controller so that the values found inside the form are copied to said variable, then this variable needs to be sent as a parameter inside the ng-submit="submitForm().
Example:
angular.module('sccateringApp')
.controller('newSubcategoryController', function (httpcalls, $scope) {
...
$scope.subcategory = [];
...
$scope.submitForm = function(subcategory){
...
$scope.request.insertSubcategory(subcategory);
}
});
Each ng-model inside the form would be:
<input type="text" ng-model="category.name">
So that the category variable found in the controller acquires that value.

ng-repeat model binding (for adding dymaic created emails)

Below code show binding for a List emails but I am having trouble binding the newly added emails to the $scope.emails (does not contain the new email user added). Any idea?
// emails is a List on server side
// email is a string
so i bind ng-model= email but
but doing the below does not work
$scope.contactInformation.Emails.push(email); --> complains about duplicates
<div ng-repeat="email in emails">
<div class="form-group">
<label for="email">Email</label>
<div class="input-group input-group-sm input-group-minimal">
<span class="input-group-addon">
<i class="linecons-mail"></i>
</span>
<input type="text" class="form-control" ng-model="email" />
</div>
<button class="btn btn-primary" ng-show="$last" ng-click="AddEmail()">Add Email</button>
Controller.js
// modelParams.ContactInformation.Emails = new List(string)() when retrieved on server side
$scope.emails = modelParams.ContactInformation.Emails;
$scope.AddEmail = function () {
$scope.contactInformation.Emails.push({ email: null });
};
I'm amending the answer to avoid confusion... Here's is how it should be done - fill in the blanks to fit it for your scenario.
controller.js
// assuming emails is an array of strings (whether defined locally or from a server)
$scope.emails = ["email1", "email2", ...];
$scope.addEmail = function(){
$scope.emails.push(""); // push an empty array as new not-yet-set email
}
The HTML is largely correct. I would have moved the "add" button to outside of the ng-repeat div instead of relying on $last:
<div ng-repeat="email in emails track by $index">
<input type="text" ng-model="email" />
</div>
<button ng-click="addEmail()">Add Email</button>
EDIT:
The example above would, actually, not work because the ng-model within ng-repeat binds to a primitive (string) email.
There are 2 ways to fix:
approach 1
Make an array of objects. If you get an array of strings from the server, you'd need to convert to an array of objects, like so:
$scope.emails = [];
angular.forEach(arrayOfEmailStrings, function(v, k){ $scope.emails.push({value: v}); });
And access like so:
<input type="text" ng-model="email.value" />
approach 2
Use the $index property:
<input type="text" ng-model="emails[$index]" />

Categories