I have my registration form with textbox username. I want to do is when the user enter the username, the custom directive will check if the entered username is exists in the database.
directives.js
angular.module('installApp').directive('pwCheck', function ($http) {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
elem.on('blur', function (evt) {
scope.$apply(function () {
$http({
method: 'GET',
url: '../api/v1/users',
data: {
username:elem.val(),
dbField:attrs.ngUnique
}
}).success(function(data, status, headers, config) {
ctrl.$setValidity('unique', data.status);
});
});
});
}
}
});
If it's exists, my div class = "invalid" will shown in the html form with label "Username already exists."
registration.html
<form name = "signupform">
<label>{{label.username}}</label>
<input type="text" id = "username" name = "username" ng-model="user.username" class="form-control"></input>
<div class="invalid" ng-show="signupform.username.$dirty && signupform.username.$invalid"><span ng-show="signupform.username.$error.unique">Username already exists.</span>
</div>
</form>
But right now, they are not working :-(,am I doing right? Please advice or suggest me things I should do. Thanks in advance.
there is a great tutorial by yearofmoo about $asyncvalidators in angular1.3. it allows you to easily show pending status when the field is being checked by the backend:
here's a working plnkr
app.directive('usernameAvailable', function($timeout, $q) {
return {
restrict: 'AE',
require: 'ngModel',
link: function(scope, elm, attr, model) {
model.$asyncValidators.usernameExists = function() {
//here you should access the backend, to check if username exists
//and return a promise
//here we're using $q and $timeout to mimic a backend call
//that will resolve after 1 sec
var defer = $q.defer();
$timeout(function(){
model.$setValidity('usernameExists', false);
defer.resolve;
}, 1000);
return defer.promise;
};
}
}
});
html:
<form name="myForm">
<input type="text"
name="username"
ng-model="username"
username-available
required
ng-model-options="{ updateOn: 'blur' }">
<div ng-if="myForm.$pending.usernameExists">checking....</div>
<div ng-if="myForm.$error.usernameExists">username exists already</div>
</form>
note the use of ng-model-options, another cool feature of 1.3
edit
here's a plnkr that shows how to use $http in the directive. note that it is only requesting another .json file, that contains a true/false value. and the directive will set validity on the ng-model accordingly.
Related
I am using Anggular Material mdDialog to show a small Form inside a Dialog Box.
I am calling a directive username-available to do asynchronous validation inside it.
dialog.tmpl.html:
<md-dialog aria-label="Save Scenario">
<form name="userForm" novalidate >
<input style="display:inline-block" name="Name" ng-model="SaveScenario.ScenarioName" ng-pattern="pattern"
required username-available ng-model-options="{ updateOn: 'blur' }">
</form>
</md-dialog>
app.js:
module.directive('usernameAvailable', function($timeout, $q,UserService) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attr, ngModel) {
ngModel.$asyncValidators.usernameExists = function(modelValue, viewValue) {
var currentValue = modelValue || viewValue;
UserService.checkifScenarioNameExists(currentValue)
.then(
function (d)
{
console.log("the data object from promise is", d.data);
if ( d.data == true)
{
console.log("username exists");
// deferred.resolve(d.data);
return $q.resolve(d.data);
}
else
{
console.log("username does not exist");
ngModel.$setValidity('usernameExists', false);
return $q.reject(d.data);
}
},
function (errResponse) {
console.error('The Promise was unsuccessfull');
}
);
};
}
}
});
Here is how my Dialog Box looks like and the error are also displayed:
I think my syntax for returning a promise inside the directive is correct. I am not sure if it could be something with md-dialog.
Can someone please help out why this error is happening??
As error states, expected asynchronous validator to return a promise. Inside validator, you missed return statement in front of your UserService usage.
return UserService.checkifScenarioNameExists(currentValue).then(...)
This is what I wrote but angular keeps complaining about the $asyncValidators telling me is {}. I do not want to work with $http nor with $q. This is the faulty code I wrote:
.directive('checkEmail', ['toolBox', function(toolBox){
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel){
ngModel.$asyncValidators.emailExists = function(userEmail){
var promise = toolBox.checkEmailExist(userEmail);
return promise.get(function success(res){
return true;
}, function error(res){
return false;
});
};
}
};
}])
Has any of you worked validation with $resource? Where I get it wrong? toolBox.checkEmailExist(userEmail) comes from a service that looks like this
angular.module('toolBoxService', ['ngResource'])
.factory('toolBox', ['$resource','$log', function($resource, $log){
var dataObj = {};
dataObj.checkEmailExist = function(email){
return $resource('api/users/email', {email: email});
};
return dataObj;
}]);
and the form element looks like
<!-- Email field -->
<div class="input-group">
<span class="input-group-addon" id="userEmail">*</span>
<label class="control-label" for="userEmailField"></label>
<input class="form-control"
name="userEmail"
id="userEmailField"
required=""
placeholder="Email"
type="email"
ng-model="data.email"
ng-model-options="{ debounce: { default : 300, blur: 0 }}"
check-email>
</input>
</div>
<!-- Validation of the email -->
<div class="help-block form-error-messages"
ng-messages="registerForm.userEmail.$error"
ng-show="registerForm.userEmail.$touched"
role="alert"
ng-messages-multiple>
<div ng-messages-include="FACETS/errors/errorMessages.html"></div>
</div>
The only solution I've came up with is this
// checking for email in the database
.directive('checkEmail', ['toolBox', function(toolBox) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$validators.checkEmail = function(modelValue, viewValue){
var currentVal = modelValue || viewValue;
toolBox.checkEmailExist(currentVal)
.get(function success(resp){
if(resp.email == currentVal) {
ngModel.$setValidity('checkEmail', false);
} else {
ngModel.$setValidity('checkEmail', true);
}
});
};
} // end link
}; // end return
}])
$resource is not working with the $asyncValidators unfortunately. Took me 2 days to realise this. I hope there was an answer, a more expert voice on the matter.
It might be that you have to return the $promise attached to the resource instance object, rather than the object itself.
If your api returns an OK 200 response when the username does exist and an error status (e.g. 404 Not Found) when it does not, then the following approach works:
.directive('usernameExists', ['$resource', function ($resource) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ngModel) {
ngModel.$asyncValidators.usernameExists = function (userName) {
return $resource('/api/Account/:userName')
.get({ userName: userName }).$promise;
};
}
}
}])
I can solve it, it was matter of playing a bit more with promises
.directive('validateNbr',['$q','PreavisoService', function($q, PreavisoService) {
return {
require: 'ngModel',
restrict: '',
link: function(scope, elm, attrs, ngModel) {
ngModel.$asyncValidators.validateNbr = function(modelValue){
var def = $q.defer();
PreavisoService.checkUnitDigit({nbr:ngModel.$viewValue}).$promise.then(
function(result){
if(result.isValid === false){
def.reject();
}else{
def.resolve();
}
});
return def.promise;
};
}
};
}]);
hope it helps
I am new to Angular JS and I am doing form validation for login page using Angular Js. If I enter username and password, it is working fine But if I choose remember credentials in browser and choose autocomplete options next time, my Submit button is not enabled. I am facing this issue only in IE9. for rest of the browsers its working fine. Any suggestions for this. My login.html looks like this:
<input ng-model="username"
class="login"
value=""
name="userId"
type="text"
required/>
<input ng-model="password"
class="login"
value=""
name="password"
type="password"
required/>
<button class="primaryButton"
type="submit"
ng-click="loginUser()"
ng-disabled="loginForm.$invalid"/>
Also, as per one blog, I tried adding directive for this. By adding directive, If I choose autocomplete options and just mouse click somewhere, submit button is enabled. But I don't want to click after choosing autocomplete option.
My directive looks like this:
angular.module('sampleModule').directive('autofill', function autofill(){
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
scope.$watch(function () {
return element.val();
}, function(nv, ov) {
if(nv !== ov) {
ngModel.$setViewValue(nv);
}
});
}
};
})
You may need to apply a timeout to your directive's logic to force it to alert IE that it needs to re-render.
angular.module('sampleModule').directive('autofill', ['$timeout',
function autofill($timeout){
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
scope.$watch(function () {
$timeout(function () {
return element.val();
}, 0);
}, function(nv, ov) {
$timeout(function () {
if(nv !== ov) {
ngModel.$setViewValue(nv);
}
}, 0);
});
}
};
}]);
Try to copy at interval times, because IE9 (and chrome) doesn't emit events for user and password autocomplete.
Set respective ids for inputs, and then:
app.controller('yourController', function($scope, $interval) {
$interval(function() {
$scope.username = $('#username').val();
$scope.password = $('#password').val();
}, 1000); // each 1 second
});
of course, you can adapt this soluction to your directive.
try a directive to call change from element:
directive('monitorAutoFill', function($timeout) {
return {
restrict: 'A',
link: function(scope, el, attrs, ctrl) {
$timeout(function() {
el.trigger('change');
}, 500);
}
};
});
and, on your inputs:
<input ng-model="username"
class="login"
value=""
name="userId"
type="text"
required
monitor-auto-fill />
<input ng-model="password"
class="login"
value=""
name="password"
type="password"
required
monitor-auto-fill />
I have the current section of html that is used to check a users password,
<div class="form-group">
<label for="auditName" class="col-lg-4 control-label">Current Password </label>
<div class="col-lg-8">
<input type="password" placeholder="Current Password"
name="currentPassword"
class="form-control"
ng-model="currentPassword"
required=""
password-new
ng-model-options="{ updateOn: 'blur' }">
</div>
<div class="col-lg-offset-4" ng-if="form.$pending.oldPassword">checking....</div>
<div class="col-lg-offset-4" ng-if="form.$error.oldPassword">Please create a NEW password</div>
</div>
{{currentPassword}}
My issue is that the currentPassword is not being updated, so nothing is being displayed on the screen. If I remove the model-options AND I remove the reference to the new-password directive it will display as you type - so both of these are for some reason stopping the model from updating the value.
The directive new-password looks like this, and is still in a basic format I found elsewhere until I get this working properly,
app.directive('passwordNew', function ($timeout, $q) {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, elm, attr, model) {
model.$asyncValidators.oldPassword = function () {
//here you should access the backend, to check if username exists
//and return a promise
var defer = $q.defer();
$timeout(function () {
model.$setValidity('oldPassword', true);
defer.resolve;
}, 1000);
return defer.promise;
};
}
}
});
Any ideas?
The {{currentPassword}} in your HTML is outside the scope of the directive. You need to link the two scopes. Checkout "Isolating the Scope of a Directive" in https://docs.angularjs.org/guide/directive.
Put something like this on the directive
scope: {
currentPassword: '='
},
EXAMPLE
This is how I solve a similar problem
app.directive('availableEmail', [
'dataSvc', (data:otolane.direct.IDataService) => {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
ctrl.$setValidity('availableEmail', true);
//only check the db if value is an email
if (viewValue.length > 3 && !ctrl.$error.email) {
data.account.checkEmail(viewValue)
.then(() => {
// data method resolves if email is available
ctrl.$setValidity('availableEmail', true);
})
.catch(() => {
//returns error if email is in use
ctrl.$setValidity('availableEmail', false);
});
}
return viewValue;
});
}
};
}
]);
I have a form field that I want validated against a rest api as the user types in the form field. Sort of like an auto-complete but an auto-verify instead. I've started to lay some of the code down in angular but not sure how to observe the input onchange in angular or how to set the inavlid property of things.
Here's what I have so far
app.directive('purchaseCode', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (CODE_REGEXP.test(viewValue)) {
//how do I get the $http context/scope to to an ajax request here?
$http.get('check/user.code').success(function(data) {
ctrl.$setValidity('purchase_code', data.is_valid);
});
return viewValue;
} else {
ctrl.$setValidity('purchase_code', false);
return undefined;
}
});
}
};
});
function Controller($scope, $routeParams, $http) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.isUnchanged = function(user) {
return angular.equals(user, $scope.master);
};
$scope.reset();
}
and the HTML
<div class="container" ng-app>
<form no-validate id="myform" name='form' action="/upload" method="POST" role="form" ng-controller="Controller">
<div class="form-group has-error">
<label class="control-label" for="purchase_code">Purchase code</label>
<input type="text" class="form-control" id="purchase_code" placeholder="purchase code"
ng-model="user.code"
purchase-code
name="code">
</div>
<button type="submit" class="btn btn-block btn-primary" ng-click="update(user)"
ng-disabled="form.$invalid || isUnchanged(user)">Submit</button>
</form>
</div>
What do I do to have it watch the event of the form field being changes. Also a bonus question: how do I keep it from showing the invalid/dirty state until it gets to be more than 3 characters in length?
I tried looking at the custom validator sample on http://docs.angularjs.org/guide/forms but it doesn't seem to work if you copy and paste their code in verbatim.
I just needed to pass in $http into the method
app.directive('purchaseCode', function($http) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (CODE_REGEXP.test(viewValue)) {
$http.get('check/user.code').success(function(data) {
ctrl.$setValidity('purchase_code', data.is_valid);
});
return viewValue;
} else {
ctrl.$setValidity('purchase_code', false);
return undefined;
}
});
}
};
});