Limiting words in textarea with AngularJS - javascript

I have been trying to limit words in text area using AngularJS.
So far I have tried this
Textarea HTML
<textarea ng-model="man_abs" style="height: 100px;" id="manuscriptabstract" name="manuscriptabstract" placeholder="Maximum 250 Words" class="form-control" ng-change="wordCount()" required=""></textarea>
<span class="help-block">{{251 - man_abs.split(' ').length}} words left</span>
AngularJS script
$scope.wordCount = function() {
if ($scope.man_abs != undefined) {
var key_length = $scope.man_abs.split(' ').length;
if (key_length >= 251) {
$scope.man_abs = $scope.man_abs.substring(0, $scope.man_abs.lastIndexOf(" "));
alert("You are allowed to enter only 250 words in this field");
}
}
}
I seems to work fine ,but when I copy paste a paragraph more than 250 words ,it is not working as expected.
I have tried preventDefault() method and it is not working.
I need to make sure that user should not be able to type more than 250 words, If Copy pasted , after 250th word the sentence have to be removed..

you can also try to limit the words with a directive like so:
.directive('myCheck', function () {
return {
restrict: 'AE',
link: function (scope, element, attrs) {
element.bind('keyup', function (e) {
console.log(this.value);
var words = this.value.split(" ");
console.log("words are:",words.length)
var tmp = '';
if (words.length >5){
for(var i=0;i<5;i++){
tmp += words[i] + ' ';
}
this.value = tmp;
}
});
}
}
});
into HTML:
<input type="text" my-check> /*you can do it with textarea as well*/
plunker example:
https://plnkr.co/edit/7EeUbu1d96vCCQhi8g19?p=preview

You have two options:
ng-click = wordCount()
Use angular watch $watch to see the changes.
But I would recommend you to go with ng-click option. As it's much easy and won't create load / stress over page as $watch don't lets page rest.
Example:
<textarea ng-model="man_abs" style="height: 100px;" id="manuscriptabstract" name="manuscriptabstract" placeholder="Maximum 250 Words" class="form-control" ng-change="wordCount()" ng-click="wordCount()" required=""></textarea>
In controller
$scope.wordCount = function() {
if ($scope.man_abs != undefined) {
var key_length = $scope.man_abs.split(' ').length;
if (key_length >= 251) {
var trimmedString = $scope.man_abs.substring(0, 250);
$scope.man_abs = trimmedString;
alert("You are allowed to enter only 250 words in this field");
}
}
}

You can create a custom validator
.directive('maxwords', [function () {
return {
require : 'ngModel',
link : function (scope, elm, attrs, ctrl) {
ctrl.$validators.maxwords = function (modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
// consider empty models to be valid
return true;
}
return (modelValue.match(/\S+/g) || []).length <= +attrs.maxwords;
};
}
};
}])
and use it as:
<textarea maxwords="30"/>
error.maxwords will be set foe validations just as usual validations.

you should have to call your function on Paste event also like
<textarea ng-model="man_abs" style="height: 100px;" id="manuscriptabstract" name="manuscriptabstract" placeholder="Maximum 250 Words" class="form-control" ng-change="wordCount()" ng-paste="wordCount()" required=""></textarea>
<span class="help-block">{{251 - man_abs.split(' ').length}} words left</span>

You can use $watch to complete this task
Try this code:
https://jsfiddle.net/jigarb1992/awLp4bnk/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="myctrl">
<textarea ng-model="txt" rows="10" cols="50"></textarea>
<br>
<span class="help-block">{{limit}} words left</span>
</div>
<script>
var app = angular.module("myapp", []);
app.controller("myctrl", function($scope){
$scope.limit = 250;
$scope.$watch("txt", function(newValue, lastValue){
var words = $scope.txt.split(" ").length - 1;
if(words > 250 ){
$scope.txt = lastValue;
}
$scope.limit = 250 - words;
})
})
</script>

You can use Split and join methods of Array.
So below will work in your case
$scope.wordCount = function() {
if ( $scope.man_abs != undefined ) {
var key = $scope.man_abs.split( ' ' );
if ( key.length >= 251 ) {
$scope.man_abs = key.splice( 0, 250 ).join( ' ' );
alert( "You are allowed to enter only 250 words in this field" );
}
}
}

Related

angular directive does not work with input type = "number"

The code below works only when the input type is text, and it doesn't work when the type is number.
<div ng-app="myApp" ng-controller="myCtrl as model">
<input type="text" ng-model="cero" ng-decimal >
</div>
angular
.module("myApp",[])
.controller('myCtrl', function($scope){
var model=this;
})
.directive('ngDecimal', function ($parse) {
var linkFunction =function(scope, element, attrs){
element.bind("keypress", function(event) {
if(event.which === 13) {
scope.$apply(function(){
scope.$eval(attrs.format, {'event': event});
if(scope.cero===undefined || scope.cero===''){
scope.cero="0.",
event.preventDefault();
}else{
}
});
}
});
};
return{
restrict : 'A',
scope:{
cero: '=ngModel'
},
link: linkFunction
}
});
What I need help with is changing the type to number and still making the code work. The code is also on CodePen.
Updated pen : http://codepen.io/anon/pen/QKOVkP?editors=1011
Works with number, constraint being you cannot assign
scope.cero = "0." // string value
to a type="number" so replace it with the minimum number you want to assign, maybe
scope.cero = parseFloat("0.01") // parseFloat("0.") won't work
In the else condition add this.
scope.cero = parseFloat(scope.cero).toFixed(2);
Convert string to decimal
Here is the code: working code

Angular bind from the controller to the input field

I have the following JSfiddle example.
HTML
<div ng-app="app" ng-controller="Example">
<input type="number" ng-model="data.mainOdd1" placeholder="#1 Main Odd" onfocus="this.placeholder=''" min="0" step="any" ui-blur="testfn('mainOdd1', $event, '#1 Main Odd');">
</div>
Javascript
angular
.module('app', [])
.directive('uiBlur', function($parse) {
return function(scope, elem, attrs) {
elem.bind('blur', function(event) {
scope.$apply(function() {
$parse(attrs.uiBlur)(scope, {
$event: event
});
});
});
};
})
.controller('Example', function($scope) {
$scope.data = {
'mainOdd1' : '',
};
$scope.testfn = function(propertyName, $event, placeHolder) {
debugger;
if (($event.target.validity.valid == false) ||
($scope.data[propertyName] == ''))
{
$scope.data[propertyName] = '';
$event.target.placeholder = placeHolder;
return;
}
debugger;
$scope.data[propertyName] = $scope.data[propertyName].toFixed(2);
};
});
I try to keep only two decimals.
I see two issues, the first one is that the number in the view does not change, although mainOdd1 does change. and the second one is that I get an error in the console about using the toFixed function.
What am I doing wrong here?
Thanks
Hi toFixed Error happening because of while converting toFIxed it will become String but your input type in number so it wont accept so u have to change to float
$scope.testfn = function(propertyName, $event, placeHolder) {
debugger;
if (($event.target.validity.valid == false) ||
($scope.data[propertyName] == ''))
{
$scope.data[propertyName] = '';
$event.target.placeholder = placeHolder;
return;
}
debugger;
$scope.data[propertyName] = parseFloat($scope.data[propertyName].toFixed(2));
};
Your input is type="number".You must use this
$scope.data = {
'mainOdd1' : 0,
};
and can use step=0.01 for input in order to have only two decimals
The problem is with this line
$scope.data[propertyName].toFixed(2)
Use Unary plus to convert it to number
$scope.data[propertyName] = +$scope.data[propertyName].toFixed(2);
run the snippet to solve both of your issues:
angular
.module('app', [])
.directive('uiBlur', function($parse) {
return function(scope, elem, attrs) {
elem.bind('blur', function(event) {
scope.$apply(function() {
$parse(attrs.uiBlur)(scope, {
$event: event
});
});
});
};
})
.controller('Example', function($scope) {
$scope.data = {
'mainOdd1' : '',
};
$scope.testfn = function(propertyName, $event, placeHolder) {
if (($event.target.validity.valid == false) ||
($scope.data[propertyName] == ''))
{
$scope.data[propertyName] = '';
$event.target.placeholder = placeHolder;
return;
}
$scope.data[propertyName] = parseFloat($scope.data[propertyName]).toFixed(2);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.js"></script>
<div ng-app="app" ng-controller="Example">
<input type="text" ng-model="data.mainOdd1" placeholder="#1 Main Odd" onfocus="this.placeholder=''" min="0" step="any" ui-blur="testfn('mainOdd1', $event, '#1 Main Odd');">
</div>

Validating custom component in angular 1.5

I was working in angular project, There I had come across a situation in which I need to validate custom component having a textbox.
<dollar-text-validate ng-model="ctrl.value" required name="myDir"></dollar-text-validate>
My Component
angular.module("myApp", []);
angular.module("myApp").controller('MyController',function(){
var ctrl = this;
ctrl.value = 56;
});
angular.module("myApp").component('dollarTextValidate',{
bindings: {
ngModel :'='
},
template: '<div><input type="text" ng-focus="ctrl.focus()" ng-blur="ctrl.blur()" ng-model="ctrl.amount1"><input type="hidden" ng-model="ctrl.ngModel"></div>',
controller: function() {
var ctrl = this;
// ctrl.amount1 =
ctrl.amount1 =ctrl.ngModel===undefined||ctrl.ngModel==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
ctrl.focus=function(){
ctrl.amount1 = ctrl.amount1 === undefined ? '' : ctrl.amount1.slice(1);
ctrl.ngModel = ctrl.amount1;
console.log(ctrl.ngModel);
}
ctrl.blur=function(){
ctrl.ngModel = ctrl.amount1;
ctrl.amount1 = ctrl.amount1==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
}
},
controllerAs:'ctrl'
})
This component is used to set $ symbol in front of entered value. So $ appended value will be available in textbox and original value which is to be validated in hidden field.
How can I validate hidden field. I tried with required attribute in hidden tag but nothing happening. Also tried with custom tag.
Sorry to break it to you, but you might wanna go for directive, and then use $parsers, $formatter and $validators properties of ngModelController.
Component can be used for this, but it is just easier with normal directive.
angular.module('myApp', []);
angular.module("myApp").directive('dollarTextValidate', function() {
return {
require: 'ngModel',
link: function($scope, $element) {
var regexp = /^\$(\d+(\.\d+)?)$/;
var ngModel = $element.controller('ngModel');
ngModel.$formatters.push(function(value) {
return value ? '$' + value : '';
});
ngModel.$parsers.push(function(value) {
var matched = value.match(regexp);
if (matched) {
return +matched[1];
}
});
ngModel.$validators.greaterThan10 = function (modelVal, viewVal) {
var value = modelVal || viewVal;
return value > 10;
};
},
controllerAs: 'ctrl'
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="myApp" ng-form="form">
dollar-text-validate = <input dollar-text-validate ng-model="value" required name="myDir"><br>
number input = <input type="number" ng-model="value" required><br>
value = {{value}}<br>
errors = {{form.myDir.$error}}
</div>

Directive not called on input change

I'm facing an issue which I can't seem to solve.
I have several inputs with each a directive to validate the input value, like this:
<div class="row form-group">
<div class="col-sm-6">last name</div>
<div class="col-sm-6">
<div class="input-group" ng-class="{'has-error': form.lastname.$invalid && (form.lastname.$touched || form.$submitted)}">
<input type="text" name="lastname" class="form-control"
model-blur
validator-lastname
ng-trim="true"
ng-model="fields.lastname.value"
ng-maxlength="fields.lastname.validation.maxLength">
<input-group-addon class="input-group-addon"
iga-char=""
iga-form="form"
iga-field="form.lastname"
iga-if-touched="true">
</input-group-addon>
</div>
<form-message-list fml-form="form"
fml-field="form.lastname"
fml-label="Last name"
fml-fieldData="fields.lastname">
</form-message-list>
</div>
</div>
This field required the following pattern: /^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/
My issue is this:
When I add an invalid value to my input, like this: / , my invalid message remains and ng-invalid-pattern remains on my field.
When I add this pattern to my field like this: ng-pattern="/^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/" I don't have any issues. But when I try to validate via my directive validator-lastname it only checks one time. When I fill the input with an invalid value and then change it to empty, which is allowed, the ng-invalid-pattern error remains.
This is my directive:
angular.module('app')
.directive('validatorLastname', validatorLastname);
/* #ngInject */
function validatorLastname() {
var directive = {
require: 'ngModel',
link: link
};
return directive;
function link(scope, element, attrs, modelCtrl) {
var valid = false;
var formatter = function (inputValue) {
if (inputValue) {
var res = inputValue.match(/^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/);
if (res && res.length > 0) {
valid = true;
}
modelCtrl.$setValidity('pattern', valid);
valid = false;
}
return inputValue;
};
modelCtrl.$parsers.push(formatter);
if (scope[attrs.ngModel] && scope[attrs.ngModel] !== '') {
formatter(scope[attrs.ngModel]);
}
}
}
I made a JSFiddle to reproduce the problem: http://jsfiddle.net/sZZEt/537/
I hope someone can point me in the right direction.
Thanks in advance.
You should update your directive code to make everything work fine.
angular.module('app')
.directive('validatorLastname', validatorLastname);
/* #ngInject */
function validatorLastname() {
var directive = {
require: 'ngModel',
link: link
};
return directive;
function link(scope, element, attrs, modelCtrl) {
var valid = false;
var formatter = function (inputValue) {
if (inputValue) {
var res = inputValue.match(/^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/);
if (res && res.length > 0) {
valid = true;
}
modelCtrl.$setValidity('pattern', valid);
valid = false;
}else{
modelCtrl.$setValidity('pattern', true);
}
return inputValue;
};
modelCtrl.$parsers.push(formatter);
if (scope[attrs.ngModel] && scope[attrs.ngModel] !== '') {
formatter(scope[attrs.ngModel]);
}
}
}
I have created a plunk for your problem...
It is because if inputValue is null then your $setValidity method will not invoke and could not perform validation again. You should set pattern validity to true inside else part. if you want to make field valid for no-input.
You can now refer to updated plunk https://plnkr.co/edit/N3DrsB?p=preview

Bind to text highlighting

I am trying to bind a controller action to text that is highlighted in a text area, text input, or content-editable. Suppose that I have:
<input type="text" ng-model="name" placeholder="Enter Name">
With Angular 1.2.0, how can I watch for text that is highlighted inside the text box and display something on the page for the user?
Here is a pretty rough implementation of a directive that uses $timeout. It could probably be improved by monitoring mouseup and keyup (or selection events if they exist).
http://jsfiddle.net/4XDR8/1/
HTML
<div ng-app="app" ng-controller="TestCtrl">
<input type="text" placeholder="Enter Name" ng-get-selection="name">
{{name}}
<br/>
<br/>here select all this text down here
</div>
JavaScript:
var app = angular.module('app', []);
app.directive('ngGetSelection', function ($timeout) {
var text = '';
function getSelectedText() {
var text = "";
if (typeof window.getSelection != "undefined") {
text = window.getSelection().toString();
} else if (typeof document.selection != "undefined" && document.selection.type == "Text") {
text = document.selection.createRange().text;
}
return text;
}
return {
restrict: 'A',
scope: {
ngGetSelection: '='
},
link: function (scope, element) {
$timeout(function getSelection() {
var newText = getSelectedText();
if (text != newText) {
text = newText;
element.val(newText);
scope.ngGetSelection = newText;
}
$timeout(getSelection, 50);
}, 50);
}
};
});
app.controller('TestCtrl', function ($scope) {
$scope.name = '';
});
You can create a directive to utilize selectionStart and selectionEnd properties of an input element to achieve what you want to accomplish, like the following:
JS:
directive('watchSelection', function() {
return function(scope, elem) {
elem.on('mouseup', function() {
var start = elem[0].selectionStart;
var end = elem[0].selectionEnd;
scope.selected = elem[0].value.substring(start, end);
scope.$apply();
});
};
});
HTML:
<input type="text" ng-model="name" placeholder="Enter Name" watch-selection>
http://plnkr.co/edit/4LLfWk110p8ruVjAWRNv
Here's how to grab the selected text from an input field:
http://jsfiddle.net/vREW8/
var input = document.getElementsByTagName('input')[0];
var selectedText = input.value.substring(input.selectionStart, input.selectionEnd);
You can use it with Anuglar.js any way that you want.

Categories