Angular: directive access to form element - javascript

I have a form label containing an input:
<label data-live-email-check="http://www.example-service-uri.com/">
<span class="embedded-label">Email</span>
<input ng-model="formData.email"
type="email"
name="email"
placeholder="Email"
required/>
<span class="message" ng-show="emailValidationMessage">{{emailValidationMessage}}</span>
</label>
I want to create a directive that takes the URL provided by the data-live-email-check attribute and sends the email to that URL, validating whether or not it already exists.
angular.module("App").directive("liveEmailCheck", [function () {
return {
restrict: "A",
scope: {
ngModel: "="
},
link: function (scope, element, attrs) {
scope.$watch(
function(){
return scope.ngModel
},
function(newVal){
console.log(newVal);
}
);
}
}
}]);
I just want to watch the model on the input, and fire a request when it updates.
Since the directive is defined on the label element ngModel is not properly bound. What am I doing wrong? My watch expression is not logging anything because it never fires.
I know I could manually grab the input, but that just seems like I'd be breaking the "angular pattern" that I feel so "bound" by. The frustrating thing is that there seems to be so many ways to do everything in Angular that I can never tell if I'm approaching a problem correctly or not.
--Edit--
To provide the solution that I personally would take (out of ignorance of a "better" way), would be the following:
angular.module("App").directive("liveEmailCheck", [function () {
return {
restrict: "A",
require: ["^form"],
link: function (scope, element, attrs, ctrl) {
var formCtrl = ctrl[0];
scope.formEl = formCtrl[element.find("input").attr("name")];
scope.$watch(function(){return scope.formEl.$valid},
function(newVal){
console.log(newVal);
});
}
}
}]);
This WORKS, but I feel like it "breaks the angular rules".

A custom validation is written like this:
'use strict';
angular.module('App')
.directive('liveEmailCheck', function (){
return {
require: 'ngModel',
link: function (scope, elem, attr, ngModel){
ngModel.$validators.liveEmailCheck= function (value){
//Your logic here or in a factory
};
}
};
});
and then on your HTML it goes like
<input type="email" live-email-check="liveEmailCheck>
Basically you add your own validation to the set of build-in validations of angular.

What you need here is an ng-model asyncValidator. Here is a simple implementation of such a directive.
angular.module("App").directive('asyncEmailValidator', function ($http) {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var emailValidationUrl = attrs.asyncEmailValidator;
ngModel.$asyncValidators.emailValidator = function (modelValue, viewValue) {
var value = modelValue || viewValue;
// NOTE: don't forget to correctly add the value to the url
return $http(emailValidationUrl + value).then(function (validationResponse) {
// NOTE: return rejected promise if validation failed else true
});
};
}
};
});
How you can use it in your case:
<label>
<span class="embedded-label">Email</span>
<input ng-model="formData.email"
async-email-validator="http://www.example-service-uri.com/"
type="email"
name="email"
placeholder="Email"
required/>
<span class="message" ng-show="<FormNameHere>.email.$error.emailValidator">
{{emailValidationMessage}}
</span>
</label>
This will be the right solution because it is implemented with angular ng-model validation which considers the validity of the model too.

Related

Using angular form in directive template

I am using require: '^form' in my simple directive.
Then I trying to use this form in an ng-show but it doesn't seem to work.
Note: I don't want to pass the form name in as an attribute.
Can anyone see where i am going wrong? I only want the message to show when the form is invalid.
angular.module('xxx').directive('errorWall', errorWall);
function errorWall() {
return {
restrict: 'E',
require: '^form',
scope: {},
link: (scope, elm, attrs, frm) => {
scope.formCtrl = frm;
},
template: '<div ng-show="formCtrl.$invalid">You have error messages.</div>'
};
}
Make sure you've placed the directive inside the form with at least one input with a ng-model directive on it.
<form>
<input type="text" ng-model="name" required />
<error-wall></error-wall>
</form>
Here's a working fiddle https://jsfiddle.net/3gv8nvL3/3/ with one form required input.

Custom validation with AngularJS isn't triggered

I'm a beginner with AngularJS and for now I'm trying to create a registration form.
I tried to follow the instructions I found on the internet, but it looks like I cannot get my validation triggered. I'm not sure what is wrong.
Here's my JS for the validation:
var login = angular.module('login', []);
login.directive('repeatedValue', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$validators.repeatedValue = function (modelValue, viewValue) {
alert("validation");
return false;
};
}
};
});
and the input field looks like this:
<input type="password" class="form-control" id="password2"
placeholder="Repeat password" value="" tabindex="3" ng-model="login.registerPassword2" repeatedValue/>
still, for some reason, even if I start typing into the field, the validation is not triggered and as a result I can still submit the form.
you must use right directive name in template (dash-delimited)
<input type="password" ... repeated-value/>
See Normalization chapter in docs https://docs.angularjs.org/guide/directive

Calling angularjs function on text input based on length

I have a text box. I would like to call a method inside controller only when user has filled in 'n' or more number of characters in the textbox.
Can someone please give me pointers on how to approach this?
Thanks
Id recommend just using ngChange and binding to an evaluation function. Below is a sample
angular.module('inputChange', [])
.controller('TextInputController', ['$scope', function ($scope) {
var inputMin = 3;
$scope.someVal = '';
$scope.result = '';
$scope.textChanged = function() {
if ($scope.someVal.length >= inputMin) executeSomething()
else $scope.result = '';
};
function executeSomething() {
$scope.result = $scope.someVal;
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="inputChange" ng-controller="TextInputController">
<input type="text" ng-model="someVal" ng-change="textChanged()" ng-Trim="false" />
<br />
someVal: <span ng-bind="someVal"></span>
<br />
Result: <span ng-bind="result"></span>
<br />
someVal Length: <span ng-bind="someVal.length"></span>
<br />
Result Length: <span ng-bind="result.length"></span>
</div>
You could simply achieve this by using ng-keyup directive
ng-keyup="(1myNgModel.length >= n) && myFunction()"
Desired function will only gets called only if length of model is greater than equal to n length
Working Plunkr
Though the better version would be having ng-model-options with debounce time, so that it will reduce number of value change. After that we can easily use ng-change directive to fire function.
<input type="text" ng-model="myNgModel"
ng-change="(myNgModel.length >= 3) && myFunction()"
ng-model-options="{ debounce: 200 }" />
Updated Demo
You can add a directive to your element and $watch for model changes. Then you can fire any logic you wish when your model has changed and has a value. In this case, lets call our model expression. Here is an example for a <textarea> element. This approach can just as well be used for an <input /> element as well.
<textarea watcher ng-model="expression"></textarea>
app.directive('watcher', [function () {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
scope.$watch(attrs.ngModel, function (v) {
if(v) {
// you have a value
} else {
// no value
}
});
}
}
}]);
JSFiddle Example
A good way to do this is to use a directive. Here's how it might be done:
view:
<div ng-app="foo" ng-controller="fooController">
<textarea text-length-handler="doThing()" text-length="6" ng-model="text">
</textarea>
</div>
js:
angular.module('foo', [])
.directive('textLength', function(){
return {
restrict: 'A',
require: 'ngModel',
scope: {
textLengthHandler: '&'
},
link: function ($scope, $element, $attrs, ctrl) {
var limit = parseInt($attrs.textLength);
var handler = function(){
if (ctrl.$modelValue.length >= limit) {
$scope.textLengthHandler()
}
};
$element.on('keypress', handler);
// remove the handler when the directive disappears
$scope.$on('destroy', function(){
$element.off('keypress', handler)
});
}
}
})
Fiddle here:
http://jsfiddle.net/dtq0mz8m/
If you tie the input field to a variable using ngModel, you can watch it from the controller (is not very elegant, though) using $watch or $observe whenever it changes, and check the length.

How to invalidate a form when using directive in AngularJS

i am working on a SPA and a form inside this app uses an input masked text box implemented using a third party library from here
i created a directive to set a mask for an IP address
angular
.module('app').directive('ipMask', [
function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
element.mask('0ZZ.0ZZ.0ZZ.0ZZ', {translation: {'Z': {pattern: /[0-9]/, optional: true}}});
element.mask('099.099.099.099');
scope.$watch(attrs.ngModel, function (newValue, oldValue) {
//????????
});
}
};
}
]);
where my form code looks like
<div ng-controller="nodesListCtrl as vm">
<form role="form" name="frmNode">
<div class="form-group">
<label>Node IP :</label>
<input type="text" data-ip-mask ng-model="vm.myIp" name="CtrIp" class="input-sm form-control" placeholder="..." >
</div>
</form>
</div>
i want to invalidate the form if the IP address is wrong. i.e. i am expecting .ng-invalid class both on the form and and the control as well until the time it remains invalid. any suggestions ?
You don't need to use $watch. Just add a parser and/or formatter. Parsers are called when the view changes and formatters when the model changes. (This link can tell you more about those, as well as the other things available on ngModelController: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController). Here's an example:
link: function (scope, element, attrs, ngModel) {
element.mask('0ZZ.0ZZ.0ZZ.0ZZ', {translation: {'Z': {pattern: /[0-9]/, optional: true}}});
element.mask('099.099.099.099');
ngModel.$parsers.unshift(function(value) {
var valid = isValid(value); // made up - use whatever validation technique based on that library
ngModel.$setValidity('ip', valid);
return valid;
});
ngModel.$formatters.unshift(function(value) {
var valid = isValid(value);
ngModel.$setValidity('ip', valid);
return valid ? value : undefined;
});
}

How to add field in ng-form, if this field is located out of ng-form (AngularJS)

I have:
<ng-form name="myForm">
<input...>
<input...>
<input...>
</ng-form>
<special-field ng-model="myField" />
and I need to add 'special-field' in ng-form using directive (It is for $dirty and $valid).
I've tried to get ngModelController of ng-form:
.directive('specialField', [function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var formCtrl = angular.element(document).find('ng-form').controller('name');
var field = element.controller('ngModel');
formCtrl.$addControl(field);
}
};
}]);
But getting controller of ng-form is not working by 'name'.
Why is 'getting by name' not working?
Are there other ways to add field to form?

Categories