I'm hoping this isn't a duplicate - plenty of similar questions about but I can't find an answer that works.
I have an Angular directive, thus:
app.directive('emailInput', function(){
return {
restrict: 'E',
templateUrl: 'template.html',
link: function(scope, elem, attrs, ctrl){
elem.bind('keyup', function(){
// TODO - what?
})
}
}
}
and in the template html:
<input type="email" required ng-model="emailAddress" />
Without knowing the name of the form, inside the link function, I want to know the value of the emailAddress.$valid property - how can I get this?
You can require the formController which would give you access to all of the inputs registered to the form
app.directive('emailInput', function(){
return {
require: '^form', // We look for it on a parent, since it will be defined somewhere higher on the DOM.
restrict: 'E',
templateUrl: 'template.html',
link: function(scope, elem, attrs, ctrl){
elem.bind('keyup', function(){
ctrl.emailAddress.$valid //check validity
})
}
}
}
Remember that Angular keeps track of input elements by name. So you have to give your input a name attribute
<input type="email" required ng-model="emailAddress" name="emailAddress" />
I would also recommend possibly just passing all of this through a directive attribute. You probably don't want to hard code the field names. So you could just have an attribute that takes the validity
inputIsValid='formName.emailAddress.$valid'
And evaluate (or $watch it) in your directive.
We can check validity more easily without knowing the name of input elements.
app.directive('emailInput', function(){
return {
require: '^form', // We look for it on a parent, since it will be defined somewhere higher on the DOM.
restrict: 'E',
templateUrl: 'template.html',
link: function(scope, elem, attrs, ctrl){
elem.bind('keyup', function(){
ctrl.$valid //check validity here
})
}
}
}
This is an old post but for the people who get here by googling, this is the cleanest way to check validity of an input in your directive without knowing its name, so you can use your directive on any input element.
You just need to require the ngModelcontroller:
app.directive('emailInput', function(){
return {
require: 'ngModel'
restrict: 'E',
templateUrl: 'template.html',
link: function(scope, elem, attrs, ngModelCtrl){
elem.bind('keyup', function(){
ngModelCtrl.$valid //check validity
})
}
}
}
See the AngularJS document for ngModel.NgModelController, $valid under the Properties section:
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Know it's an old thread but if someone runs into this problem this is how I solved it:
app.directive('emailInput', function(){
return {
restrict: 'E',
templateUrl: 'template.html',
controller:function($scope){
$scope.isInvalid = function(){
return $scope.myelem.data().$ngModelController.$invalid;
}
},
link: function(scope, elem, attrs){
$scope.myelem = $(elem).find("input");
}
}
}
Here is a directive that will set dirty to true even if nothing has been typed in.
By default $dirty is set if something is typed in and wouldn't show a required error message until the user submits. With this
function() {
return function (scope, element, attrs) {
$(element).blur(function () {
scope.$apply(function() {
var field = scope.$eval(attrs.makeDirty);
field.$dirty = true;
});
});
};
HTML:
<label class="fieldLabel" for="confirmEmail">Confirm Email*</label>
<input type="text" id="confirmEmail" name="confirmEmail" ng-model="confirmEmail" ng-pattern="{{Ctrl.Model.Email.EmailAddress}}" required make-dirty="form.confirmEmail">
<span class="error" ng-show="form.confirmEmail.$error.pattern && form.confirmEmail.$dirty">Emails must match</span>
<span class="error" ng-show="form.confirmEmail.$error.required && (form.$submitted || form.confirmEmail.$dirty)">Confirm your email</span>
That allows me to give feedback as the user is filling out or tabbing on the form.
Let me give you another way to do it, it can be useful in some cases
link: function (scope, element, attrs, formCtrl) {
scope.fileSizeError=false;
scope.$watch(function () {
return formCtrl.fileP.$error.maxSize;
},function(newValue) {
scope.fileSizeError=newValue;
});
}
In my case I was inside a directive that is used to upload a file so I needed to know the state of the var $error.maxSize in the template so I did in that way.
Related
I've written a directive with an isolate scope.
app.directive('myDirective', function() {
return {
restrict: 'E',
scope {
attr1: '#',
attr2: '#',
noValueAttr: // what to put here?
},
link: function(scope, elem, attrs) {
// how to check here if noValueAttr is present in mark-up?
}
};
});
the html could be
<my-directive attr1='...' attr='...' ... no-value-attr>
or
<my-directive attr1='...' attr='...' >
I am wondering how to use (and have the directive detect if it's there or not) an optional attribute which has no assigned value. Thanks.
Just use attrs.hasOwnProperty('noValueAttr') in the link function to test whether the attribute is present or not.
Don't forget the attribute in markup would be no-value-attr, not noValueAttr like you showed.
link: function(scope, elem, attrs) {
if (attrs.hasOwnProperty('noValueAttr'))
// attribute is present
else
// attribute is not present
}
I know it is an old question, but there is that way to:
link: function(scope, elem, attrs) {
scope.noValueAttr = scope.$eval(attrs.noValueAttr) || 'default value';
}
Say I have the following directive:
myApp.directive('myDirective', function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs, ngModelCtrl) {
scope.$watch('ngModel', function() {
ngModelCtrl.$modelValue = 'foo';
});
}
}
});
And the following html:
<input ng-model="name" my-directive></input>
Basically, whenever the user changes the input, my-directive would ideally change the internal model value to "foo" while leaving the view value untouched.
But when I print out $scope.name in the corresponding controller, it doesn't log "foo", it logs whatever the user entered in.
It would seem that ngModelCtrl.$modelValue is not what the controller is accessing -- am I approaching this problem incorrectly?
(Also watching the ngModel in the scope feels really wrong, but I'm not sure of any other way. Any suggestions would be much appreciated!)
If you are looking for view change, you should never register a watch. ngModelController's $viewChangeListeners are specifically designed for this purpose and to avoid creating any additional watch on the ngModel. You can also remove 2 way binding set up on the ngModel.
I can think of this way.
.directive('myDirective', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
/*Register a viewchange listener*/
ngModelCtrl.$viewChangeListeners.push(function(){
/*Set model value differently based on the viewvalue entered*/
$parse(attrs.ngModel).assign(scope, ngModelCtrl.$viewValue.split(','));
});
}
}
});
Demo
While thinking about it the other way around (Credits #Cody) it becomes more concise and appropriate while using a $parser.
ngModelCtrl.$parsers.push(function(val) { return val.split(',') });
I have an attribute directive that I use on an input=text tag like this:
<input type="text" ng-model="helo" my-directive />
On my directive I'm trying to use the ngModelController to save the initial value of my input, in this case the value of the ng-model associated with it.
The directive is like this:
app.directive('myDirective', function () {
return {
restrict: "A",
scope: {
},
require: "ngModel",
link: function (scope, elm, attr, ngModel) {
console.log("hi");
console.log(ngModel.$modelValue);
console.log(ngModel.$viewValue);
console.log(elm.val());
}
}
});
The problem is that ngModel.$modelValue is empty maybe because at the time the directive is initialized the ngModel wasn't yet updated with the correct value. So, how can I store on my directive the first value that is set on my input field?
How to correctly access ngModel.$modelValue so that it has the correct value?
I'll also appreciate an explanation on why this isn't working as I'm not clearly understanding this from reading the docs.
Plunkr full example: http://plnkr.co/edit/QgRieF
Use $watch in myDirective
app.directive('myDirective', function () {
return {
restrict: "A",
scope: {
},
require: "ngModel",
link: function (scope, elm, attr, ngModel) {
var unwatch = scope.$watch(function(){
return ngModel.$viewValue;
}, function(value){
if(value){
console.log("hi");
console.log(ngModel.$modelValue);
console.log(ngModel.$viewValue);
console.log(elm.val());
unwatch();
}
});
}
}
});
For Demo See This Link
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
So, I need a validator that will check to make sure my password_confirmation field matches my password field. I ended up with the following directive:
Directive
#app.directive 'matches', ->
require: 'ngModel', #Needed for validation bits
scope: { matched_value: '=matches' } #Looks up the value of the scope element we're matching against and keeps it bound
link: (scope, elem, attrs, ctrl) ->
ctrl.$parsers.unshift (view_value) -> #Add a new parser that updates the validity
ctrl.$setValidity(elem.attr('name'), view_value == scope.matched_value)
Form
<form name="form">
<input ng-model="new_user.password" name="password">
<input ng-model="password_confirmation" name="password_confirmation" matches="new_user.password">
</form>
This works fine when the user uses the form from top to bottom. However, if they go on to change the password after they've filled in the password_confirmation then it doesn't become invalid like it should.
My first stab looked added a $watcher to the matches directive, but I can't seem to get the correct value of the new password_confirmation input.
Directive w/ Watcher (CoffeeScript)
#app.directive 'matches', ->
require: 'ngModel',
scope: { matched_value: '=matches' }
link: (scope, elem, attrs, ctrl) ->
ctrl.$parsers.unshift (view_value) ->
ctrl.$setValidity(elem.attr('name'), view_value == scope.matched_value)
scope.$watch attrs['watches'], ->
ctrl.$setValidity(elem.attr('name'), ctrl.$view_value == scope.matched_value)
But ctrl.$view_value is always undefined, leading me to believe that I'm doing this wrong.
How would I get the actual value? Is this the correct way to set up this sort of inverse relationship? Is there a better way to do this?
https://github.com/wongatech/angular-confirm-field is a good project for this.
Example here http://wongatech.github.io/angular-confirm-field/
The code below shows 2 input fields with the implemented functionality
<input ng-model="email" name="my-email" />
<input ng-confirm-field ng-model="emailconfirm" confirm-against="email" name="my-email-confirm"/>
This directive differs from other implementations in the way it uses $parsers and $watch to ensure the model property is only updated when the view value is valid.
You can watch both elements from the one directive. Pass a function to watch. Each digest, it will compare values instead of only when the one element changes.
.directive('match', function () {
return {
require: 'ngModel',
restrict: 'A',
scope: {
match: '='
},
link: function(scope, elem, attrs, ctrl) {
scope.$watch(function() {
var modelValue = ctrl.$modelValue || ctrl.$$invalidModelValue;
return (ctrl.$pristine && angular.isUndefined(modelValue)) || scope.match === modelValue;
}, function(currentValue) {
ctrl.$setValidity('match', currentValue);
});
}
};
});
With (ctrl.$pristine && angular.isUndefined(ctrl.$modelValue)), it will not add the error until the field is touched or something is in it (pre-filled).
More info: https://github.com/TheSharpieOne/angular-input-match
Demo: http://jsfiddle.net/TheSharpieOne/Wnv8u/
For version 1.3.x of angular, the new validation pipeline works best, here is the version for angular 1.3.x
.directive('match', function () {
return {
require: 'ngModel',
restrict: 'A',
scope: {
match: '='
},
link: function(scope, elem, attrs, ctrl) {
scope.$watch('match', function(pass){
ctrl.$validate();
});
ctrl.$validators.match = function(modelValue){
return (ctrl.$pristine && (angular.isUndefined(modelValue) || modelValue === "")) || modelValue === scope.match;
};
}
};
});