angularjs directive - capitalize - angularjs

I have this code below working fine when a user is typing an input data. But my problem is when the data is from the database(auto filled) my directive is not working(capitalize the letter
). Is there a reason why its not working?.
But when using a class="text-uppercase" its working.
HTML
<input ng-model="profile.Name" type="text" placeholder="" maxlength="40" capitalize />
JS
app.directive('capitalize', function ($parse) {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
var capitalize = function (inputValue) {
if (inputValue === undefined) { inputValue = ''; }
var capitalized = inputValue.toUpperCase();
//for (var i = 0; i < capitalized.length; i++) {
// capitalized[i] = capitalized[i].charAt(0).toUpperCase() + capitalized[i].substring(1);
//}
if (capitalized !== inputValue) {
modelCtrl.$setViewValue(capitalized);
modelCtrl.$render();
}
return capitalized;
}
modelCtrl.$parsers.push(capitalize);
capitalize($parse(attrs.ngModel)(scope)); // capitalize initial value
}
};
});

You can check below for more information on require: 'ngModel' configure setting.
What's the meaning of require: 'ngModel'?

Related

AngularJs: NumberOnly directive

I want to allow numbers only and - sign in textbox.
When i type - sign on IPhone safari it remove value from input like a backspace.
But it is working fine on android and IPad safari.
Here is my directive:
app.directive('numberOnly', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elm, attrs, ngModel) {
var pattern = /[^0-9]/g;
var digitsLength = attrs.digitsLength || 0;
var allowMinus = attrs.allowMinus || false;
if (allowMinus) {
pattern = /(?!^-)[^0-9]/g;
} else {
pattern = /[^0-9]/g;
}
scope.$watch(attrs.ngModel, function (newValue, oldValue) {
if (newValue) {
var result = newValue.toString().replace(pattern, '');
ngModel.$setViewValue(result);
if (digitsLength > 0) {
if (result.charAt(0) === '-') {
if (result.substring(1, result.length).length > digitsLength) {
ngModel.$setViewValue(oldValue);
}
}
else {
if (result.substring(0, result.length).length > digitsLength) {
ngModel.$setViewValue(oldValue);
}
}
}
}
ngModel.$render();
}, true);
}
}
});
app.html
<input type="text" ng-model="number" number-only digits-length="7" >
I think it would be a lot more easier if you use
<input type="number" name="input" ng-model="value">
and check validity of the input. Also you can set digits length by setting min and max values of the input.
$scope.example = {
max: 9999999,
min: 1000000
};
with the following min max settings only 7 digits numbers will be valid.
<input type="number" name="input" ng-model="example.value"
min="{{example.min}}" max="{{example.max}}">
You can try below custom directive which I've used. You can modify the regex here to filter the text.
<input type="text" maxlength="3" replacewith="[^0-9-]" ng-model="number">
app.directive('replacewith',replacewith);
replacewith.$inject = ['$timeout'];
function replacewith($timeout) {
return {
require: 'ngModel',
scope: {
regex: '#replacewith'
},
link: function(scope, element, attrs, model) {
model.$parsers.push(function(val) {
if (!val) {
$timeout(function(){
model.$setValidity('parse', true);
}, 0);
return;
}
var regex = new RegExp(scope.regex);
var replaced = val.replace(regex, '');
if (replaced !== val) {
model.$setViewValue(replaced);
model.$render();
}
return replaced;
});
}
};
}

min/max validations not working if values are changed later

i have requirement where min value of one field depends on the input given in another field.
<input type="number" name="minval" class="form-control" ng-model="user.minval"
ng-required="true">
this input is used to validate another field
<input type="number" name="inputval" class="form-control" ng-model="user.inputval"
ng-required="true" min="{{user.minval}}">
but this is not working as expected.. if i change the "minval" later the input does not get revalidated..
i have tried setting the initial value for min from JS as was suggested in some solution but thats also not helping...
PLUNKER LINK
use ng-min/ng-max directives
app.directive('ngMin', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
scope.$watch(attr.ngMin, function(){
if (ctrl.$isDirty) ctrl.$setViewValue(ctrl.$viewValue);
});
var isEmpty = function (value) {
return angular.isUndefined(value) || value === "" || value === null;
}
var minValidator = function(value) {
var min = scope.$eval(attr.ngMin) || 0;
if (!isEmpty(value) && value < min) {
ctrl.$setValidity('ngMin', false);
return undefined;
} else {
ctrl.$setValidity('ngMin', true);
return value;
}
};
ctrl.$parsers.push(minValidator);
ctrl.$formatters.push(minValidator);
}
};
});
app.directive('ngMax', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
scope.$watch(attr.ngMax, function(){
if (ctrl.$isDirty) ctrl.$setViewValue(ctrl.$viewValue);
});
var maxValidator = function(value) {
var max = scope.$eval(attr.ngMax) || Infinity;
if (!isEmpty(value) && value > max) {
ctrl.$setValidity('ngMax', false);
return undefined;
} else {
ctrl.$setValidity('ngMax', true);
return value;
}
};
ctrl.$parsers.push(maxValidator);
ctrl.$formatters.push(maxValidator);
}
};
});
I've developed a couple of directives that actually restrict the user from setting an invalid value instead of simply throwing an error when an invalid value is provided.
These directives also do not require ngModel (though I doubt you would use them without) and what's really cool is that it will wrap the value around to the min/max if both settings are provided!
I've tried to simplify the directives as much as possible to make them easier for our readers.
Here is a JSFiddle of the whole thing: JSFiddle
And here are the directives:
app.directive('ngMin', function($parse){
return {
restrict: 'A',
link: function(scope, element, attrs){
function validate(){
if(element
&& element[0]
&& element[0].localName === 'input'
&& isNumber(attrs.ngMin)
&& isNumber(element[0].value)
&& parseFloat(element[0].value) < parseFloat(attrs.ngMin)){
if(isNumber(attrs.ngMax)){
element[0].value = parseFloat(attrs.ngMax);
if(attrs.hasOwnProperty("ngModel"))
$parse(attrs.ngModel).assign(scope, parseFloat(attrs.ngMax));
}
else {
element[0].value = parseFloat(attrs.ngMin);
if(attrs.hasOwnProperty("ngModel"))
$parse(attrs.ngModel).assign(scope, parseFloat(attrs.ngMin));
}
}
}
scope.$watch(function(){
return attrs.ngMin + "-" + element[0].value;
}, function(newVal, oldVal){
if(newVal != oldVal)
validate();
});
validate();
}
};
});
app.directive('ngMax', function($parse){
return {
restrict: 'A',
link: function(scope, element, attrs){
function validate(){
if(element
&& element[0]
&& element[0].localName === 'input'
&& isNumber(attrs.ngMax)
&& isNumber(element[0].value)
&& parseFloat(element[0].value) > parseFloat(attrs.ngMax)){
if(isNumber(attrs.ngMin)){
element[0].value = parseFloat(attrs.ngMin);
if(attrs.hasOwnProperty("ngModel"))
$parse(attrs.ngModel).assign(scope, parseFloat(attrs.ngMin));
}
else {
element[0].value = parseFloat(attrs.ngMax);
if(attrs.hasOwnProperty("ngModel"))
$parse(attrs.ngModel).assign(scope, parseFloat(attrs.ngMax));
}
}
}
scope.$watch(function(){
return attrs.ngMax + "-" + element[0].value;
}, function(newVal, oldVal){
if(newVal != oldVal)
validate();
});
validate();
}
};
});
...also, you will need this little helper function as well:
function isNumber(n){
return !isNaN(parseFloat(n)) && isFinite(n);
}
To invoke these directives, just set them on an input box where type="number":
<input ng-model="myModel" ng-min="0" ng-max="1024" />
And that should do it!
When you provide both an ngMin and ngMax, these directive will wrap the value around, so that when your value becomes less than ngMin, it will be set to ngMax, and vice-versa.
If you only provide ngMin or ngMax, the input value will simply be capped at these values.
I prefer this method of preventing bad values rather than alerting the user that they have entered a bad value.

AngularJS custom validation, dates are either before or after other date

I have these custom validation directives which work as intended.
Problems is I want to trigger validation of the input when the other input that is used to compare with is changed.. Is this solvable in the watch block?
Lets say i have
<input afterOtherDate="dateB" ng-model="dateA" value="2014"/>
<input beforeOtherDate="dateA" ng-model="dateB" value="2013"/>
If i then set dateA to be after dateB, dateA will become invalid, but dateB wont know.
Same the other way around, if i have
<input afterOtherDate="dateB" ng-model="dateA" value="2013"/>
<input beforeOtherDate="dateA" ng-model="dateB" value="2014"/>
Both inputs need to be re-validated when the other one changes. So both become valid.
validationHelpers.directive('afterOtherDate', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
var doValidation = function () {
ctrl.$parsers.unshift(function (viewValue) {
if (viewValue <= compareTo) {
ctrl.$setValidity('afterOtherDate', false);
return viewValue;
} else {
ctrl.$setValidity('afterOtherDate', true);
return viewValue;
}
});
};
var scopeHierarchy = attrs["afterOtherDate"].split('.');
var compareTo = scope;
for (var k = 0; k < scopeHierarchy.length; k++) {
compareTo = compareTo[scopeHierarchy[k]];
}
scope.$watch(attrs["afterOtherDate"], function (val) {
});
doValidation();
}
};
});
validationHelpers.directive('beforeOtherDate', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
var doValidation = function () {
ctrl.$parsers.unshift(function (viewValue) {
if (viewValue <= compareTo) {
ctrl.$setValidity('beforeOtherDate', true);
return viewValue;
} else {
ctrl.$setValidity('beforeOtherDate', false);
return viewValue;
}
});
};
var scopeHierarchy = attrs["beforeOtherDate"].split('.');
var compareTo = scope;
for (var k = 0; k < scopeHierarchy.length; k++) {
compareTo = compareTo[scopeHierarchy[k]];
}
scope.$watch(attrs["beforeOtherDate"], function (val) {
});
doValidation();
}
};
});
Would be cool to solve it inside the custom directives!
BR
twd
What I do in my directive, that I get the controller of ngModel of the field which I'm trying to compare to:
var compareModel = scope.$eval([formCtrl.$name, attr.dateAfter].join("."));
I'm passing the model name in the attribute. Then you can access its parsers and push a method that would fire a validation in your model.
compareModel.$parsers.push(function (value) {
I force the compareModel to revalidate by using $setViewValue:
compareModel.$parsers.push(function (value) {
checkBefore(value);
model.$setViewValue(model.$viewValue);
return value;
});

Restrict input as numbers on input fields using AngularJS

I am trying to restrict input as numbers on below fields
Postal Code:
<input type="text" id="zipCode1" name="zipCode1" size="4" maxlength="5" ng-model="zipCode1" ng-change="myNumbers(zipCode1)" />
<input type="text" id="zipCode2" name="zipCode2" size="3" maxlength="4" ng-model="zipCode2" ng-change="myNumbers(zipCode2)" />
it doesn't work with
$scope.myNumbers = function(fieldName){
var tN = fieldName.replace(/[^\d]/g, "");
if(tN != fieldName)
fieldName = tN
};
It works with below code but changing both the fields
$scope.$watch('myNumbers', function() {
var tN = $scope.myNumbers.replace(/[^\d]/g, "");
if(tN != $scope.myNumbers)
$scope.myNumbers = tN;
})
Need to change the value for the input field where user is typing and not both
Use the directive found here: https://stackoverflow.com/a/19675023/149060 instead of the ng-change function. Replicated here for easy reference:
angular.module('app').
directive('onlyDigits', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) return;
ngModel.$parsers.unshift(function (inputValue) {
var digits = inputValue.split('').filter(function (s) { return (!isNaN(s) && s != ' '); }).join('');
ngModel.$viewValue = digits;
ngModel.$render();
return digits;
});
}
};
});
You could try adding to the inputs ng-pattern='/^\d{2}$/'
Here is a directive I've done to restrict the keys allowed.
angular.module('app').directive('restrictTo', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var re = RegExp(attrs.restrictTo);
var exclude = /Backspace|Enter|Tab|Delete|Del|ArrowUp|Up|ArrowDown|Down|ArrowLeft|Left|ArrowRight|Right/;
element[0].addEventListener('keydown', function(event) {
if (!exclude.test(event.key) && !re.test(event.key)) {
event.preventDefault();
}
});
}
}
});
And the input would look like:
<input type="text" name="zipCode1" maxlength="5" ng-model="zipCode1" restrict-to="[0-9]">
The regular expression evaluates the pressed key, not the value.
It also works perfectly with inputs type="number" because prevents from changing its value, so the key is never displayed and it does not mess with the model.

How to autocapitalize the first character in an input field in AngularJS?

How to autocapitalize the first character in an input field inside an AngularJS form element?
I saw the jQuery solution already, but believe this has to be done differently in AngularJS by using a directive.
Yes, you need to define a directive and define your own parser function:
myApp.directive('capitalizeFirst', function($parse) {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
var capitalize = function(inputValue) {
if (inputValue === undefined) { inputValue = ''; }
var capitalized = inputValue.charAt(0).toUpperCase() +
inputValue.substring(1);
if(capitalized !== inputValue) {
modelCtrl.$setViewValue(capitalized);
modelCtrl.$render();
}
return capitalized;
}
modelCtrl.$parsers.push(capitalize);
capitalize($parse(attrs.ngModel)(scope)); // capitalize initial value
}
};
});
HTML:
<input type="text" ng-model="obj.name" capitalize-first>
Fiddle
Please remember that not everything needs an Angular solution. You see this a lot with the jQuery crowd; they like to use expensive jQuery functions to do things that are simpler or easier to do with pure javascript.
So while you might very well need a capitalize function and the above answers provide that, it's going to be a lot more efficient to just use the css rule "text-transform: capitalize"
<tr ng-repeat="(key, value) in item">
<td style="text-transform: capitalize">{{key}}</td>
<td>{{item}}</td>
</tr>
You can create a custom filter 'capitalize' and apply it to any string you want:
<div ng-controller="MyCtrl">
{{aString | capitalize}} !
</div>
JavaScript code for filter:
var app = angular.module('myApp',[]);
myApp.filter('capitalize', function() {
return function(input, scope) {
return input.substring(0,1).toUpperCase()+input.substring(1);
}
});
Use the CSS :first-letter pseudo class.
You need to put everything lowercase and after apply the uppercase only to the first letter
p{
text-transform: lowercase;
}
p:first-letter{
text-transform: uppercase;
}
Here's an example: http://jsfiddle.net/AlexCode/xu24h/
Modified his code to capitalize every first character of word. If you give 'john doe', output is 'John Doe'
myApp.directive('capitalizeFirst', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
var capitalize = function(inputValue) {
var capitalized = inputValue.split(' ').reduce(function(prevValue, word){
return prevValue + word.substring(0, 1).toUpperCase() + word.substring(1) + ' ';
}, '');
if(capitalized !== inputValue) {
modelCtrl.$setViewValue(capitalized);
modelCtrl.$render();
}
return capitalized;
}
modelCtrl.$parsers.push(capitalize);
capitalize(scope[attrs.ngModel]); // capitalize initial value
}
};
});
I would prefer a filter and directive. This should work with cursor movement:
app.filter('capitalizeFirst', function () {
return function (input, scope) {
var text = input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase();
return text;
}
});
app.directive('capitalizeFirst', ['$filter', function ($filter) {
return {
require: 'ngModel',
link: function (scope, element, attrs, controller) {
controller.$parsers.push(function (value) {
var transformedInput = $filter('capitalizeFirst')(value);
if (transformedInput !== value) {
var el = element[0];
el.setSelectionRange(el.selectionStart, el.selectionEnd);
controller.$setViewValue(transformedInput);
controller.$render();
}
return transformedInput;
});
}
};
}]);
Here is a fiddle
To fix the cursor problem (from where Mark Rajcok's solution),
you can store element[0].selectionStart at the beginning of your method,
and then ensure to reset element[0].selectionStart and element[0].selectionEnd to the stored value before the return.
This should capture your selection range in angular
Generate directive:
ng g directive capitalizeFirst
Update file capitalize-first.directive.ts:
import {Directive, ElementRef, HostListener} from '#angular/core';
#Directive({
selector: '[appCapitalizeFirst]'
})
export class CapitalizeFirstDirective {
constructor(private ref: ElementRef) {
}
#HostListener('input', ['$event'])
onInput(event: any): void {
if (event.target.value.length === 1) {
const inputValue = event.target.value;
this.ref.nativeElement.value = inputValue.charAt(0).toUpperCase() + inputValue.substring(1);
}
}
}
Usage:
<input appCapitalizeFirst>
This code woks with Angular 11+
Comment to Mark Rajcok solution: when using $setViewValue, you trigger the parsers and validators again. If you add a console.log statement at the beginning of your capitalize function, you'll see it printed twice.
I propose the following directive solution (where ngModel is optional):
.directive('capitalize', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
var capitalize = function (inputValue) {
return (inputValue || '').toUpperCase();
}
if(ngModel) {
ngModel.$formatters.push(capitalize);
ngModel._$setViewValue = ngModel.$setViewValue;
ngModel.$setViewValue = function(val){
ngModel._$setViewValue(capitalize(val));
ngModel.$render();
};
}else {
element.val(capitalize(element.val()));
element.on("keypress keyup", function(){
scope.$evalAsync(function(){
element.val(capitalize(element.val()));
});
});
}
}
};
});
Here's a codepen for a filter that capitalizes the first letter:
http://codepen.io/WinterJoey/pen/sfFaK
angular.module('CustomFilter', []).
filter('capitalize', function() {
return function(input, all) {
return (!!input) ? input.replace(/([^\W_]+[^\s-]*) */g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}) : '';
}
});
Further to the CSS-only answers, you could always use Twitter Bootstrap:
<td class="text-capitalize">
Building off Mark Rajcok's solution; It's important to consider that the directive evaluate only when the input field in engaged, otherwise you'll get error messages firing off until the input field has a 1st character.
Easy fix with a few conditionals:
A jsfiddle to go with that: https://jsfiddle.net/Ely_Liberov/Lze14z4g/2/
.directive('capitalizeFirst', function(uppercaseFilter, $parse) {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
var capitalize = function(inputValue) {
if (inputValue != null) {
var capitalized = inputValue.charAt(0).toUpperCase() +
inputValue.substring(1);
if (capitalized !== inputValue) {
modelCtrl.$setViewValue(capitalized);
modelCtrl.$render();
}
return capitalized;
}
};
var model = $parse(attrs.ngModel);
modelCtrl.$parsers.push(capitalize);
capitalize(model(scope));
}
};
});
The problem with css-ony answers is that the angular model is not updated with the view. This is because css only applies styling after rendering.
The following directive updates the model AND remembers the cursors location
app.module.directive('myCapitalize', [ function () {
'use strict';
return {
require: 'ngModel',
restrict: "A",
link: function (scope, elem, attrs, modelCtrl) {
/* Watch the model value using a function */
scope.$watch(function () {
return modelCtrl.$modelValue;
}, function (value) {
/**
* Skip capitalize when:
* - the value is not defined.
* - the value is already capitalized.
*/
if (!isDefined(value) || isUpperCase(value)) {
return;
}
/* Save selection position */
var start = elem[0].selectionStart;
var end = elem[0].selectionEnd;
/* uppercase the value */
value = value.toUpperCase();
/* set the new value in the modelControl */
modelCtrl.$setViewValue(value);
/* update the view */
modelCtrl.$render();
/* Reset the position of the cursor */
elem[0].setSelectionRange(start, end);
});
/**
* Check if the string is defined, not null (in case of java object usage) and has a length.
* #param str {string} The string to check
* #return {boolean} <code>true</code> when the string is defined
*/
function isDefined(str) {
return angular.isDefined(str) && str !== null && str.length > 0;
}
/**
* Check if a string is upper case
* #param str {string} The string to check
* #return {boolean} <code>true</code> when the string is upper case
*/
function isUpperCase(str) {
return str === str.toUpperCase();
}
}
};
}]);
You can use the provided uppercase filter.
http://docs.angularjs.org/api/ng.filter:uppercase
You could use pure css:
input {
text-transform: capitalize;
}

Resources