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 />
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 using Ionic Angularjs and want, after typing 3 characters into a input type text, to set the focus on the next input .
Can I use limitTo filter combining with another angularjs feature?
For this you can use AngularJS directives, combined with HTML5's input maxlength attribute:
The directive
angular.module('myApp', [])
.directive('focusAfter', function() {
return {
restrict: 'A',
scope: {
elemToFocus: '#'
},
link: function(scope, elem, attrs) {
var elementToFocus = document.getElementById(scope.elemToFocus);
elem.on('keyup', function() {
if(elem.val().length === 3) {
window.setTimeout(function() {
elementToFocus.focus();
});
}
});
}
};
});
View
<input type="text" maxlength="3" id="input1" focus-after elem-to-focus="input2">
<input type="text" maxlength="3" id="input2" focus-after elem-to-focus="input3">
<input type="text" maxlength="3" id="input3" focus-after elem-to-focus="input1">
Here's a plunkr: http://plnkr.co/edit/QvS0dwUNW3oAhHID1VRX?p=info
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;
});
}
};
}
]);
I have this directive:
(function () {
'use strict';
var app = angular.module('myAppName');
app.directive('smFocus', [ '$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element) {
scope.$on('sm:focus', function () {
$timeout(function() {
element[0].focus();
}, 10);
});
}
};
}]);
})();
I also have these two controls:
<input type="text"
name="nickname"
id="nickname"
ng-model="currentDynamicRule.nickname"
class="form-control"
ng-disabled="!isNew"
placeholder="Nickname"
required
ng-maxlength="10"
sm-focus />
and another one
<input type="text" name="descrip" id="descrip" ng-model="currentDynamicRule.descrip" class="form-control"
placeholder="Description" required ng-maxlength="30"
sm-focus />
So, two controls where the first one is only enabled when it's a new row (disabled in Edit mode). I want to have the first control focused when it's a new record and the second control focused when it's in edit mode.
I am using ASP.NET MVC. Right now in both edit and new modes I have the second control focused. I am not sure how to make this focus conditional.
hmm I had written a directive before wherein it accepts an event and an element id to focus when that event has been triggered.
It's something like this(Plunker DEMO):
JAVASCRIPT
.directive('eventFocus', function($timeout) {
return function(scope, elem, attr) {
elem.on(attr.eventFocus, function() {
// timeout makes sure that is invoked after any other event has been triggered.
// e.g. click events that need to run before the focus or
// inputs elements that are in a disabled state but are enabled when those events
// are triggered.
$timeout(function() {
var element = document.getElementById(attr.eventFocusId);
if(element)
element.focus();
});
});
scope.$on('$destroy', function() {
element.off(attr.eventFocus);
});
};
})
HTML (Possible implementation)
<input type="text" id="pet-desc" ng-model="pet.desc">
<button type="button" event-focus="click" event-focus-id="pet-desc">Edit</button
When Edit Button is clicked, input with id="pet-desc" is focused.
UPDATE:
To identify between which sm-focus element is the target for the sm:focus event, you can add an argument(the id of the element to focus to) within your $rootScope.$broadcast(). See this PLUNKER.
e.g.
Controller
$rotoScope.$broadcast('sm:focus', 'pet-id');
Directive
directive('smFocus', function($timeout) {
return function(scope, elem, attr) {
scope.$on('sm:focus', function(event, id) {
$timeout(function() {
if(attr.id == id)
elem[0].focus();
});
});
};
})
Here is my current implementation that seems to be working! Need to test more:
app.directive('smFocus', [ '$timeout', function ($timeout) {
return {
restrict: 'A',
scope: {
noFocus: "=?"
},
link: function (scope, element) {
var noFocus = angular.isDefined(scope.noFocus) ? scope.noFocus : false;
// console.log('noFocus=' + noFocus)
if (!noFocus) {
scope.$on('sm:focus', function () {
$timeout(function () {
element[0].focus();
}, 10);
});
}
}
};
And my form controls are:
<input type="text"
name="nickname"
id="nickname"
ng-model="currentDynamicRule.nickname"
class="form-control"
ng-disabled="!isNew"
placeholder="Nickname"
required
ng-maxlength="10"
no-focus="!isNew"
sm-focus />
And similar for description:
<input type="text" name="descrip" id="descrip" ng-model="currentDynamicRule.descrip" class="form-control"
placeholder="Description" required ng-maxlength="30"
no-focus="isNew"
sm-focus />
The form works as I want it. I am going to test few more forms to make sure this change didn't break anything.