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/
Related
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
How to disable special characters in angular js input tag. Only allow alphanumeric
just like we use
<input type="text" ng-trim="false" style="text-transform: uppercase" ng-pattern="/^[a-zA-Z0-9]*$/" class="form-text" id="pan_card_number" name="pan_card_number" ng-minlength="10" maxlength="10" required ng-model="registration.newTSP.panCardNumber">
you can use Regex with Ng-pattern and Display the message through ng-message
$scope.useOnlySpecialCharacters = /^[a-zA-Z0-9]*$/;
<input type="text" ng-model="specialcharacters"
ng-pattern="useOnlySpecialCharacters" />
show message through ng-message
<div ng-message="pattern"> Please Enter AlphaNumeric </div>
OR
Best Option is to use Directives
app.directive('noSpecialChar', function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
if (inputValue == null) {
return '';
}
var cleanInputValue = inputValue.replace(/[^\w\s]/gi, '');
if (cleanInputValue != inputValue) {
modelCtrl.$setViewValue(cleanInputValue);
modelCtrl.$render();
}
return cleanInputValue;
});
}
}
});
LINK
use ng-pattern="/[A-Z]{5}\d{4}[A-Z]{1}/i" in your HTML input tag
use the following
Controller
$scope.panCardRegex = '/[A-Z]{5}\d{4}[A-Z]{1}/i';
HTML
<input type="text" ng-model="abc" ng-pattern="panCardRegex" />
Use Directives to restrict Special characters:
angular.module('scPatternExample', [])
.controller('scController', ['$scope', function($scope) {
}])
.directive('restrictSpecialCharactersDirective', function() {
function link(scope, elem, attrs, ngModel) {
ngModel.$parsers.push(function(viewValue) {
var reg = /^[a-zA-Z0-9]*$/;
if (viewValue.match(reg)) {
return viewValue;
}
var transformedValue = ngModel.$modelValue;
ngModel.$setViewValue(transformedValue);
ngModel.$render();
return transformedValue;
});
}
return {
restrict: 'A',
require: 'ngModel',
link: link
};
});
<input type="text" ng-model="coupon.code" restrict-Special-Characters-Directive>
set pattern to allow only alphanumeric
/^[a-z0-9]+$/i
I am coding a filter that will format phone numbers in a contact form I've built however for some reason the value in the input is never being updated and I'm not sure what I'm doing wrong.
Here's my HTML:
<div class='form-group'>
<input name='phone' ng-model='home.contact.phone' placeholder='(Area code) Phone' required ng-bind='home.contact.phone | phone' />
</div>
and here's my filter:
(function () {
'use strict'
angular
.module('Allay.phoneFilter', [])
.filter('phone', function () {
return function (phone) {
if(!phone) return '';
var res = phone + '::' // Since this isn't working, I'm doing something super simple, adding a double colon to the end of the phone number.
return res;
}
});
})();
I'm not sure if you need this, but here's the controller:
(function () {
'use strict'
angular
.module('Allay', [
'Allay.phoneFilter'
])
.controller('HomeController', function () {
var home = this;
});
})();
If I add an alert(res) before 'return res' in the filter I see the value I expect '123::', however the value in the input it's self is still just 123.
You need create directive to change your ngModel, like this:
.directive('phoneFormat', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var setvalue = function() {
elem.val(ctrl.$modelValue + "::");
};
ctrl.$parsers.push(function(v) {
return v.replace(/::/, '');
})
ctrl.$render = function() {
setvalue();
}
elem.bind('change', function() {
setvalue();
})
}
};
});
Use in html:
<input name='phone' ng-model='contact.phone' placeholder='(Area code) Phone' required phone-format />
JS Fiddle: http://jsfiddle.net/57czd36L/1/
Your usage of ngBind on the input is not quite correct. From the documentation,
The ngBind attribute tells Angular to replace the text content of the specified HTML element with the value of a given expression, and to update the text content when the value of that expression changes
You do not need to replace the text content of the <input> element, that wouldn't make sense. You can instead extend the formatter pipeline of the NgModelController using a directive like
app.directive('phoneFormat', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
ngModel.$formatters.push(function (value) {
if (value)
return value + '::';
});
}
}
});
Then, in your HTML,
<input ng-model='home.contact.phone' phone-format />
In case you wanted to keep the filter you wrote (for other usages), you can actually re-use it in the directive like
app.directive('phoneFormat', [ '$filter', function ($filter) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
ngModel.$formatters.push($filter('phone'));
}
}
}]);
$filter('phone') simply returns the filter function registered under 'phone'. Here is a Plunker.
Note, this solution will only format data when you change the $modelValue of the NgModelController, for example like
$scope.$apply('home.contact.phone = "123-456-7890"');
If you are looking for something to update/format the value of the input as the user is typing, this is a more complicated task. I recommend using something like angular-ui/ui-mask.
Although a filter module is a good approach, I use an 'A' directive to do the dirty work because changing the element value will affect its ng-model.
However, I would only suggest this kind of solution if your actual data manipulation could sum in 3-4 lines of code; otherwise, a more thorough approach is needed.
This is an example that will delete anything which isn't an integer:
(function () {
'use strict'
angular.module('Allay').directive('phoneValidator', function () {
return {
restrict: 'A',
link: function(scope, element, attrs) {
angular.element(element).on('keyup', function() {
element.val(element.val().replace(/[^0-9\.]/, ''));
});
}
}
});
})();
And than in your HTML template :
<input name="phone" ng-model="home.contact.phone" placeholder="(Area code) Phone" phoneValidator required/>`
You should remove your "ng-bind" cause you are filtering it and what is presented is what in the ng-model. use value instead.
<input name='phone' ng-model='home.contact.phone | phone' value="{{contact.phone | phone}}" />
see working example: JsFiddle
Why value of the ng-model is not updated with the expression. Before ng-model is defined value get updated
Value will be updated as soon as phase2 or phase3 changes
<input type="text" name="phase1" value="{{phase2 - phase3}}" ></input>
Value will not be updated
<input type="text" name="phase1" value="{{phase2 - phase3}}" ng-model="phase1"></input>
So I think of writing a directive which will evaluate the expression inside the directive and updated the output to model,
Here is html it will look like
<input type="text" name="phase1" ng-model="phase1" my-value="{{phase2 - phase3}}" my-model-value></input>
Directive:
myApp.directive('myModelValue', function(){
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
value: '#myValue'
},
link: function (scope, element, attr, controller) {
scope.model = scope.value;
}
};
});
This directive evaluate only at load time, but I want to continuously update/watch as the dependent fields (phase2 & phase3) changes.
I can update value from controller but I want to do it from html. Please help me, it it possible or against the working of angular
Thanks guys I figure out what I wanted to do. Here is the my final simple but useful directive :)
app.directive('myModelValue', function () {
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel'
},
link: function (scope, element, attr, controller) {
attr.$observe('myModelValue', function (finalValue) {
scope.model = finalValue;
});
}
};
});
Usage:
<input type="text" ng-model="phase1" my-model-value="{{phase2 - phase3}}"></input>
<input type="text" ng-model="phase1.name" my-model-value="{{valid angular expression}}"></input>
In order to continously watch the phase2/3 changes you can make use of $scope.$watch function.
Something like this will work for you:
link: function (scope, element, attr, controller) {
scope.$watchCollection('[phase1,phase2]', function() {
//whatever you want to do over here }
and in the scope pass phase1 and phase2 values as well
This will watch the value expression and update the same when value will change
myApp.directive('myModelValue', function(){
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
value: '#myValue'
},
link: function (scope, element, attr, controller) {
scope.$watch('value',function(newValue){
console.log(newValue);
});
}
};
});
here value is a local scope so it will watch the expression
I have created a directive for auto focus on text box
(function () {
'use strict';
angular.module('commonModule').directive('srFocuson',function(){
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs) {
scope.$watch(attrs.focusMe, function (value) {
if (value === true) {
console.log('value=', value);
element[0].focus();
scope[attrs.focusMe] = false;
}
});
}
};
});
})();
And now i want to bind that directive to my text box.I have tried to bind to input field but its not working.
<input placeholder="SR ID, SSN/ITIN, or School ID" sr-focuson="focusMe" type="text"
id="form_ID" name="searchId" autofocus
data-ng-model="vm.searchCriteria.searchId"
maxlength="20" class="form-control">
http://plnkr.co/edit/A39duXhGvCedAaVuB3uQ?p=preview
I made working fiddle with your idea. http://jsfiddle.net/fLaAG/
It's sort of unclear where you would be updating scope.focusMe so I made an explicit button that would set that value to true.
<button type="button" ng-click="Focus()" type="button">Focus</button>
...
$scope.Focus = function() {
$scope.focusMe = true;
};
Also I'm setting up an isolate scope, so I can just watch string I give it.
scope: {
focusMe: '=focusOn'
},
Hope this helps
Here is a method using built-in angular functionality, dug out from the murky depths of the angular docs. Notice how the "link" attribute can be split into "pre" and "post", for pre-link and post-link functions.
Working Example: http://plnkr.co/edit/Fj59GB
// this is the directive you add to any element you want to highlight after creation
Guest.directive('autoFocus', function() {
return {
link: {
pre: function preLink(scope, element, attr) {
console.debug('prelink called');
// this fails since the element hasn't rendered
//element[0].focus();
},
post: function postLink(scope, element, attr) {
console.debug('postlink called');
// this succeeds since the element has been rendered
element[0].focus();
}
}
};
});
<input value="hello" />
<!-- this input automatically gets focus on creation -->
<input value="world" auto-focus />
Full AngularJS Directive Docs: https://docs.angularjs.org/api/ng/service/$compile