Question: How can I show the validation error using only the ng-model if I cannot name the form and its elements.
I have a html form to collect credit card details. To prevent the credit card data from touching my server, I cannot name the form elements. So my form looks like:
<form ng-submit="vm.processForm()">
<div class="form-row">
<label>
<span>Card Number</span>
<input type="text" size="20" data-stripe="number" ng-model="vm.number" required>
</label>
</div>
<div class="form-row">
<label>
<span>Expiration (MM/YY)</span>
<input type="text" size="2" data-stripe="exp_month" ng-model="vm.exp_month" required>
</label>
<span> / </span>
<input type="text" size="2" data-stripe="exp_year" ng-model="vm.exp_year" required>
</div>
<div class="form-row">
<label>
<span>CVC</span>
<input type="text" size="4" data-stripe="cvc" ng-model="vm.cvc" required>
</label>
</div>
<input type="submit" class="submit" value="Submit Payment">
</form>
Usually in Angular, I used to check validation using the form elements name, for example like this:
<p ng-show="userForm.creditcard.$error.required">Your credit card number is required.</p>
But since I cannot name the form and its elements, how can I show the validation error using only the ng-model? Because, the following doesn't work:
<p ng-show="vm.number.$error.required">Your credit card number is required.</p>
I am using Angular v1.4.8.
I created a directive to export de the model controller. I don't think that is the best way but it works.
.directive('exportModel', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
attr.$observe('exportModel', function (value) {
scope[value] = ngModel;
})
}
}
})
http://jsfiddle.net/Lvc0u55v/11352/
Related
I have an angular directive to facilitate the adding of bootstrap classes at runtime to streamline the need to apply "form-group", "control-label" and "form-control". This works perfectly as long as I don't try to include multiple levels at once, meaning that I cannot seem to make this work to include multiple divs in "form-group. I have attached the code, raw HTML and processed HTML for review to see if someone might have some insight into how I might modify to make this tool meaningful.
Directive:
(function () {
"use strict";
angular.module("ppac")
.directive("formfix", formfix);
var addClasses = function (element) {
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "checkbox" && type !== "radio") {
input.classList.add("form-control");
}
var label = element.querySelector("label");
label.classList.add("control-label");
element.classList.add("form-group");
};
function formfix() {
return {
restrict: "A",
link: function (scope, element) {
addClasses(element[0]);
}
}
}
})();
HTML Form:
<form name="contactForm" ng-submit="model.submit()" novalidate>
<div class="form-horizontal">
<div formfix>
<label for="firstName" class="col-md-2">First Name</label>
<div class="col-md-3">
<input type="text" name="firstName" id="firstName" ng-model="model.contact.firstName" />
</div>
<label for="lastName" class="col-md-2">Last Name</label>
<div class="col-md-3">
<input type="text" name="lastName" id="lastName" ng-model="model.contact.lastName" />
</div>
</div>
</div>
</form>
Processed HTML:
<form name="contactForm" ng-submit="model.submit()" novalidate="" class="ng-pristine ng-valid">
<div class="form-horizontal">
<div formfix="" class="form-group">
<label for="firstName" class="col-md-2 control-label">First Name</label>
<div class="col-md-3">
<input type="text" name="firstName" id="firstName" ng-model="model.contact.firstName" class="ng-pristine ng-valid form-control ng-empty ng-touched">
</div>
<label for="lastName" class="col-md-2">Last Name</label>
<div class="col-md-3">
<input type="text" name="lastName" id="lastName" ng-model="model.contact.lastName" class="ng-pristine ng-untouched ng-valid ng-empty">
</div>
</div>
</div>
</form>
You can use ng-class to add class dynamically as the document says
The ngClass directive allows you to dynamically set CSS classes on an
HTML element by databinding an expression that represents all classes
to be added.
Reference
https://docs.angularjs.org/api/ng/directive/ngClass
I'm adding validation to the user registration page in my app and I'm trying to use ngMessages and the build-in angularjs validation.
I'm experiencing strange behaviour for password matching and with patterns in general.
Here's the code for password matching:
<span>Password (6 to 16 character)</span>
<span class="item item-input">
<input type="password"
ng-model="userRegistration.password"
placeholder="password"
name="password"
ng-minlength="6"
ng-maxlength="16"
required
ng-class="{'invalid-input': userRegistrationForm.password.$touched && userRegistrationForm.password.$invalid }">
</span>
<!-- Form validation messages -->
<div role="alert" class="error-message" ng-messages="userRegistrationForm.password.$error" ng-show="userRegistrationForm.password.$touched">
<p ng-message="required">This field is required</p>
<p ng-message="minlength">This field is too short</p>
<p ng-message="maxlength">This field is too long</p>
</div>
<span>Confirm Password</span>
<span class="item item-input">
<input type="password"
placeholder="confirm password"
ng-model="userRegistration.passwordConfirmation"
name="confpass"
ng-minlength="6"
ng-maxlength="16"
ng-pattern="/^{{userRegistration.password}}$/"
required
ng-class="{'invalid-input': userRegistrationForm.confpass.$touched && userRegistrationForm.confpass.$invalid }">
</span>
<!-- Form validation messages -->
<div role="alert" class="error-message" ng-messages="userRegistrationForm.confpass.$error" ng-show="userRegistrationForm.confpass.$touched">
<p ng-message="required">This field is required</p>
<p ng-message="pattern">Password not matching!</p>
</div>
The problem is that even If the password matches, the error stays there displayng that it is not matching and I cannot figure out why is it doing this.
Another example of pattern not behaving as expected can be found in the code below:
<span>System ID</span>
<span class="item item-input">
<input type="text"
ng-model="userRegistration.id"
placeholder="system id"
name="systemID"
ng-minlength="6"
ng-maxlength="6"
pattern="/:/"
required
ng-class="{'invalid-input': userRegistrationForm.systemID.$touched && userRegistrationForm.systemID.$invalid }">
</span>
<!-- Form validation messages -->
<div role="alert" class="error-message" ng-messages="userRegistrationForm.systemID.$error" ng-show="userRegistrationForm.systemID.$touched">
<p ng-message="required">This field is required</p>
<p ng-message="pattern">Colons not required!</p>
<p ng-message="minlength">This field is too short</p>
<p ng-message="maxlength">This field is too long</p>
</div>
I need to check if colons are added in the input, and display and error if they are. The proble, as above, is that even if colons are not there, the message is still displayed.
What am I missing? I'm clearly using ng-pattern and ng-messages wrong, but can't understand why.
Any help would be really appreciated.
Codepen with both examples here: http://codepen.io/NickHG/pen/NNZYwK?editors=1010
Thanks
I used a little different approach to get this to work with ng-messages. I made a custom directive like this:
.directive('compareTo', function() {
return {
require: "ngModel",
scope: {
otherModelValue: "=compareTo"
},
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.compareTo = function(modelValue) {
return modelValue == scope.otherModelValue;
};
scope.$watch("otherModelValue", function() {
ngModel.$validate();
});
}
};
});
And display the error with it like this:
<div role="alert" class="error-message"
ng-messages="userRegistrationForm.conf.$error"
ng-show="userRegistrationForm.conf.$touched">
<p ng-message="required">This field is required</p>
<p ng-message="compareTo">Password not matching!</p>
</div>
For further information and a demo please see the link below.
Codepen demo: http://codepen.io/thepio/pen/rLNBWr?editors=1010
EDIT based on comment:
Here is an example with using ng-pattern (no directive needed): http://codepen.io/thepio/pen/zBYOPy?editors=1010
I set a ng-change function to your first input and set a regexp in the controller when it's changed. Then I bind it to the ng-pattern of the confirmation input and show the ng-messages accordingly.
I'm a total beginner in Angular, so maybe I am thinking about it totally wrong.
What I'm trying to do is create a directive that wraps an input in some boilerplate html. It should bind the input's ng-model to the parent scope (the myForm controller's scope) but should have access to the validation state of the input.
Here's my setup:
parent_form.html
<form name="myForm">
<div fieldContainer label="'Field 1'" model="'field1'">
<input type="text" ng-model="field1" required/>
</div>
<div fieldContainer label="'Field 2'" model="'field2'">
<input type="text" ng-model="field2" required/>
</div>
</form>
field_container.html
<div class="row">
<div class="col-md-5">{{label}}</div>
<div class="col-md-5" ng-transclude></div>
<div class="col-md-2" ng-show="valid">REQUIRED!</div>
</div>
parent_form.js
angular.module('myApp')
.controller('myForm', function ($scope) {
$scope.field1 = '';
$scope.field2 = '';
})
.directive('fieldContainer', function () {
return {
restrict: 'A',
templateUrl: 'field_container.html',
transclude: true,
scope: {
label: '=label',
model : '=model'
}
}
});
I got it working by:
Adding a name to the field (so that Angular's Form validation takes over)
Setting ng-show on the validation message to be: $parent.myForm[model].$valid
So apparently the transcluded input still uses the parent (myForm) scope and the directive has access to the parent's scope via scope.$parent.
Not sure if this is the cleanest way to do it...
Final code looks like:
parent_form.html
<form name="myForm">
<div fieldContainer label="'Field 1'" model="'field1'">
<input type="text" name="field1" ng-model="field1" required/>
</div>
<div fieldContainer label="'Field 2'" model="'field2'">
<input type="text" name="field2" ng-model="field2" required/>
</div>
</form>
field_container.html
<div class="row">
<div class="col-md-5">{{label}}</div>
<div class="col-md-5" ng-transclude></div>
<div class="col-md-2" ng-show="$parent.myForm[model].$valid">REQUIRED!</div>
</div>
I am using angular js in my application, and I have a form which contains a lot of fields:
<form>
<div class="form-group">
<label for="idname">Name:</label>
<input type="name" class="form-control" id="idname" ng-model="name" name="name">
</div>
<div class="form-group">
<label for="idemail">Email Address:</label>
<input type="password" class="form-control" id="idemail" ng-model="email" name="email">
</div>
<div class="form-group">
<label for="idtype">Choose Type:</label>
<select class="form-control" id="idtype" name="type">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
There are almost 60+ fileds, and most of the markups is bootstrap styles related, so I want to simplify the markups, like this:
<form>
<input label="Name:" type="text" model="name">
<input label="Email Address:" type="email" model="email">
<select label="Choose Type:" model="type">
....
</select>
<button type="submit" class="btn btn-default">Submit</button>
</form>
I want to add some extra attributes like label and model for the form inputs, then I will generate the label and input element and wrap them with the bootstrap styles. Also I want the third directives like min-length or anything else still take effect.
I tried to create the directive like this:
.directive('label', function() {
return {
priority:1,
restrict: 'A',
templateUrl: "field.html",
replace:true
};
})
However it does not work as expected, the custom-ed element like
<input label="Name:" type="text" model="name">
are not inserted into the template, and the label is not generated:
<div class="form-group">
</div>
I think the compile and link may be necessary, but I have no idea how to implement them.
This is the demo.
Is it possible to fix it?
Replace is deprecated since Angular 1.3.
But it works: https://jsfiddle.net/basslagter/yh0qdbhL/1/
What you can do (because of the deprecation) is make a custom element:
<custom-label label="Name:" type="text" model="name"></custom-label>
Defined as:
var myApp = angular.module('myApp', []);
myApp.directive("customLabel", function () {
return {
restrict: "E",
template: '<label>The label was: {{vm.label}}</label>',
controllerAs: 'vm',
scope: { label: '#' },
bindToController: true,
controller: function(){}
};
});
See: https://jsfiddle.net/basslagter/yh0qdbhL/
I have built a from and trying to implement validation process with angularjs. At my source code, you will see I have three input fields:
Email
Password
Confirm Password
At Email, I can show a 'Required message' and a 'Custom Message' by doing this:
<input type="email" class="form-control" id="inputEmail" name="inputEmail" placeholder="Email" ng-model="email" required>
<p ng-show="createLogin.inputEmail.$error.required">This field is required</p>
<p ng-show="createLogin.inputEmail.$error.email">Enter a valid email<p>
This is completely fine. But, following the same procedure at other two input fields(Password & Confirm Password), I can't show the 'Custom Messages'! Only 'Required Messages' are shown. How can I fix this? This is the code which I have used for password:
<input type="password" class="form-control" id="createPassword" name="createPassword" placeholder="Password" ng-model="password" password-validate required>
<div ng-show="createLogin.createPassword.$error.required"><p>This field is required</p></div>
<div ng-show="createLogin.createPassword.$error.password">
<p>Custom Messages:</p>
</div>
And for Confirm Password:
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="Password" ng-model="verifyPass" required data-password-verify="password">
<p ng-show="createLogin.confirmPassword.$error.required">This field is required</p>
<p ng-show="createLogin.confirmPassword.$error.verifyPass">Password don't match<p>
So, how can I show the Custom Messages for Password and Confirm Password filed too like Email filed?
Here is my Plunker work
Since your custom directive set validation key to pwd (ctrl.$setValidity('pwd', true)) you need to use it in HTML in expressions like:
<div ng-show="createLogin.createPassword.$error.pwd">
<p>Password must meet the following requirements:</p>
<ul>
<li ng-class="pwdHasLetter">At least <strong>one letter</strong></li>
<li ng-class="pwdHasNumber">At least <strong>one number</strong></li>
<li ng-class="pwdValidLength">Be at least <strong>8 characters</strong></li>
</ul>
</div>
The same issue with password verification field, key should be passwordVerify:
<p ng-show="createLogin.confirmPassword.$error.passwordVerify">Password don't match</p>
Demo: http://plnkr.co/edit/zMbCxcdkYggOdvXYkMX1?p=preview
Please have a look at the below link
http://plnkr.co/edit/iSFp3n4PSDTM6pRofXc2
Following is the directive for matching passwords.
Directive
app.directive('pwdMatch', function () {
return {
require: 'ngModel',
restrict: 'A',
scope: {
pwdMatch: '='
},
link: function (scope, elem, attrs, ctrl) {
scope.$watch(function () {
var modelValue = ctrl.$modelValue || ctrl.$$invalidModelValue;
return (ctrl.$pristine && angular.isUndefined(modelValue)) || scope.pwdMatch === modelValue;
}, function (currentValue) {
ctrl.$setValidity('pwdMatch', currentValue);
});
}
};
})
HTML
<div class="form-group">
<label for="password" class="col-sm-4 control-label">Password</label>
<div class="col-sm-8">
<input type="password" class="form-control" id="password" placeholder="Enter password"
ng-model="user.password" name="password" required="true"
ng-pattern="/^(?=.*[0-9])(?=.*[A-Za-z])[a-zA-Z0-9~!##$%^&*]{6,15}$/">
<span ng-show="signUpForm.password.$dirty && signUpForm.password.$error.required">
<small class="text-danger">Please enter a password.</small>
</span>
<span ng-show="signUpForm.password.$dirty && !signUpForm.password.$error.required && signUpForm.password.$error.pattern">
<small class="text-danger">Password should have 6 to 15 characters with alphabets and digits</small>
</span>
</div>
</div>
<div class="form-group">
<label for="confirmPassword" class="col-sm-4 control-label">Confirm Password</label>
<div class="col-sm-8">
<input type="password" class="form-control" name="confirmPassword" id="confirmPassword"
placeholder="Confirm password" ng-model="user.confirmPassword" required="true"
data-pwd-match="user.password">
<span ng-show="signUpForm.confirmPassword.$dirty && (signUpForm.confirmPassword.$error.required || signUpForm.confirmPassword.$error.pwdMatch)">
<small class="text-danger">Passwords do not match.</small>
</span>
</div>
</div>
Hope this is what you are looking for.