angularjs $setValidity from a directive not updating - javascript

I am trying to build a custom directive (based on an example I saw) to ensure that the confirm password is correct.
I am running into some trouble with the $setValidity tag, although all my console.log() execute and print out, it does not seem that the validity is being updated. Pass stays true even when I read in my console "set to false".
I made sure the name and the ng-model are the same and I also tried using scope.$apply, but that would comeout with an error saying i was trying to apply when something else is being applied.
Here is my code:
HTML
<form ng-submit="signup()" name="profileForm">
<div class="form-group">
<label class="form-label" for="input-example-2">Password</label>
<input class="form-input" ng-model="pnew" type="password" name="pnew" placeholder="Password" required>
</div>
<div class="form-group">
<label class="form-label" for="input-example-2">Confirm Password</label>
<input class="form-input" name="confirm" ng-model="confirm" type="password" placeholder="Password" required pwcheck>
</div>
{{profileForm.confrim.$error.pass}}
<hr>
{{profileForm.confirm.$error}}
<hr>
{{profileForm.confirm.$valid}}
<span ng-show="profileForm.confirm.$error.pwCheck"> the passwords dont match</span>
<div class="form-group">
<button class="btn btn-primary">Sign up</button>
</div>
</form>
JS code for the pwcheck directive
.directive('pwcheck', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
function valid(value){
scope.$watch("confirm", function(newval, oldval){
console.log(value);
console.log(scope.pnew);
if(value==scope.pnew){
console.log("success!");
ctrl.$setValidity('pass', true);
return value;
}
else {
console.log("set to false");
ctrl.$setValidity('pass', false);
return undefined;
}
});
}
ctrl.$parsers.push(valid);
} } });

No need to use $watch on confirm if you're using your directive on same element & having ng-model in require. So your code can be
ctrl.$parsers.unshift(valid);
ctrl.$formatters.unshift(valid);
function valid(viewValue){
console.log(viewValue);
console.log(scope.pnew);
if(viewValue==scope.pnew){
console.log("success!");
ctrl.$setValidity('pass', true);
return viewValue;
}
else {
console.log("set to false");
ctrl.$setValidity('pass', false);
return undefined;
}
}
Most trivial example plunk
https://plnkr.co/edit/vPbICfSCDnwHKh07DAXJ?p=preview

Related

AngularJS - submit form programmatically after validation

I have recently started working on AngularJS 1.6.
I am trying to submit a form programmatically. The reason is I want to validate a few fields (required field validation). I have spent a lot of efforts (probably 3-4 hours) trying to make this work but none of the existing answers on stack overflow or AngularJS docs seems to be working for me today (strange), hence I am posting this as last resort.
Below is my html
<form method="post" id="loginform" name="loginform" ng-submit="loginUser()" novalidate>
<div>
{{message}}
</div>
<div>
<label>User Name</label>
<input type="text" id="txtUserName" ng-model="user.UserName" name="user.UserName" />
</div>
<div>
<label>Password</label>
<input type="text" id="txtPassword" ng-model="user.Password" name="user.Password" />
</div>
<div>
<input type="submit" id="btnLogin" title="Save" name="btnLogin" value="Login" />
</div>
</form>
My angular code
var demoApp = angular.module('demoApp', []);
demoApp.controller("homeController", ["$scope", "$timeout", function ($scope, $timeout) {
$scope.loginUser = function () {
var form = document.getElementById("loginform");
//var form = $scope.loginform; - tried this here...
//var form = $scope["#loginform"]; tried this
//var form = angular.element(event.target); - tried this...
// tried a lot of other combinations as well...
form.attr("method", "post");
form.attr("action", "Home/Index");
form.append("UserName", $scope.user.UserName);
form.append("Password", $scope.user.Password);
form.append("RememberMe", false);
form.submit();
};
}]);
I keep on getting error 'attr' is not a function.
All I need is submit a form using post method, with values. Just before that I am trying to intercept the submit call and check for validations.
I am open to try any other approach as well. Such as changing the input type from submit to button. Putting the input outside the form. I would be more than happy if validations and submit both can happen any which way. I just want it to post back the values after validating on the client side and then the server will take care of the redirect.
Note: I want the form to do a full postback so that I can get it to redirect to another form. (I know I could use Ajax, but some other day, may be!)
1st of all avoid doing var form = document.getElementById("loginform");. Instead of using form.submit you can use the following code. Do it the angular way cheers :D
$scope.loginUser = function () {
if($scope.loginform.$valid){
user.rememberme=false;
$http({
url: 'Home/Index',
method: "POST",
data: user
})
.then(function(response) {
// success
},
function(response) { // optional
// failed
});
}
};
this is a code to validation if validation not complate button is not enable
<form method="post" id="loginform" name="loginform" ng-submit="loginUser()" novalidate>
<div>
{{message}}
</div>
<div>
<label>User Name</label>
<input type="text" id="txtUserName" required ng-model="user.UserName" name="UserName" />
</div>
<div>
<label>Password</label>
<input type="text" id="txtPassword" ng-model="Password" name="user.Password"required />
</div>
<div>
<input type="submit" ng-disabled="myForm.UserName.$invalid || myForm.Password.$invalid" id="btnLogin" title="Save" name="btnLogin" value="Login" />
</div>
</form>
You should use $scope when trying to access the form, something like $scope.loginform. But......
Take a look at ng-messages. Heres an example using ng-messages with your form:
<form id="loginform" name="loginform" ng-submit="loginUser()">
<div>
{{message}}
</div>
<div>
<label>User Name</label>
<input type="text" id="txtUserName" ng-model="user.UserName" name="user.UserName" required/>
<div class="help-block" ng-messages="loginform.txtUserName.$error" ng-show="loginform.txtUserName.$touched">
<p ng-message="required">Username is required.</p>
</div>
</div>
<div>
<label>Password</label>
<input type="text" id="txtPassword" ng-model="user.Password" name="user.Password" required/>
<div class="help-block" ng-messages="loginform.txtPassword.$error" ng-show="loginform.txtPassword.$touched">
<p ng-message="required">Password is required.</p>
</div>
</div>
<div>
<input type="submit" id="btnLogin" title="Save" name="btnLogin" value="Login" ng-click="loginUser()" />
</div>
</form>
Add ngMessages:
var demoApp = angular.module('demoApp', ['ngMessages']);
demoApp.controller("homeController", ["$scope", "$timeout", function ($scope, $timeout) {
$scope.loginUser = function () {
if($scope.loginform.$valid){
//Code to run before submitting (but not validation checks)
} else{
return false;
}
};
}]);
Don't forget to include ngMessages in your app declaration and include the ngMessages.js script file. Note how you can simply use HTML5 validators.
I found the thing I was looking for. In the end I had to create a directive for validating and then submitting. So I am posting it here as a whole answer.
My HTML
<div ng-controller="homeController" ng-init="construct()">
<form method="post" action="Index" role="form" id="loginform" name="loginform" ng-form-commit novalidate class="ng-pristine ng-invalid ng-invalid-required">
<div class="form-group">
<label for="UserName">User ID</label>
<input autocomplete="off" class="form-control ng-valid ng-touched ng-pristine ng-untouched ng-not-empty"
id="UserName" name="UserName" ng-model="user.UserName" type="text" value=""
ng-change="userNameValidation = user.UserName.length == 0">
<span class="field-validation-error text-danger" ng-show="userNameValidation">The User ID field is required.</span>
</div>
<div class="form-group">
<label for="Password">Password</label>
<input autocomplete="off" class="form-control ng-valid ng-touched ng-pristine ng-untouched ng-not-empty"
id="Password" name="Password" ng-model="user.Password" type="password" value=""
ng-change="passwordValidation = user.Password.length == 0">
<span class="field-validation-error text-danger" ng-show="passwordValidation">The Password field is required.</span>
</div>
<div>
<input type="button" id="btnLogin" title="Login" name="btnLogin" value="Login" ng-click="validateUser(loginform)" />
</div>
</form>
</div>
Look for ng-form-commit on the form element. It is the directive that I created.
My Angular code
var demoApp = angular.module('demoApp', []);
demoApp.factory("commonService", function () {
return {
isNullOrEmptyOrUndefined: function (value) {
return !value;
}
};
});
//This is the directive that helps posting the form back...
demoApp.directive("ngFormCommit", [function () {
return {
require: "form",
link: function ($scope, $el, $attr, $form) {
$form.commit = function () {
$el[0].submit();
};
}
};
}]);
demoApp.controller("homeController", ["$scope", "commonService", function ($scope, commonService) {
$scope.construct = function construct() {
$scope.user = { UserName: "", Password: "" };
};
$scope.userNameValidation = false;
$scope.passwordValidation = false;
$scope.isFormValid = false;
$scope.validateUser = function ($form) {
$scope.isFormValid = true;
$scope.userNameValidation = commonService.isNullOrEmptyOrUndefined($scope.user.UserName);
$scope.passwordValidation = commonService.isNullOrEmptyOrUndefined($scope.user.Password);
$scope.isFormValid = !($scope.userNameValidation || $scope.passwordValidation);
if ($scope.isFormValid === true) {
$scope.loginUser($form);
}
};
$scope.loginUser = function ($form) {
$form.commit();
};
}]);
I found the directive here
Example using Angular 1.5 components.
(function(angular) {
'use strict';
function DemoFormCtrl($timeout, $sce) {
var ctrl = this;
this.$onInit = function() {
this.url = $sce.trustAsResourceUrl(this.url);
/*$timeout(function() {
ctrl.form.$$element[0].submit();
});*/
};
this.validate = function(ev) {
console.log('Running validation.');
if (!this.form) {
return false;
}
};
}
angular.module('app', [])
.component('demoForm', {
template: `
<p>To run this demo allow pop-ups from https://plnkr.co</p>
<hr>
<p>AngularJS - submit form programmatically after validation</p>
<form name="$ctrl.form" method="get" target="blank" action="{{::$ctrl.url}}" novalidate
ng-submit="$ctrl.validate($event)">
<input type='hidden' name='q' ng-value='::$ctrl.value'>
<input type='hidden' name='oq' ng-value='::$ctrl.value'>
<input type="submit" value="submit...">
</form>`,
controller: DemoFormCtrl,
bindings: {
url: '<',
value: '<'
}
});
})(window.angular);
https://plnkr.co/edit/rrruj6vlWrxpN3od9YAj?p=preview

Searching through JSON Object with ng-disabled (AngularJS)

One of the features of my web application is the possibility to add new users (username + password) through a form. Thereby, I have one JSON object (l_usernames) defined in a controller (UsersController) with all the usernames already chosen by users to avoid the repetition of usernames (it's a unique key).
Sample of my data (fetched-data.json) - format of object "usernames" (l_usernames):
[{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}]
There is a sample of the form to add new users (add-user.html):
<div class="row" ng-controller="UsersController">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Add New User</h3>
</div>
<div class="panel-body">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="inputUserUsername" class="col-sm-2 control-label"><i class="icon fa fa-user"></i> USERNAME</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputUserUsername" placeholder="Username" ng-model="user_username">
</div>
</div>
<div class="form-group">
<label for="inputUserPassword" class="col-sm-2 control-label"><i class="icon fa fa-key"></i> PASSWORD</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputUserPassword" placeholder="Password" ng-model="user_password">
</div>
</div>
</form>
</div>
</div>
<form class="form-inline">
<div class="span7 text-center">
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="(!user_username || !user_password)">Save</button>
</div>
</form>
</div>
</div>
Sample of my controller (userscontroller.js):
var app = angular.module('myApp');
app.controller('UsersController', ['$scope', 'services', function($scope, services) {
services.getData().then(function(data){
$scope.l_usernames = data.data;
});
}])
.factory('services', ['$http', function($http){
var serviceBase = 'services/'
var object = {};
object.getData = function(){
return $http.get('fetched-data.json');
};
return object;
}]);
I would like to know how it is possible to not allow the insert of new users if the username is already chosen - searching through the JSON object l_usernames - with ng-disabled (by disabling the "Save" button). I also want to print a simple message - "Username already chosen" - if such situation occurs. Thank you.
Add a watch on the user_username scope variable. Whenever it changes search through the JSON object, you can use lodash or underscorejs to search through l_usernames to see if the username already exists. If it exists then set a variable in the scope to false. Bind the ng-disabled of the save button to this variable. Use debounce on the user_username for better performance.
Take a look at this fiddle here
Controller
function UsersController($scope) {
$scope.name = 'Superhero';
$scope.l_username = [{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}];
$scope.allowSave = true;
$scope.$watch('user_username', function(value) {
if (_.findWhere($scope.l_username, {"USERNAME": value}) !== undefined)
$scope.allowSave = false;
else
$scope.allowSave = true;
})
}
HTML
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="!allowSave">Save</button>
Whenever the entered username is found in the array, the allowSave variable is changed which disables the 'save' button.
Note: I have used underscore.js to search through the list. You can use you custom method as well.
I have added the warning message and debounced the model for better performance.
I would make a validation directive.
HTML:
<input username-exists type="text" ng-model="userName" ng-model-options="{updateOn: 'default blur', debounce: { 'default': 700, 'blur': 0 }}" />
<div ng-if="myFormName.$error.usernameExists">Username exists!</div>
<button type="button" ng-disabled="myFormName.$invalid">
The ng-model-options is so that your model doesn't go crazy and update always (it delays the validation).
Javascript:
app.directive('usernameExists', function() {
return {
restrict: 'A', //match attributes only
require: 'ngModel',
scope: {
l_usernames: '='
},
link: function(scope, elem, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
//first, assume the model is valid until proven otherwise
ctrl.$setValidity('usernameExists', true);
if(viewValue.length > 0 && !ctrl.$error.usernameExists) {
for(var i = 0; i < scope.l_usernames.length; ++i) {
if(scope.l_usernames[i].USERNAME === viewValue) {
//username exists, so set valididty to false
//the form is not valid
ctrl.$setValidity('usernameExists', false);
break; //found match
}
}
}
return viewValue;
});
}
};
})

Why does the $asyncValidators in angularjs returns a promise but got undefined

So i'm new to the whole web thing and today i discover about $asyncValidators.
So after a lot of trying i get stuck with this example.
username.validator
(function() {
angular
.module('app')
.directive('emailNotUsed',emailNotUsed);
emailNotUsed.$inject = ['$http', '$q'];
function emailNotUsed ($http, $q) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$asyncValidators.emailNotUsed = function(modelValue, viewValue) {
console.log("start");
console.log(viewValue);
console.log($http.post('/email', viewValue));
$http.post('/email',viewValue).then(function(response) {
console.log("Check if email is valid in directive")
console.log(response.data)
return response.data == true ? $q.reject(response.data.errorMessage) : true;
});
};
}
};
}
}());
register.html
<div class="container" ng-controller="RegisterController as vm">
<div class="col-md-6 col-md-offset-3">
<h2>Register</h2>
<div ng-show="vm.error" class="alert alert-danger">{{vm.error}}</div>
<form name="form" ng-submit="!form.$pending && form.$valid && vm.register()" role="form">
<div class="form-group" ng-class="{ 'has-error': form.email.$dirty && form.email.$error.required }">
<label for="email">Email</label>
<input type="text" name="email" id="email" class="form-control" ng-model="vm.user.email" email-not-used ng-model-options="{ debounce: 500 }" required />
<div ng-messages="form.email.$error">
<div ng-message="emailNotUsed">User with this email already exists.</div>
</div>
</div>
<div class="form-actions">
<button type="submit" ng-disabled="form.$invalid || vm.dataLoading" class="btn btn-primary">Register</button>
Cancel
</div>
</form>
</div>
</div>
And now the where it got weird this is the output in the console
So why does this gives an error while i got the promise value? Any idea how to proceed
Asynchronous validators expect promise to be returned, you must therefore modify your validation function to return the result of $http call like this:
ngModel.$asyncValidators.emailNotUsed = function(modelValue, viewValue) {
return $http.post('/email',viewValue).then(function(response) {
return response.data == true ? $q.reject(response.data.errorMessage) : true;
});
};

How to call custom directive on form submit

I have a custom directive which works when I once change input value but when I submit form without changing input field value directive does not display error message.
html code:
<form name="myform" role="form" class="form-horizontal" method="post" novalidate>
<div class="form-group">
<label class="control-label col-sm-3" for="firstName">First name :</label>
<div class="col-sm-9">
<input name='firstName' class="form-control" type='text' required ng-model='name' string>
</div>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-success" />
</div>
</form>
directive :
validationModule.directive('string', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, ctrl) {
function validationError(value) {
if (value.length < 1) {
ctrl.$setValidity('required', false);
//showPopOver(element, "Required field");
errorValidation(element, "Required field");
// $(element).popover('show');
}
if (/[a-zA-Z]/.test(value)) {
ctrl.$setValidity('invalid', true);
// $(element).popover('destroy');
successValidation(element);
}
if (/[0-9]/.test(value)) {
ctrl.$setValidity('invalid', false)
errorValidation(element, "number not allowed");
// showPopOver(element, "number not allowed");
// $(element).popover('show');
}
return value;
}
ctrl.$parsers.push(validationError);
}
};
});
How can I call directive to perform validation on submit event ?
I don't know which version of angular you are using, but since 1.3 validators are not hacked into parsers. See $validators in https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Also using such generic names for directives is discouraged due to possible name collisions. Use some prefix eg myString

AngularJS watch not being triggered

I'm having a problem with AngularJs. I have created a directive that $watch the model and it takes some action based on the model's current status. However, although during debugging I can see that the $watch is set, it is only being triggered after the model get valid at least once and I don't know why that is happening. Debugging it doesn't even gets into the $watch function when something is typed.
The code is below:
Directive:
(function() {
'use strict';
var app = angular.module('app');
app.directive('tooltipValidation', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var tooltip = $(element).qtip({
content: {
text: element.next('div')
},
show: false,
hide: true
}).qtip('api');
scope.$watch(attrs.ngModel, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
}
};
});
})()
HTML:
<div class="form-address clearfix" ng-show="isNewShippingAddress" ng-form="newShippingAddressForm">
<h3>Include new shipping address:</h3>
<div class="novo-endereco clearfix" id="newAddress">
<div class="required address apelido">
<label for="newShippingAddressAlias">Alias</label>
<input id="newShippingAddressAlias" name="newShippingAddressAlias" type="text" tooltip-validation ng-model="newShippingAddress.Alias" required ng-maxlength="32" />
<div data-ng-show="newShippingAddressForm.newShippingAddressAlias.$dirty && newShippingAddressForm.newShippingAddressAlias.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.maxlength">max 32 char</p>
</div>
</div>
<div class="required endereco">
<label for="newShippingAddressStreet">Street</label>
<input id="newShippingAddressStreet" name="newShippingAddressStreet" type="text" tooltip-validation ng-model="newShippingAddress.Street" required ng-maxlength="256" />
<div data-ng-show="newShippingAddressForm.newShippingAddressStreet.$dirty && newShippingAddressForm.newShippingAddressStreet.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.maxlength">max 256 char</p>
</div>
</div>
<div class="required cep">
<label for="newShippingAddressZipCode">ZipCode</label>
<input id="newShippingAddressZipCode" name="newShippingAddressZipCode" type="text" tooltip-validation ng-model="newShippingAddress.ZipCode" required ng-pattern="/^[0-9]{8}$/" />
<div data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$dirty && newShippingAddressForm.newShippingAddressZipCode.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.pattern">8 digits</p>
</div>
</div>
<input type="submit" class="button grey" value="Save new address" data-ng-click="saveShippingAddress()" ng-disabled="newShippingAddressForm.$invalid" />
</div>
</div>
Regards,
dimello
Try:
scope.$watch(function(){
return ngModel.$viewValue; //Watch for view value (the value in your input)
}, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
DEMO
Explanation:
When you type an invalid value into the input with ng-model, the underlying model is not updated, causing your scope.$watch(attrs.ngModel not being fired because you're watching for changes in the model. If you need to fire the function every time the input changes no matter it's valid or not, try the above solution.

Categories

Resources