I'm trying to make a directive of input elements, with the following template
<label class="item-custom item-input input">
<i class="iconic"></i> <!-- gets icon class -->
<input /> <!-- gets all necessary attributes -->
</label>
The problem is that $valid Boolean gets the 'true' at the beginning and doesn't update after.
Here is my directive:
main.directive('dtnInput', function () {
return {
controller: function ($scope, dictionary) {
$scope.d = dictionary;
},
restrict: 'EAC',
compile: function(element, attr) {
var input = element.find('input');
var icon = element.find('i');
angular.forEach({
'type': attr.type,
'placeholder': attr.placeholder,
'ng-value': attr.ngValue,
'ng-model': attr.ngModel,
'ng-disabled': attr.ngDisabled,
'icon': attr.ngIcon,
'ng-maxlength': attr.ngMaxlength,
'name': attr.name
}, function (value, name) {
if (angular.isDefined(value)) {
if (name == 'icon') {
icon.attr('class', value);
} else {
input.attr(name, value);
}
}
});
},
templateUrl: 'templates/directives/dtn-input.html'
};
});
and the HTML
<form name="myForm">
<dtn-input type="number"
placeholder="number"
name="phone"
ng-icon="icon-login"
ng-model="user.phone"
ng-maxlength="5">
</dtn-input>
<input type="text"
name="firstName"
ng-model="user.firstName"
ng-maxlength="5"
required />
<span>
value: {{user.phone}}
</span><br />
<span>
error: {{myForm.phone.$valid}}
</span>
</form>
Thanks for help!
==== UPDATE =======
I found the bug.
The problem occurred because i called to directive attributes with the names of angular directives:
<dtn-input type="number"
placeholder="number"
name="phone"
ng-icon="icon-login"
ng-model="user.phone" - //this
ng-maxlength="5"> - //and this
Related
This is my controller
function commentController($stateParams, commentFactory, $rootScope){
const ctrl = this;
ctrl.comment = {};
ctrl.decision = {};
ctrl.$onInit = function(){
ctrl.decision.replyVisible = false;
}
export {commentController}
This is my component
import {commentController} from './comment.controller'
export const commentComponent = 'commentComponent'
export const commentComponentOptions = {
bindings: {
post: '<'
},
templateUrl: 'blog/src/app/components/post/comment/comment.html',
controller: ['$stateParams', 'commentFactory', '$rootScope', commentController],
controllerAs: 'cm'
}
This is my directive
function showReply(){
return {
restrict: 'A',
scope: {
collapsed: '<'
},
link: function(scope, element, attrs){
element.on('click', ()=>{
console.log(`Trying to hide ${scope.collapsed}`);
scope.collapsed = true
console.log(`Trying to hide ${scope.collapsed}`);
})
}
}
}
function hideReply(){
return {
restrict: 'A',
scope: {
collapsed: '<'
},
link: function(scope, element, attrs){
element.on('click', ()=>{
scope.collapsed = false;
console.log(`Trying to hide scope.replyVisible is ${scope.collapsed}`);
})
}
}
}
export {showReply, hideReply}
This is my html
<div class="comment_wrapper">
<div class="comment" ng-repeat="comment in cm.post.comments">
<h3>{{comment.author.name}}</h3>
<p ng-bind-html="comment.comment"></p>
<button show-reply collapsed="cm.decision.replyVisible">Reply</button>
<div class="reply_wrapper" ng-show="cm.decision.replyVisible">
<label for="name">Name</label>
<input type="text" id="name" ng-model="cm.comment.author.name">
<label for="email">Email</label>
<input type="text" id="email" ng-model="cm.comment.author.email">
<label for="comment">Comment</label>
<textarea name="" id="comment" cols="10" rows="5" ng-model="cm.comment.comment"></textarea>
<button ng-click="cm.reply(comment._id)">Submit</button> <button hide-reply collapsed="cm.decision.replyVisible">Cancel</button>
</div>
</div>
</div>
And this is my module
import angular from 'angular'
import {showReply, hideReply} from './comment.directive'
import {commentFactory, commentFactoryFunc} from './comment.factory'
import {commentComponent, commentComponentOptions} from './comment.component'
export const comment = angular.module('comment', [uiRouter])
.factory(commentFactory, ['$http', commentFactoryFunc])
.component(commentComponent, commentComponentOptions)
.directive('showReply', showReply)
.directive('hideReply', hideReply)
.name
Here's what I am trying to do, in my html, I want to show the reply form when someone clicks on the reply button. And since there are multiple comments for with each having a reply form, I can't use the regular ng-click.
The changes made to the scope.collapsed don't get reflected on cm.decision.replyVisible
How do I make this work? I have always been confused about accessing parent controller variables from a directive.
You should use comment.decision.replyVisible instead of cm.decision.replyVisible
HTML
<div class="comment_wrapper">
<div class="comment" ng-repeat="comment in cm.post.comments">
<h3>{{comment.author.name}}</h3>
<p ng-bind-html="comment.comment"></p>
<button show-reply
collapsed="comment.decision.replyVisible">Reply</button>
<div class="reply_wrapper" ng-show="comment.decision.replyVisible">
<label for="name">Name</label>
<input type="text" id="name" ng-model="cm.comment.author.name">
<label for="email">Email</label>
<input type="text" id="email" ng-model="cm.comment.author.email">
<label for="comment">Comment</label>
<textarea name="" id="comment" cols="10" rows="5" ng-model="cm.comment.comment"></textarea>
<button ng-click="cm.reply(comment._id)">Submit</button> <button hide-reply collapsed="cm.decision.replyVisible">Cancel</button>
</div>
</div>
</div>
Here is the code I'm using for the directive:
var compareTo = function() {
return {
require: "ngModel",
scope: {
otherModelValue: "=compareTo"
},
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.compareTo = function(modelValue) {
console.log(modelValue + ":" + scope.otherModelValue);
return modelValue == scope.otherModelValue;
};
scope.$watch("otherModelValue", function() {
ngModel.$validate();
});
}
};
};
app.directive("compareTo", compareTo);
Here is my html:
<div class="form-group">
<label>Password</label>
<span>Must contain at least eight characters, including uppercase, lowercase letters and numbers</span>
<input type="password"
class="form-control"
name="password"
ng-model="signUpPass1"
ng-pattern="/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/"
required>
<div ng-messages="signUpForm.password.$error" class="formMsgContainer">
<span class="formMsg" ng-message="pattern">Passwords Does Not Meet the Criterias!</span>
</div>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password"
class="form-control"
name="conPass"
ng-model="signUpPass2"
compare-to="signUpPass1"
required>
<div ng-messages="signUpForm.conPass.$error" class="formMsgContainer">
<span class="formMsg" ng-message="compareTo">Passwords Do Not Match!</span>
</div>
</div>
However the compareTo directive doesn't work. Looking at the console log I put in the directive, it prints out the string for pass2 and undefined for pass1. Such as aaaa:undefined. This means that they will never be equal and thus there will always be an error. So there must be something wrong with the statement scope.otherModelValue but I can't seem to figure out what is wrong.
Thanks
Try this directive. I haven't tested it, so if it doesn't work, please tell me.
app.directive("compareTo", function() {
return {
require: "ngModel",
link: function(scope, element, attributes, controller) {
scope.$watch(attributes.ngModel, function(value) {
controller.$setValidity('compare', (element.val() === value));
});
}
};
});
You can then use the "compare" in the ng-message to output an error.
To create your own checks try directive use-form-error. It is easy to use and will save you a lot of time.
Live example jsfiddle
<form name="ExampleForm">
<label>Password</label>
<input ng-model="password" required />
<br>
<label>Confirm password</label>
<input ng-model="confirmPassword" required />
<div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>
Currently what I'm doing is like this for HTML,
<label class="item item-input">
<input type="text" maxlength="1" ng-model="user.name" maxlength="1" input-move-next-on-maxlength></input>
</label>
<label class="item item-input">
<input type="text" maxlength="1" ng-model="user.email" maxlength="1" input-move-next-on-maxlength></input>
</label>
and the .js looks like this
.directive("inputMoveNextOnMaxlength", function() {
return {
restrict: "A",
link: function($scope, element) {
element.on("label", function(e) {
if(element.val().length == element.attr("maxlength")) {
var $nextElement = element.next();
if($nextElement.length) {
$nextElement[0].focus();
}
}
});
}
}
})
But, the result is the auto tab is not working. I also tried removing the <label>, that worked. I'm wondering does it happen because of the input is at other class or does my code have an error.
Try with this solution, It worked for me
HTML:
<div ng-app="autofocus">
<label>Name:</label>
<input ng-model="maxLengthReach"></input>
<br/><br/>
<label>Title:</label>
<input autofocus-when></input>
</div>
Javascript:
var app = angular.module('autofocus', []);
app.directive('autofocusWhen', function () {
return function (scope, element, attrs) {
scope.$watch('maxLengthReach', function(newValue){
if (newValue.length >= 5 ) {
element[0].focus();
}
});
}
});
Adjust the length based upon your requirement
This is my code and I want to set focus on first name textbox on form submit if first name textbox has error like $error.required,$error.pattern,$error.minlength or $error.maxlength.
<form class="form-horizontal" name="clientForm" id="clientForm" novalidate ng-submit="clientForm.$valid" ng-class="{ loading:form.submitting }">
<div class="form-group">
<label class="col-lg-2 control-label">First Name</label>
<div class="col-lg-8">
<input type="text" ng-model="client.firstName" class="form-control" required autofocus name="firstName" autocomplete="off" ng-maxlength="100" ng-minlength=3 ng-pattern="/^[a-zA-Z]*$/" />
<!--<span ng-show="clientForm.firstName.$dirty && clientForm.firstName.$invalid" class="error">First Name is required.</span>-->
<div class="errors" ng-show="clientForm.$submitted || clientForm.firstName.$touched">
<div class="error" ng-show="clientForm.firstName.$error.required">
First Name is required.
</div>
<div class="error" ng-show="clientForm.firstName.$error.pattern">
Enter valid name.
</div>
<div class="error" ng-show="clientForm.firstName.$error.minlength">
First Name is required to be at least 3 characters
</div>
<div class="error" ng-show="clientForm.firstName.$error.maxlength">
First Name cannot be longer than 100 characters
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">Save</button></form>
This question is a duplicate of:
Set focus on first invalid input in AngularJs form
You can use a directive on the form:
<form accessible-form>
...
</form>
app.directive('accessibleForm', function () {
return {
restrict: 'A',
link: function (scope, elem) {
// set up event handler on the form element
elem.on('submit', function () {
// find the first invalid element
var firstInvalid = elem[0].querySelector('.ng-invalid');
// if we find one, set focus
if (firstInvalid) {
firstInvalid.focus();
}
});
}
};
});
Here is a working Plunker:
http://plnkr.co/edit/wBFY9ZZRzLuDUi2SyErC?p=preview
You'll have to handle this using directive. For example:
It will iterate through the element(s) and check if the focusNow attribute is true or not. Make sure that the error handler code sets the expression true/false.
.directive('focusNow', function ($timeout) {
return {
link: function (scope, element, attrs) {
scope.$watch(attrs.focusNow, function (value) {
if (value === true) {
for (var i = 0; i < element.length; i++) {
var ele = angular.element(element[i].parentNode);
if (!ele.hasClass('ng-hide')) { //Skip those elements which are hidden.
element[i].focus();
}
}
}
});
}
};
});
and in the HTML, you'll have:
<input type="text" focus-now="expression" />
where expression will be a normal expression which will evaluate to true in case of error.
You can try this: i.e add ng-focus="clientForm.$error" in first name input
<input type="text" ng-focus="clientForm.$invalid" ng-model="client.firstName" class="form-control" required autofocus name="firstName" autocomplete="off" ng-maxlength="100" ng-minlength=3 ng-pattern="/^[a-zA-Z]*$/" />
We have a validation directive that we use to validate the controls on Blur and viewContentLoaded event.
We persist the form values in local storage to remember the details when we navigate away from the form and come back
The problem is that, even though it remembers the details it doesn't re-validate the Firstname and Lastname when we load that view again.
Following is our original code:
<div>
<form name="form" class="form-horizontal">
<label class="control-label">First name</label>
<div class="controls">
<input id="firstName" name="FirstName" ng-model="order.FirstName" type="text" validate="alphabeticOnly" maxLength="30" required/>
<span class="help-block" ng-show="form.FirstName.$dirty && form.FirstName.$invalid">Please enter valid Firstname</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Last name</label>
<div class="controls">
<input id="lastName" name="LastName" ng-model="order.LastName" type="text" validate="alphabeticOnly" maxLength="30" required/>
<span class="help-block" ng-show="form.LastName.$dirty && form.LastName.$invalid">Please enter valid Lastname</span>
</div>
</div>
</form>
Confirm
The next function just saves the order and redirects to another page.
We have a $watch on $scope.order that saves the data in local storage to remember.
Directive:
.directive('validate', ['validationService', function(validationService) {
function validate(elm) {
var fn = elm.attr("validate");
var value = elm.val();
return validationService[fn](value);
}
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
function triggerValidations(){
checkValidity();
ctrl.$parsers.unshift(function(viewValue) {
checkValidity();
return viewValue;
});
ctrl.$formatters.unshift(function(viewValue) {
checkValidity();
return viewValue;
});
}
function checkValidity(){
if (elm.val().length > 0)
{
var isValid = validate(elm);
ctrl.$setValidity('validate', isValid);
console.log(" here i am - CheckValidity", attrs.id, elm.val(), isValid );
//scope.$apply();
}
}
$rootScope.$on('$viewContentLoaded', triggerValidations);
elm.bind('blur', function(event) {
scope.$apply(function() {
ctrl.$setValidity('validate', validate(elm));
});
});
}
};
}])
If we add $scope.apply it gives an error "$digest already in progress"
At the end, we want to validate the form when someone lands onto the page.