directive angularjs to check mail used or not - angularjs

i want to check if email is already used or not (communication with back-end) but the error message is not shown in the screen.
the verficiation function service :
demoApp.factory('verifyEmail', function($http) {
return function(mail) {
var test=mail;
var urll="http://localhost:8080/app/personne/verifmail?msg=";
var aplrest=urll+test;
var ch3=aplrest;
return $http.post(ch3)
};});
the directive (with the help of #jsonmurphy)
demoApp.directive('existTo', ["$q","verifyEmail",function ($q, verifyEmail) {
return {
require: "ngModel",
scope: {
otherModelValue: "=existTo"
},
link: function(scope, element, attributes, ngModel) {
// Note the change to $asyncValidators here <-------------
ngModel.$asyncValidators.existTo = function(modelValue) {
var deferred = $q.defer();
verifyEmail(modelValue).then(function(respons) {
// the respons can be {exist="true"} or{exist="false"}
var rep=respons.exist;
deferred.resolve(rep);
});
return deferred.promise;
};
scope.$watch("otherModelValue", function() {
ngModel.$validate();
});
}
}; }]);
the html file:
<label>Email</label>
<input type="email" name="email" class="form-group"
ng-model="registration.user.email"
ng-pattern="/^[_a-z0-9]+(\.[_a-z0-9]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,8})$/"
required exist-To="registration.user.email"/>
<span style="color:red" ng-show="registrationForm.email.$dirty && registrationForm.email.$invalid">
<span ng-show="registrationForm.email.$error.required">champs email obligatoire.</span>
<span ng-show="registrationForm.email.$error.pattern">Invalid email address.</span>
<span ng-show="registrationForm.email.$error.existTo">mail already exist.</span>
</span>

this the solution
demoApp.directive('existTo', ["$q","verifyEmail",function ($q, verifyEmail) {
return {
require: "ngModel",
scope: {
otherModelValue: "=existTo"
},
link: function(scope, element, attributes, ngModel) {
// Note the change to $asyncValidators here <-------------
ngModel.$asyncValidators.existTo = function(modelValue) {
var deferred = $q.defer();
// window.alert("l email a tester "+modelValue);
verifyEmail(modelValue).success(function(respons) {
// the respons can be {exist="true"} or{exist="false"}
// window.alert("la reponse " +respons);
var rep=respons.exist;
// window.alert("la reponse " +rep);
if (rep =="false") {
// The username is available
deferred.resolve();
} else {
deferred.reject();
}
});
return deferred.promise;
};
scope.$watch("otherModelValue", function() {
ngModel.$validate();
});
}
}; }]);

Related

Angular async validation with multiple form fields

I have a async validation directive which works fine when, but it depends on two fields to define if a person exists (numId and type), here's the code:
app.directive('personaUnica', function($http, $q){
return{
require:'ngModel',
scope: {
tipo: "=personaUnica"
},
link: function(scope, element, attrs, ctrl){
ctrl.$asyncValidators.personaUnica = function(modelValue, viewValue){
if (ctrl.$isEmpty(modelValue)) {
// valido si es vacio
return $q.when();
}
var defer = $q.defer();
$http.get('/someRESTEndpoint/', {
params:{ identificacion: modelValue, tipo: scope.tipo }
}).then(function(respuesta){
//Person found, not valid
if( respuesta.data.elementoExiste ){
defer.reject('Persona existe.');
}
}, function(respuesta){
//Person not found, resolve promise
if(!respuesta.data.elementoExiste){
defer.resolve();
}
});
return defer.promise;
}
}
}
});
But I dont know how to make the same validation when the other dependant field has changed.
I've read something about require ^form in the directive but in kinda lost.
I've tried to add this block of code
scope.$watch('tipo', function(){
ctrl.$validate();
});
But then I get an infinite $digest loop.
Thanks in advance.
You can do something like this:
$scope.$watch('tipo', function(newValue, oldValue, scope) {
if (newValue != oldValue) {
ctrl.$validate();
}
});
This way, $scope.watch will be called everytime you have a new value on your $scope.tipo.
Turns out that I was using watch in the wrong place inside the ctrl.$asyncValidators.numId, it has to be outside. Now It works as expected.
app.directive('numId', function($http, $q){
return {
restrict : 'A',
scope: {
tipo: "=numId"
},
require: 'ngModel',
link: function(scope, element, attrs, ctrl){
ctrl.$asyncValidators.numId = function(modelValue, viewValue){
if (ctrl.$isEmpty(modelValue) || ctrl.$isEmpty(scope.tipo)) {
// consider empty model valid
console.log('Not going yet..');
return $q.when();
}
var defer = $q.defer();
$http.get("/some-server/endpoint",{
params:{ identificacion: modelValue, tipo: scope.tipo }
}).then(function(res){
if( res.data.personaExiste){
console.log('exists, not valid')
defer.reject('exists, not valid');
}else if( !res.data.personaExiste ){
console.log('NOT exists, valid!')
defer.resolve();
}
}, function(){
defer.reject('Error...');
});
return defer.promise;
}
// Search when tipo is changed
scope.$watch('tipo', function(){
console.log('Tipo:' + scope.tipo)
ctrl.$validate();
});
}
}
});
And the html:
<div class="form-group">
<label>Numero identificacion</label>
<input type="text"
name="numId"
required
ng-model="busqueda.numId"
num-id="busqueda.tipoUsuario">
<pre class="well"> {{ frmBuscar.numId.$error }} </pre>
</div>

verify email is already used or not angularjs

I start with angularJS and I want to check if the email entered in the form already exists or not . for that, I will use RESTto communicate withe back_end.My problem is : how to check email before sending the form with a directive and display an error message if the email is already used.
<form name="registrationForm">
<div>
<label>Email</label>
<input type="email" name="email" class="form-group"
ng-model="registration.user.email"
ng-pattern="/^[_a-z0-9]+(\.[_a-z0-9]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,8})$/"
required compare-to="registration.user.email"
/>
the model
demoApp.directive('existTo', [function () {
return {
require: "ngModel",
scope: {
otherModelValue: "=existTo"
},
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.existTo = function(modelValue) {
return modelValue == scope.otherModelValue;
};
scope.$watch("otherModelValue", function() {
ngModel.$validate();
});
}
}; }]);
back_end checkmail: the controller
demoApp.controller('signupCtrl',function($scope,$http) {
$scope.verficationEmail = function(mail) {
$scope.test = mail;
var urll="";
var test="";
$scope.urll="http://localhost:8080/app/personne/verifmail?msg=";
$scope.aplrest=$scope.urll+$scope.test;
var ch3=$scope.aplrest;
$http.post(ch3).
success(function(respons) {
$scope.data = respons;
$scope.valid = respons.valid;
if ( $scope.valid == "false") {
$scope.msgalert="mail used";
}
})
};});
thank you in advance for your assistance
You will most likely want to put the verficiation function in a service and inject it wherever it's needed:
demoApp.factory('verifyEmail', function() {
return function(mail) {
var test=mail;
var urll="http://localhost:8080/app/personne/verifmail?msg=";
var aplrest=urll+test;
var ch3=aplrest;
return $http.post(ch3)
};
});
Then...
In your directive:
demoApp.directive('existTo', ["$q","verifyEmail",function ($q, verifyEmail) {
return {
require: "ngModel",
scope: {
// This is probably not needed as ngModel's
// validators will pass the current value anyway
otherModelValue: "=existTo"
},
link: function(scope, element, attributes, ngModel) {
// Note the change to $asyncValidators here <-------------
ngModel.$asyncValidators.existTo = function(modelValue) {
var deferred = $q.defer()
verifyEmail(modelValue).then(function(respons){
deferred.resolve(respons.valid);
});
return deferred.promise
};
scope.$watch("otherModelValue", function() {
ngModel.$validate();
});
}
}; }]);
In your controller:
demoApp.controller('signupCtrl',function($scope,$http,verifyEmail) {
$scope.verficationEmail = function (mail) {
verifyEmail(mail).success(function(respons) {
$scope.data = respons;
$scope.valid = respons.valid;
if ( $scope.valid == "false") {
$scope.msgalert="mail used";
}
});
}
});
Also, in your html you dont seem to be actually using the directive exist-to, I'm guessing that's what compare-to should have been. But, in any case, it might look like this:
<input type="email" name="email" class="form-group"
ng-model="registration.user.email"
ng-pattern="/^[_a-z0-9]+(\.[_a-z0-9]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,8})$/"
required exist-to
/>
Note that there's no need to pass the model value to the directive
Hope that helps.

$asyncValidators with params for two modes ('new' and 'edit' form)

I'm using $asyncValidators to check if email is available or not.
I have only one controller and template for two modes : 'new' and 'edit'.
i created a directive for this check. But on 'edit' mode, i don't want to check current email. So, in my controller, i've created $scope.initialEmail to compare it with entered email. But i don't know how to use it in directive (for edit mode).
Template :
<input id="email" name="email" type="email" class="form-control"
placeholder="{{'placeholders_email'|i18n}}"
ng-model="user.email" ng-required="true" email-available/>
<ng-messages for="myForm.email.$error" ng-if="myForm.email.$dirty">
<ng-message class="red" when="emailAvailable">{{'email_exists_in_db'|i18n}}</ng-message>
...
Controller :
//formMode is injected in controller ('new' or 'edit')
$scope.formMode = angular.copy(formMode);
$scope.user = {email: '...', ...};
switch(formMode){
case 'edit':
$scope.initialEmail = angular.copy($scope.user.email);
break;
}
Factory :
//AuthHttp is an auth service using $http
angular.module('myapp').factory('EmailAvailableValidator', ['$q', 'AuthHttp', function($q, AuthHttp) {
return function(email) {
var deferred = $q.defer();
AuthHttp.get('/rest/users/emailAvailable/'+email).then(function() {
deferred.resolve();
}, function() {
deferred.reject();
});
return deferred.promise;
}}]);
Directive :
angular.module('myapp').directive('emailAvailable', ['EmailAvailableValidator', function(EmailAvailableValidator) {
return {
restrict: "A",
require : 'ngModel',
link : function(scope, element, attrs, ngModel) {
switch(scope.formMode){
case 'new':
ngModel.$asyncValidators.emailAvailable = EmailAvailableValidator;
break;
case 'edit':
ngModel.$asyncValidators.emailAvailable = function(email){ // ?
if(!_.isEqual(scope.initialEmail, ngModel.$modelValue)){ // ?
return EmailAvailableValidator; // ??
}
};
break;
}
}
}
}]);
The solution is to add $q.when for the edit mode like this :
Factory :
angular.module('myapp').factory('UsersService', ['AuthHttp', '$q', function (AuthHttp, $q) {
return {
emailAvailable: function(email){
var deferred = $q.defer();
AuthHttp.get('/rest/users/emailAvailable/'+email).then(function() {
deferred.resolve();
}, function() {
deferred.reject();
});
return deferred.promise;
}
}
}]);
Directive :
angular.module('myapp').directive('emailAvailable', ['UsersService', '$q', function(UsersService, $q) {
return {
restrict: "A",
require : 'ngModel',
link : function(scope, element, attrs, ngModel) {
switch(scope.formMode){
case 'new':
ngModel.$asyncValidators.emailAvailable = function(modelValue, viewValue) {
return UsersService.emailAvailable(modelValue || viewValue);
};
break;
case 'edit':
ngModel.$asyncValidators.emailAvailable = function(modelValue, viewValue){
var newValue = modelValue || viewValue;
var promise;
if(!_.isEqual(scope.initialEmail, newValue)){
promise = UsersService.emailAvailable(newValue);
} else {
promise = $q.when(!_.isEqual(scope.initialEmail, newValue));
}
return promise;
};
break;
}
}
}
}]);

angular ng-options randomly not loading

I've got a dropdown menu like this:
<select selectpicker name="SubArea" ng-model="search.SubArea" ng-options="SubArea.SubArea as (SubArea.SubArea + ' (' + SubArea.Count + ')') for SubArea in subareas | orderBy:'SubArea'" ng-multiple="true" multiple title="All Areas" data-size="auto" data-header="Select Areas" data-live-search="true" data-selected-text-format="count > 2">
</select>
This is in the controller:
$scope.subareas = {};
$http.get('subareas.php')
.success(function(response)
{
$scope.subareas = response;
});
I'm also using this bootstrap select directive:
angular.module('angular-bootstrap-select', [])
.directive('selectpicker',
[
'$timeout',
function($timeout) {
return {
restrict: 'A',
require: ['?ngModel'],
compile: function(tElement, tAttrs) {
tElement.selectpicker();
if (angular.isUndefined(tAttrs.ngModel)) {
throw new Error('Please add ng-model attribute!');
}
return function(scope, element, attrs, ngModel) {
if (angular.isUndefined(ngModel)){
return;
}
scope.$watch(attrs.ngModel, function(newVal, oldVal) {
if (newVal !== oldVal) {
$timeout(function() {
element.selectpicker('val', element.val());
});
}
});
ngModel.$render = function() {
element.selectpicker('val', ngModel.$viewValue || '');
};
$timeout(function() {
element.selectpicker('refresh');
element.change(function() {
if ($(this).val() !== '') {
$('.form-search .bootstrap-select.open').addClass('selected-option-check');
}else {
$('.form-search .bootstrap-select.open').removeClass('selected-option-check');
}
});
},1000);
ngModel.$viewValue = element.val();
};
}
};
}
]
);
The problem I'm having is that sometimes the select options don't load (seems to be on pc's with a slower internet connection.
Any known issues with this?
Thanks in advance
I had a similar problem. It was solved when change ng-model after data loading.
$scope.subareas = {};
$http.get('subareas.php')
.success(function(response)
{
$scope.subareas = response;
$scope.SubArea = [];
});

How to create a AngularJS jQueryUI Autocomplete directive

I am trying to create a custom directive that uses jQueryUI's autocomplete widget. I want this to be as declarative as possible. This is the desired markup:
<div>
<autocomplete ng-model="employeeId" url="/api/EmployeeFinder" label="{{firstName}} {{surname}}" value="id" />
</div>
So, in the example above, I want the directive to do an AJAX call to the url specified, and when the data is returned, show the value calculated from the expression(s) from the result in the textbox and set the id property to the employeeId. This is my attempt at the directive.
app.directive('autocomplete', function ($http) {
return {
restrict: 'E',
replace: true,
template: '<input type="text" />',
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
elem.autocomplete({
source: function (request, response) {
$http({
url: attrs.url,
method: 'GET',
params: { term: request.term }
})
.then(function (data) {
response($.map(data, function (item) {
var result = {};
result.label = item[attrs.label];
result.value = item[attrs.value];
return result;
}))
});
},
select: function (event, ui) {
ctrl.$setViewValue(elem.val(ui.item.label));
return false;
}
});
}
}
});
So, I have two issues - how to evaluate the expressions in the label attribute and how to set the property from the value attribute to the ngModel on my scope.
Here's my updated directive
(function () {
'use strict';
angular
.module('app')
.directive('myAutocomplete', myAutocomplete);
myAutocomplete.$inject = ['$http', '$interpolate', '$parse'];
function myAutocomplete($http, $interpolate, $parse) {
// Usage:
// For a simple array of items
// <input type="text" class="form-control" my-autocomplete url="/some/url" ng-model="criteria.employeeNumber" />
// For a simple array of items, with option to allow custom entries
// <input type="text" class="form-control" my-autocomplete url="/some/url" allow-custom-entry="true" ng-model="criteria.employeeNumber" />
// For an array of objects, the label attribute accepts an expression. NgModel is set to the selected object.
// <input type="text" class="form-control" my-autocomplete url="/some/url" label="{{lastName}}, {{firstName}} ({{username}})" ng-model="criteria.employeeNumber" />
// Setting the value attribute will set the value of NgModel to be the property of the selected object.
// <input type="text" class="form-control" my-autocomplete url="/some/url" label="{{lastName}}, {{firstName}} ({{username}})" value="id" ng-model="criteria.employeeNumber" />
var directive = {
restrict: 'A',
require: 'ngModel',
compile: compile
};
return directive;
function compile(elem, attrs) {
var modelAccessor = $parse(attrs.ngModel),
labelExpression = attrs.label;
return function (scope, element, attrs) {
var
mappedItems = null,
allowCustomEntry = attrs.allowCustomEntry || false;
element.autocomplete({
source: function (request, response) {
$http({
url: attrs.url,
method: 'GET',
params: { term: request.term }
})
.success(function (data) {
mappedItems = $.map(data, function (item) {
var result = {};
if (typeof item === 'string') {
result.label = item;
result.value = item;
return result;
}
result.label = $interpolate(labelExpression)(item);
if (attrs.value) {
result.value = item[attrs.value];
}
else {
result.value = item;
}
return result;
});
return response(mappedItems);
});
},
select: function (event, ui) {
scope.$apply(function (scope) {
modelAccessor.assign(scope, ui.item.value);
});
if (attrs.onSelect) {
scope.$apply(attrs.onSelect);
}
element.val(ui.item.label);
event.preventDefault();
},
change: function () {
var
currentValue = element.val(),
matchingItem = null;
if (allowCustomEntry) {
return;
}
if (mappedItems) {
for (var i = 0; i < mappedItems.length; i++) {
if (mappedItems[i].label === currentValue) {
matchingItem = mappedItems[i].label;
break;
}
}
}
if (!matchingItem) {
scope.$apply(function (scope) {
modelAccessor.assign(scope, null);
});
}
}
});
};
}
}
})();
Sorry to wake this up... It's a nice solution, but it does not support ng-repeat...
I'm currently debugging it, but I'm not experienced enough with Angular yet :)
EDIT:
Found the problem. elem.autocomplete pointed to elem parameter being sent into compile function. IT needed to point to the element parameter in the returning linking function. This is due to the cloning of elements done by ng-repeat. Here is the corrected code:
app.directive('autocomplete', function ($http, $interpolate, $parse) {
return {
restrict: 'E',
replace: true,
template: '<input type="text" />',
require: 'ngModel',
compile: function (elem, attrs) {
var modelAccessor = $parse(attrs.ngModel),
labelExpression = attrs.label;
return function (scope, element, attrs, controller) {
var
mappedItems = null,
allowCustomEntry = attrs.allowCustomEntry || false;
element.autocomplete({
source: function (request, response) {
$http({
url: attrs.url,
method: 'GET',
params: { term: request.term }
})
.success(function (data) {
mappedItems = $.map(data, function (item) {
var result = {};
if (typeof item === "string") {
result.label = item;
result.value = item;
return result;
}
result.label = $interpolate(labelExpression)(item);
if (attrs.value) {
result.value = item[attrs.value];
}
else {
result.value = item;
}
return result;
});
return response(mappedItems);
});
},
select: function (event, ui) {
scope.$apply(function (scope) {
modelAccessor.assign(scope, ui.item.value);
});
elem.val(ui.item.label);
event.preventDefault();
},
change: function (event, ui) {
var
currentValue = elem.val(),
matchingItem = null;
if (allowCustomEntry) {
return;
}
for (var i = 0; i < mappedItems.length; i++) {
if (mappedItems[i].label === currentValue) {
matchingItem = mappedItems[i].label;
break;
}
}
if (!matchingItem) {
scope.$apply(function (scope) {
modelAccessor.assign(scope, null);
});
}
}
});
}
}
}
});

Resources