Bind to text highlighting - javascript

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.

Related

AngularJS input change reset

I have a simple search box. When the input box is not empty a delete button shows up to remove the text inside the box. After that the button should disappear again until i type something in the box again.
When i manually remove the text the delete box is disappearing, but when i press the delete button its not working. Do i have to use .length? I was using .value before like that: if ($(".form-control").value == '' || $(".form-control").value == $(".form-control").defaultValue) {
Thanks in advance.
a = $scope
a.change = function () {
a.limit = 6;
var x = a.search;
if (x.length == '') {
$(".form-inline").removeClass("isset");
} else {
$(".form-inline").addClass("isset");
}
};
a.clearSearch = function () {
a.search = "";
a.limit = 6;
};
html part:
<input type="search" ng-change="change()" ng-model="search" class="form-control" placeholder="Labor durchsuchen...">
<div class="icon-close" ng-click="clearSearch()"></div>
You can use ng-class for that what you are doing with JQuery:-
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.formData = {};
$scope.change = function () {
//do anything here
};
$scope.clearSearch = function () {
$scope.formData.search = "";
};
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<input type="search" ng-class="(formData.search != undefined && formData.search)?'isset':''" ng-change="change()" ng-model="formData.search" class="form-control" placeholder="Labor durchsuchen...">
<div class="icon-close" ng-click="clearSearch()"></div>
</div>
You should be able to drop the jQuery code and just use
<div class="icon-close" ng-click="clearSearch()" ng-show="search.length===0"></div>
I think you need to trigger your a.change function inside a.cleanSearch to check again if something is typed or not.
a.clearSearch = function () {
a.search = "";
a.limit = 6;
a.change()
};

Limiting words in textarea with AngularJS

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" );
}
}
}

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>

How can I revalidate form after appending compiled content into ng-form in Angular?

I have a form with nested ng-forms where on each key press user make script append a compiled form fields (like ui-select, ui-datepicker, etc.) to container with ng-form directive.
How can I trigger validation after content is replaced in ng-form?
EDIT:
Below directive for appending fields into preview:
var previewDirective = function ($compile) {
var link = function ($scope, $el, $attrs) {
var previewField = $('.preview');
var fields = {
date: '<div class="datepicker-wrap" ng-controller="DatepickerCtrl"><button type="button" class="svg--center svg__calendar-gray ngapp-svg" ng-click="open($event)" ng-class="{true: \'is-active\'}[isOpened]"></button><input type="text" class="ngapp-datepicker" ng-model="date" is-open="isOpened" datepicker-options="datepickerSettings" datepicker-popup="{{format}}" ng-focus="open($event)" ng-class="{true: \'is-active\'}[isOpened]" required></div>'
};
var timer = null;
function appendInputs() {
var $this = $(this);
var value = this.value;
var replaced = function () {
var output;
for (var prop in fields) {
output = value.replace(prop, fields[prop]);
}
return output;
};
clearTimeout(timer);
timer = setTimeout(function () {
previewField.html(replaced());
$compile(previewField.contents())($scope);
}, 300);
};
$el.on('keyup', appendInputs);
};
return {
restrict: 'A',
link: link
}
};
Simple demo on JSFiddle
If you want to reproduce the bug, append field by typing into textarea word "date", then remove appended field value, remove word "date" from textarea and type new one to refresh date field.

inject HTML when enter key pressed on input

I have an input field and when people press enter I'd like the field to be emptied and its value printed below with an 'x' icon to delete it afterwards just like the 'search' field on angel.co: https://angel.co/jobs
Here is my HTML:
<form ng-submit="searchAds(searchInput)">
<input id="search-field" type="search" placeholder="Start typing your search..." ng-change="searchRequest()" ng-model="searchInput"/>
</form>
<div id="search-tags"></div>
And my JS in my controller:
$scope.searchAds = function(item){
if (item === "") return;
$scope.searchInput = "";
$('#search-tags').append('<div ng-show="showDetails">' + item + '<div ng-click="showDetails = ! showDetails"> (my DVG icon here) </div></div>');
}
I have 2 problems here:
1 - I believe the code is not compiled when printed so the 'ng-show' and 'ng-click' are so working - how can I make this work?
2 - How can I make sure that when there are several tags, when clicking on my delete icon it hide only this specific tag?
Many thanks
Why not angular instead of jQuery?
You can add a directive to manage the "enter" key:
angular.module('yourModule').directive(
'ngEnter',
function () {
'use strict';
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13) {
scope.$apply(function (){
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
And then change a bit your code:
<form ng-submit="searchAds(searchInput)">
<input type="search" placeholder="Start typing your search..." ng-enter="add(searchInput)" ng-model="searchInput"/>
</form>
<div ng-repeat="tag in tags">
<div>{{tag}}<div ng-click="delete(tag)">(my DVG icon here)</div></div>
</div>
Your controller:
$scope.add = function(item) {
$scope.tags.push(item);
$scope.searchInput = "";
}
$scope.delete = function(item) {
var index = $scope.tags.indexOf(item);
$scope.tags.splice(index, 1);
}

Categories