How do I show angularjs material validation for another model property? - javascript

Using angularjs and material, I want to be able to have a readonly textbox that displays the name for a selected object a user looks up (via a modal popup), but the textbox validation should show as required and fire off if a separate id property is not populated. Here is an example plnkr.
I was originally thinking that I could do this simply by adding a hidden field with an ng-model, name, and required attribute, it would create the associated form property for the field with required validator (which it does), and I would be able to show the validator on the readonly textbox like so:
<form name="myCtrl.myForm" novalidate>
<input type="hidden" ng-model="myCtrl.id" name="id" required />
<div layout="row">
<md-input-container flex="50">
<label>Selected Object</label>
<input ng-model="myCtrl.selectedObject.selectedText" readonly />
<div ng-messages="myCtrl.myForm.id.$error">
<div ng-message="required">Please select an object.</div>
</div>
</md-input-container>
<div>
<md-button class="md-icon-button md-primary" ng-click="myCtrl.select($event)">
<md-tooltip md-direction="top">
Select Object
</md-tooltip>
<md-icon>search</md-icon>
</md-button>
</div>
</div>
<div>
<md-button class="md-raised md-primary" type="submit">Submit</md-button>
</div>
</form>
JS:
vm.select = function(evt) {
// Set the selected Object
vm.selectedObject = { selectedText: "Object id 1 selected", id: 1 };
// Set the associated ID
vm.id = 1;
};
However, the <div ng-message="required">Please select an object.</div> never displays when the form is submitted and validation fires. Any idea how I can accomplish this?

While I was typing up this question I had an idea - perhaps I should be creating a custom validator that I can apply to this input which references a separate property. That appeared to do what I needed. Here's the plnkr and here's the directive:
angular.module('MyApp', ['ngMessages', 'ngMaterial'])
.directive('requiredOther', RequiredOther);
function RequiredOther() {
return {
require: "ngModel",
scope: {
requiredOtherValue: "=requiredOther"
},
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.requiredOther = function(modelValue) {
return scope.requiredOtherValue !== undefined && scope.requiredOtherValue !== null && scope.requiredOtherValue !== '';
};
scope.$watch("requiredOtherValue", function() {
ngModel.$validate();
});
}
};
}
This is the updated HTML:
<form name="myCtrl.myForm" novalidate>
<input type="hidden" ng-model="myCtrl.id" />
<div layout="row">
<md-input-container flex="50">
<label>Selected Object</label>
<input name="id" ng-model="myCtrl.selectedObject.selectedText" readonly required-other="myCtrl.id" />
<div ng-messages="myCtrl.myForm.id.$error">
<div ng-message="requiredOther">Please select an object.</div>
</div>
</md-input-container>
<div>
<md-button class="md-icon-button md-primary" ng-click="myCtrl.select($event)">
<md-tooltip md-direction="top">
Select Object
</md-tooltip>
<md-icon>search</md-icon>
</md-button>
</div>
</div>
<div>
<md-button class="md-raised md-primary" type="submit">Submit</md-button>
</div>
</form>
The required-other="myCtrl.id" directive references the id property and watches for changes and fires off validation on change:
I guess I don't really need the hidden input field anymore either.

Related

How to set the required field for md-autocomplete dynamically

trying to use md-autocomplete inside a custom directory.
I wanted the field to be required based on two conditions. One which is initialized(cond1) and one that changes(cond2)
I've tried to do it like this:
required="{{ cond1 && !cond2 }}"
But for some reason the md-autocomplete always becomes required, even if cond1 is initially undefined or false. It also doesn't respond to changes done to the value of cond2.
The validator also doesn't check if the selected-item has been set. It just checks if there is text in the field.
ng-required doesn't seem do anything.
Any ideas how i can make this work?
EDIT:
Here is my directive's .html
<div ng-form="dummyForm">
<md-autocomplete
md-input-name="{{ctrl.fieldName}}"
md-selected-item="ctrl.dummyItem"
md-selected-item-change="ctrl.setSelectedItem()"
md-search-text="ctrl.searchText"
md-items="item in ctrl.querySearch(ctrl.searchText)"
md-item-text="item.display || item"
md-min-length="0"
placeholder="{{ ctrl.placeholder }}"
ng-disabled="ctrl.selectAll || ctrl.isDisabled"
required="{{ ctrl.isRequired && !ctrl.selectAll }}">
<md-item-template>
<span>{{ item.display || item }}</span>
</md-item-template>
<md-not-found>
No results.
</md-not-found>
</md-autocomplete>
</div>
<div class="label-wrapper">
<label
class="checkbox-label"
ng-if="ctrl.selectAll !== null">
<input
type="checkbox"
ng-model="ctrl.selectAll"
ng-change="ctrl.checkSelectAll()">
{{ ctrl.checkboxName ? ctrl.checkboxName : 'Select all' }}
</label>
</div>
It is basically just a custom input field that lets the user either select a value from the list OR click the checkbox to "select all" items. When the checkbox is selected, i don't want the md-autocomplete field to be required anymore.
It does work fine with ng-required as you can see in this demo plnkr.
View
<form name="myForm">
<md-autocomplete
md-autoselect=true
placeholder="What is your favorite place?"
md-item-text="item.display"
md-items="item in ctrl.items"
md-menu-class="autocomplete-custom-template"
md-min-length="2"
md-delay="ctrl.throttle"
md-search-text="ctrl.searchText"
md-search-text-change="ctrl.searchTextChange(ctrl.searchText)"
md-select-on-match=true
md-match-case-insensitive=true
md-selected-item-change="ctrl.selectedItemChange(item)"
ng-required="ctrl.cond1 && !ctrl.cond2"
md-selected-item="ctrl.selectedItem">
{{ myForm.$error }}
<div>
<md-button type="submit">Submit</md-button>
</div>
</form>
AngularJS controller
myApp.controller('MyCtrl', function ($scope) {
var vm = this;
this.cond1 = true;
this.cond2 = false;
});

textbox validation for number and required in repeating mode angular js

Please refer below link
https://plnkr.co/edit/9HbLMBUw0Q6mj7oyCahP?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.NDCarray = [{val: ''}];
$scope.NDCadd = function() {
$scope.NDCarray.unshift(
{val: ''}
);
};
$scope.data = angular.copy($scope.NDCarray);
$scope.NDCcancel=function(){debugger
$scope.NDCarray=$scope.data;
}
$scope.NDCdelete = function(index) {
if(index != $scope.NDCarray.length -1){
$scope.NDCarray.splice(index, 1);
}
};
});
It contains the textbox with add button. I have added validation for number and required field, it is working fine. but when i click add button it will create another textbox with entered value that time it showing the validation message for all the textboxes , i don't want to show validation message for all the textboxes. need to show validation for corresponding textbox only. that means when i enter something wrong in second textbox it is showing message to that textbox only.refer below screenshot.
validation message displaying for all textboxes.that should display for only one textbox.
Working plnkr : https://plnkr.co/edit/f4kAdZSIsxWECd0i8LDT?p=preview
Your problem is in your HTML, to get independant fields you must :
Move outside the form of the ng-repeat
Provide a dynamic name using $index on your fields, because name is what make each fields independant on the validation.
Here is the final HTML from the plnkr i didn't touch at all the javascript :
<body ng-controller="MainCtrl">
<form name="myForm">
<div ng-repeat ="ndc in NDCarray">
<div class="col-sm-4 type7" style="font-size:14px;">
<div style="margin-bottom:5px;">NDC9</div>
<label>Number:
<input type="number" ng-model="ndc.value"
min="0" max="99" name="{{'input_'+$index}}" required>
</label>
<div role="alert">
<span class="error" ng-show="myForm.input.$dirty && myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.number">
Not valid number!</span>
</div>
<tt>value = {{example.value}}</tt><br/>
<tt>myForm['input_{{$index}}'].$valid = {{myForm['input_'+$index].$valid}}</tt><br/>
<tt>myForm['input_{{$index}}'].$error = {{myForm['input_'+$index].$error}}</tt><br/>
</div>
<div class="col-sm-4 type7 " style="font-size:14px;">
<div style="padding-top:20px; display:block">
<span class="red" id="delete" ng-class="{'disabled' : 'true'}" ng-click="NDCdelete($index)">Delete</span>
<span>Cancel </span>
<span id="addRow" style="cursor:pointer" ng-click="NDCadd()">Add </span>
</div>
</div>
</div>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</body>
Couple of changes:
If you add "track by $index" to your ng-repeat it will make each group of elements unique so that you don't have to worry about deriving unique names for elements.
Your validation on the number (myForm.ndcValue.$error.number) didn't work so I changed it to myForm.ndcValue.$error.max || myForm.ndcValue.$error.min
Also, you can throw an ng-form attribute directly on the div with your ng-repeat.
Like this:
<div ng-repeat="ndc in NDCarray track by $index" ng-form="myForm">
<div class="col-sm-4 type7" style="font-size:14px;">
<div style="margin-bottom:5px;">NDC9</div>
<label>Number:
<input type="number" ng-model="ndc.value" min="0" max="99" name="ndcValue" required>
</label>
<div role="alert">
<span class="error" ng-show="myForm.ndcValue.$dirty && myForm.ndcValue.$error.required">
Required!</span>
<span class="error" ng-show="myForm.ndcValue.$error.max || myForm.ndcValue.$error.min">
Not valid number!</span>
</div>
<tt>value = {{example.value}}</tt>
<br/>
<tt>myForm.ndcValue.$valid = {{myForm.ndcValue.$valid}}</tt>
<br/>
<tt>myForm.ndcValue.$error = {{myForm.ndcValue.$error}}</tt>
<br/>
</div>
Here's the working plunker.
I changed the input element name from "input" to "ndcValue" to be less confusing.

Angular JS /Angular Material .How to validate Checkbox group on click?

I am using Angular Material and i am doing a simple validation with checkboxes.
The Problem :
At least One checkbox should be checked.
1-If i click a checkbox and then i click again,the "required message" is not shown.
2-If i click a checkbox and then i click again,and press "Tab Key" going throught each checkbox pressing Tab,when i reach the last one and press Tab Key,the message "Required" appears.
I want that ,condition number 1 occurs too,and one more thing ,i dont want to show the "required" message when the application loads. Here is my example : https://codepen.io/anon/pen/zrjjyL
I Hope you can give me a hand,Thanks!!!!!
Code :
HTML:
<form name="myForm" ng-app="myApp" ng-controller="myController"
class="container-fluid" ng-submit="submit()">
<div class="row">
<div class="col-xs-8">
<md-input-container md-no-float>
<label>Name *</label>
<input required="" name="nombre" ng-model="model.nombre">
<div ng-messages="myForm.nombre.$error" ng-
show="myForm.nombre.$touched || myForm.$submitted">
<div ng-message="required">Required.</div>
</div>
</md-input-container>
<md-input-container md-no-float>
<label>Search Options *</label>
<input type="text" id="query" ng-model="query"/>
</md-input-container>
<md-input-container md-no-float>
<md-list-item ng-repeat="item in items | filter:query">
<p style ="font-size: 1.3rem;font-family: Roboto,'Helvetica
Neue',sans-serif"> {{ item.title }} </p>
<md-checkbox name ="permiso" ng-
model="model.opciones[item.id]['checked']" ng-required="!isChecked" ></md-
checkbox>
</md-list-item>
<div ng-messages="myForm.permiso.$error" ng-show=" myForm.permiso.$touched
|| myForm.$submitted">
<div ng-message="required">Required.</div>
</md-input-container>
</div> </div>
JS:
var app = angular.module('myApp', ['ngAnimate', 'ngAria', 'ngMaterial',
'ngMessages'])
.controller('myController', function($scope) {
$scope.items = [{
id: 1,
title: 'Item A'
}, {
id: 2,
title: 'Item A'
}, {
id: 3,
title: 'Item C'
}];;
$scope.model = {}
$scope.model.opciones = {};
$scope.isChecked = false;
$scope.$watch('model.opciones', function(selected) {
$scope.isChecked = false;
angular.forEach(selected, function(selectedItem) {
if (selectedItem.checked) $scope.isChecked = true;
});
}, true);
});
You can check if the form has been modified with formName.$dirty. Then you can only show the error if the form is actually dirty.
<div ng-message="required" ng-if="myForm.$dirty">Required.</div>
You are iterating over your items and overwriting the myFrom.permiso with each new item which means myForm.permiso is always the model of the last item in your items. That is why, when you tab through them, only the last change registers.
You should remove the name attribute of the checkboxes and add a ng-change handler instead of the $watch.
And you add a hidden checkbox for the permiso model which you update on change.
https://codepen.io/kuhnroyal/pen/LGrzGv

Angular setPristine on ngBlur

I have a form with password field for which I implemented a password directive.
I currently implemented only 1 validation but I've a list of validations on the field.
I want them to displayed in either red or green depending on valid/invalid respectively - when the user starts typing into the field. If the users control goes out of the box AND all validations are passed, I want to set the field to valid and set it to pristine so that the validation list won't show up.
However if any of the validations fail, I want all of them to be seen even if the field is out of focus. Below is my snippet for the form group.
<div class="form-group">
<label for="inputPass" class="col-sm-3 control-label text-sm text-left">Password</label>
<div class="col-sm-9">
<input type="password" placeholder="Password"
class="form-control" ng-model="registerAccount.password"
required name="inputPass" id="inputPass"
password ng-blur="form.inputPass.$invalid ? return: form.inputPass.$setPristine">
<div ng-show="form.inputPass.$dirty" class="help-block">
<p class="text-danger" ng-show="form.inputPass.$error.required">
Password is required.
</p>
<p ng-class="form.inputPass.$error.invalidLength ? 'text-danger' : 'text-success'">
Password should be atleast 8 characters.
</p>
</div>
</div>
</div>
Following is my directive
'use strict';
angular.module('nileLeApp')
.directive('password', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function ($scope, $element, $attrs, ngModelCtrl) {
$scope.$watch($attrs.ngModel, function (value) {
if (value) {
var length = (value.length >= 8);
ngModelCtrl.$setValidity('invalidLength', length);
}
});
}
};
});
When the focus out of the field, the validation list is still showing up. I was expecting it to be hidden because the field is being set to pristine. Any ideas ? I wanted it to be similar to password field in https://dl.dropboxusercontent.com/u/636000/password_verification/index.html. As the user types the password, the validations are reflected in red/green.
You are not calling $setPristine method. Should be form.inputPass.$setPristine():
ng-blur="form.inputPass.$invalid ? return: form.inputPass.$setPristine()"
or cleaner variation:
ng-blur="form.inputPass.$valid && form.inputPass.$setPristine()"
You may not need to use a special directive:
<form name="testForm">
<input ng-model="testVal" name="testVal" required ng-minlength="3" ng-maxlength="10" ng-pattern="/^\w+$/">
<div ng-show="testForm.$invalid">
<div ng-class="{'errors':testForm.testVal.$error.required,'success':!testForm.testVal.$error.required}">Required</div>
<div ng-class="{'errors':testForm.testVal.$error.minlength,'success':!testForm.testVal.$error.minlength}">Minlength</div>
<div ng-class="{'errors':testForm.testVal.$error.maxlength,'success':!testForm.testVal.$error.maxlength}">Maxlength</div>
<div ng-class="{'errors':testForm.testVal.$error.pattern,'success':!testForm.testVal.$error.pattern}">Pattern</div>
</div>
</form>
This code does the same thing and given you the link: dl.dropboxusercontent.com/u/636000/password_verification/index.html
If you need to establish a more sophisticated checks, you can use the following directive use-form-error:
Live example on JsFiddle
<form name="testForm">
<input ng-model="testVal" name="testVal" required ng-minlength="3" ng-maxlength="10" ng-pattern="/^\w+$/" use-form-error="containWow" use-error-expression="testVal.indexOf('wow')>-1">
<div ng-show="testForm.$invalid">
<div ng-class="{'errors':testForm.testVal.$error.required,'success':!testForm.testVal.$error.required}">Required</div>
<div ng-class="{'errors':testForm.testVal.$error.minlength,'success':!testForm.testVal.$error.minlength}">Minlength</div>
<div ng-class="{'errors':testForm.testVal.$error.maxlength,'success':!testForm.testVal.$error.maxlength}">Maxlength</div>
<div ng-class="{'errors':testForm.testVal.$error.pattern,'success':!testForm.testVal.$error.pattern}">Pattern</div>
<div ng-class="{'errors':testForm.testVal.$error.containWow,'success':!testForm.testVal.$error.containWow}">It's 'wow' contains</div>
</div>
</form>

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.

Categories