12

Is it possible to ignore or not allow line breaks in a textarea in angularjs? The text is used in a pdf generator and I don't want the user to be able to type in a newline then not see it in the pdf. I would rather have the return key ignored all together.

<textarea ng-model="model.text"></textarea>
Dev01
  • 13,292
  • 19
  • 70
  • 124
  • 1
    What do you mean? It already is ignored [(example)](http://jsbin.com/navogicixi/1/edit?html,js,output) -- add line breaks, the output is still one line. – Tom May 08 '15 at 20:45
  • @Tom there are still newline characters there, it just doesn't show them. I'm using the text in a server side php generator script and it prints the newlines. I was hoping to just not allow newline characters (not show them in the textarea) at all do the user doesn't think they can have newlines. – Dev01 May 08 '15 at 21:10

5 Answers5

7

In your controller/directive if you run a regex to remove all the occurrences of '\n' in $scope.model.text, you should get a plain string with no newline characters.

You can refer to this answer to do that: How to replace all occurrences of a string in JavaScript?

If you don't want even the text area to have any line breaks you can add the above logic in a watcher like this:

$scope.$watch('model.text', function(){
  $scope.model.text = $scope.model.text.replace(/\n/g, '');
})
Community
  • 1
  • 1
midhunsezhi
  • 461
  • 5
  • 10
  • 1
    This would work and I thought of doing that but I don't want the user to think they can use newlines then not see them in the final product. I would rather just not allow them to type a newline at all. – Dev01 May 08 '15 at 21:15
  • I've edited the answer for even the text area to not have line breaks(using watcher). The user now gets what he sees in the text area. :) – midhunsezhi May 08 '15 at 21:22
  • Nice! This works unless you add a newline to the end of the textarea and then it doesn't fire the $watch. Not sure why... I think by default, angular trims the text in the textarea so the model isn't changing and not firing the $watch. – Dev01 May 08 '15 at 21:34
6

Using Gísli Konráð and midhunsezhi answers I was able to put together a directive that did what I want. Credit should really go to them.

.directive('noNewLines', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attributes, ngModelController) {
            var model = attributes.ngModel;
            var regex = new RegExp("^[^\n\r]*$");

            // $parsers handle input from the element where the
            // ng-model directive is set
            ngModelController.$parsers.unshift(function(value) {
                if(!value) return value;
                var modelValue = ngModelController.$modelValue;
                var isValid = regex.test(value);
                ngModelController.$setValidity('Does not match pattern', isValid);

                var transformedInput = value.replace(/[\n\r]/g, '');
                if(transformedInput !== value) {
                    ngModelController.$setViewValue(transformedInput);
                    ngModelController.$render();
                }
               return transformedInput;
            });

            // $formatters handle when the model is changed and 
            // the element needs to be updated
            ngModelController.$formatters.unshift(function(value) {
               if(!value) return value;
               var isValid = regex.test(value);
               ngModelController.$setValidity('Does not match pattern', isValid);
               return value;
            });

            element.on('keypress', function(e) {
                var char = String.fromCharCode(e.which);
                var text = angular.element(e.srcElement).val();
                if(!regex.test(char) || !regex.test(text)) {
                    event.preventDefault();   
                }                   
            });

        }
   };
 });

And it's used like this:

<textarea ng-model="text" no-new-lines></textarea>
Dev01
  • 13,292
  • 19
  • 70
  • 124
1

Here's a directive that you could use. It has options to block invalid input and to ignore case.

.directive('taPattern', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attributes, ngModelController) {
            var pattern = attributes.taPattern;  
            var flag = attributes.taPatternIgnoreCase === true ? 'i' : undefined;
            var blockInvalidInput = attributes.hasOwnProperty('taPatternBlockInvalid');
            var model = attributes.ngModel;
            if(!pattern) return;
            if(pattern[0] != '^')
                pattern = '^' + pattern;
            if(pattern[pattern.length - 1] != '$')
                pattern += '$';

            var regex = new RegExp(pattern, flag);

            // $parsers handle input from the element where the
            // ng-model directive is set
            ngModelController.$parsers.unshift(function(value) {
               if(!value) return value;
               var modelValue = ngModelController.$modelValue;
               var isValid = regex.test(value);
               ngModelController.$setValidity('Does not match pattern', isValid);
               return value;
            });

            // $formatters handle when the model is changed and 
            // the element needs to be updated
            ngModelController.$formatters.unshift(function(value) {
               if(!value) return value;
               var isValid = regex.test(value);
               ngModelController.$setValidity('Does not match pattern', isValid);
               return value;
            });

            if(blockInvalidInput){                    
                element.on('keypress', function(e) {
                    var char = String.fromCharCode(e.which);
                    var text = angular.element(e.srcElement).val();
                    if(!regex.test(char) || !regex.test(text)) {
                        event.preventDefault();   
                    }                   
                });
            }

        }
   };
 });

You can see a demo of it here: https://jsfiddle.net/mr59xf3z/3/

gislikonrad
  • 3,401
  • 2
  • 22
  • 24
  • Awesome! Best answer yet! Works great except when you copy and paste in text that has a newline character. It can be fixed by using @midhunsezhi ```$watch()``` solution in tandem with this directive but I would rather not use two solutions, any way to filter our newlines when copying them in? – Dev01 May 19 '15 at 18:40
0

Just an update for newer versions of Angular:

component.html:

Catch the keyup event, since at this point the bound variable will contain the new char.

<textarea [(ngModel)]="textAreaContent" (keyup)="keyUp($event)">

component.ts:

Don't look for the Enter key specifically, since the key code can vary between devices. Also that would not catch the unwanted char when user is pasting text. Get every keystroke, and just do a replace.

public textAreaContent;

keyUp(event:KeyboardEvent) {
  console.log(event.key+" pressed");
  this.textAreaContent = this.textAreaContent.replace(/[\r\n]+/," ");
}
Jette
  • 2,459
  • 28
  • 37
-2

Use <input instead of <textarea and set the height like textarea.

input.textarea {
  height: 100px;
  
  word-break: break-word;
  width: 300px;
}
<input class="textarea">
YOU
  • 120,166
  • 34
  • 186
  • 219
  • Great idea. I tried it out and it's very close. It works except the text is vertically aligned in the middle of the ```input``` which doesn't match the ```textarea``` style. Any way to fix it so that the text is vertically aligned top? – Dev01 May 13 '15 at 12:26
  • This is not a correct answer because input box cannot wrap the text. So it won't work like expected. You can align text to the top in css, but you'll always going to have only 1 line of text. that will go in overflow once you reach the end of the input box. – Filip Kis Sep 09 '17 at 13:39