How to set input field validation in reusable custom directive? - angularjs

I'm trying to make a reusable custom directive that will validate date in input field. Code provided below is working, however is not reusable at all which is my biggest concern.
What I was trying to do, was to set a new scope in directive however I got an error:
Multiple directives requesting isolated scope.
So I guess isolated scope is not going to help me.
Any other solutions?
That's my first template:
<form ng-submit="add()" name="addTask" class="form-horizontal">
<input name="dateInput" is-date-valid type="text" class="form-control" ng-model="task.DueDate" datepicker-options="datepicker.options" ng-model-options="{ timezone: 'UTC' }" uib-datepicker-popup="mediumDate" is-open="isOpened" required>
</form>
That's my second template:
<form ng-submit="edit()" name="editTask" class="form-horizontal">
<input name="dateInput" is-date-valid type="text" class="form-control" ng-model="task.DueDate" datepicker-options="datepicker.options" ng-model-options="{ timezone: 'UTC' }" uib-datepicker-popup="mediumDate" is-open="isOpened" required>
</form>
And that's my custom directive:
function isDateValid($log) {
'ngInject';
var directive = {
restrict: 'A',
require: 'ngModel',
link: link
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.$watch(attrs.ngModel, function () {
var validation = can_i_get_this_from_controller ?
if (validation) {
ctrl.$setValidity('validation', true);
} else {
ctrl.$setValidity('validation', false);
}
});
}
}
module.exports = isDateValid;

The way you implemented the custom validator is not good, you should be doing something like this -
.directive('dateValidate', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ngModel) {
ngModel.$validators.dateValidate = function(modelValue) {
//Your logic here, return true if success else false
}
}
};
});
It can be used on both form paths, so no need of that logic here.
To know more about these this is one good resource

Related

AngularJS Custom Directive - Match More Than One Field?

I have a custom directive obtained from https://github.com/neoziro/angular-match that matches two form fields. However, how can I customize it to match more than one field? Here is better explanation of what I mean:
-Form Field 1
-Form Field 2
-Form Field 3
-Form Field 4
-Confirmation (I want this one to match either Field 1,2,3 OR 4.)
Currently, I can only match it up to one field.
HTML Form:
<input type="text"
name="correctAnswer"
ng-model="quiz.quizData.correctAnswer"
match="answer1">
<div ng-show="theQuiz.correctAnswer.$error.match && !theQuiz.correctAnswer.$pristine">Answers do not match!</div>
Directive:
angular.module('match', []).directive('match', ['$parse', matchDirective]);
function matchDirective($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
scope.$watch(function () {
return [scope.$eval(attrs.match), ctrl.$viewValue];
}, function (values) {
ctrl.$setValidity('match', values[0] === values[1]);
}, true);
}
};
}
It might be easier to write your own directive for this, especially since angular-match plugin is no longer maintained.
To watch multiple form inputs, just pass the ng-model of each desired input to the directive. Here I called it match.
<input type="text" name="firstNameOne" ng-model="firstNameOne"/>
<input type="text" name="firstNameTwo" ng-model="firstNameTwo"/>
<input type="text" name="firstNameThree" ng-model="firstNameThree"/>
<input type="text" name="confirmFirstName" ng-model="confirm" match="{{[firstNameOne, firstNameTwo, firstNameThree]}}"/>
Now for the directive
app.directive('match', function() {
return {
restrict: 'A',
controller: function($scope) {
$scope.doValidation = function(matches) {
//Validation logic.
}
},
link: function(scope, element, attrs) {
scope.$watch('confirm', function() {
scope.matches = JSON.parse(attrs.match); //Parse the array.
scope.doValidation(scope.matches); //Do your validation here.
});
}
}
});
Here is a fiddle showing validation of form inputs: https://jsfiddle.net/cpgoette/und9t5ee/

Angular directive for form elements

I want to create directive which will be used for form elements like input,textarea,select...
My code:
app.directive('input', function() {
return {
restrict: 'E',
priority: -1000,
require: '^?required',
link: function (scope, element, attrs, ctrl) {
element.on('focus', function (e) {
element.addClass('validate');
});
}
};
});
When I try to use common directive it doesn't work but don't have idea why...
<input common-directive type="text" name="name" placeholder="Firstname" ng-model="profile.name" ng-minlength="2" required />

AngularJS Autocomplete IE9 Issue

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 />

AngularJS: Model not updating when using directive OR model-options

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;
});
}
};
}
]);

Custom angularjs validator does not have up-to-date view

In angularjs 1.3.15 I have a custom validator which uses its attribute value:
sApp.directive('customValidator', [ '$q', function($q){
return {
require: 'ngModel',
link : function(scope, elm, attrs, ctrl){
ctrl.$asyncValidators.customValidator= function(modelValue)
{
// I need the value of "attrs.customValidator"
};
}
};
}]);
I use this validator in the following way:
<form ... ng-show="data">
<input ... custom-validator='{{data}}'/>
</form>
data is some data being asynchronously loaded by a web-service. Unfortunately when the validator is called the attribute value is not available yet (despite ng-show="data"). So i call scope.$apply() inside the validator. This solves my problem but I get the error "$digest already in progress".
Does anybody know a better solution?
I think a synchronous formatter/parser will suffice. Here is a plunker
Here is the directive:
app.directive('exactlyEquals', function() {
return {
require: "ngModel",
link: function(scope, elm, attrs, ctrl) {
var validator = function(value) {
ctrl.$setValidity('exactlyEquals', value == attrs.exactlyEquals);
return value;
};
ctrl.$parsers.unshift(validator);
ctrl.$formatters.unshift(validator);
}
};
});
And its usage:
<form name="theForm">
<input type="text" exactly-equals="test2" ng-model="data.name" />
</form>
{{ theForm.$valid }}
#user3632710 ng-if="data" did the job. My code looks now like this:
<form ng-controller="MyController">
<div ng-if="data"> <!-- ng-if cannot by on same DOM as controller -->
<input ... custom-validator='{{data}}'/>
</div>
</form>

Resources