Restricting Characters in Input Field to a Set of Characters

17,292

Solution 1

To 'restrict some characters from being typed in' what comes in my mind is to attach event handlers for 'keyup', 'change', 'paste' events on inputs and when they are triggered to 'clean' their values against your pattern. I implemented the logic as jQuery plugin but you can adapt it to angular, use better naming or whatever you want.

The plugin:

$.fn.restrictInputs = function(restrictPattern){
    var targets = $(this);

    // The characters inside this pattern are accepted
    // and everything else will be 'cleaned'
    // For example 'ABCdEfGhI5' become 'ABCEGI5'
    var pattern = restrictPattern || 
        /[^0-9A-Z !\\"#$%&'()*+,\-.\/:;<=>?@\[\]^_`{|}~]*/g; // default pattern

    var restrictHandler = function(){
        var val = $(this).val();
        var newVal = val.replace(pattern, '');

        // This condition is to prevent selection and keyboard navigation issues
        if (val !== newVal) {
            $(this).val(newVal);
        }
    };

    targets.on('keyup', restrictHandler);
    targets.on('paste', restrictHandler);
    targets.on('change', restrictHandler);
};

Usage:

$('input').restrictInputs();

// Or ...

$('.my-special-inputs-decorated-with-this-class').restrictInputs();

Here is a JsFiddle Demo

Note: you can try to change the implementation to accept string with forbidden characters instead of regular expression and create the regex dynamically. Also you may find others events which are appropriate for triggering the 'cleaning'.

Solution 2

The Angular way to do this is to make use of Angular's ngModelController.$parsers See the documentation:

$parsers

Array of functions to execute, as a pipeline, whenever the control reads value from the DOM. Each function is called, in turn, passing the value through to the next. The last return value is used to populate the model. Used to sanitize / convert the value as well as validation. For validation, the parsers should update the validity state using $setValidity(), and return undefined for invalid values.

Here is an example of a re-usable directive using this approach:

app.directive('inputRestrictor', [
  function() {
    return {
      restrict: 'A',
      require: 'ngModel',
      link: function(scope, element, attr, ngModelCtrl) {
        var pattern = /[^0-9A-Z !\\"#$%&'()*+,\-.\/:;<=>?@\[\]^_`{|}~]*/g;

        function fromUser(text) {
          if (!text)
            return text;

          var transformedInput = text.replace(pattern, '');
          if (transformedInput !== text) {
            ngModelCtrl.$setViewValue(transformedInput);
            ngModelCtrl.$render();
          }
          return transformedInput;
        }
        ngModelCtrl.$parsers.push(fromUser);
      }
    };
  }
]);

Here is a plnkr demo.

*Regex pattern for the above directive taken from Viktor Bahtev answer.

You can obviously extend this directive to take an input parameter as well. I'll leave that for your exersize.

Solution 3

Try to use regExp to filter unnecessary characters on ng-keypress by passing $event.

# It'll be more clear in plnk!

Share:
17,292
Kyle V.
Author by

Kyle V.

Updated on June 17, 2022

Comments

  • Kyle V.
    Kyle V. almost 2 years

    Question Update: How can I prevent all characters except for the ones specified in a char array from being typed into an input field using AngularJS (or jQuery)?


    Old Question:

    I have a simple <input type="text" /> field in my AngularJS application and I want the user to only be able to enter the following characters into the field:

    0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~
    

    I know that I can add ng-pattern="allowed" to the <input> and then set $scope.allowed to some regex pattern and that will mark the input invalid if any invalid characters are entered, but I also want to prevent restricted characters from being input into the field AT ALL.

    So my question is composed of two questions:

    1. What regex pattern do I use to restrict the character set to the one I posted above?
    2. How do I prevent illegal characters from being entered in the field? (e.g. if you type a lowercase letter then it won't appear in the field to begin with, similarly if you try to paste in text containing any illegal characters they will be removed immediately)
  • Kyle V.
    Kyle V. over 9 years
    This works except that I can copy+paste in uppercase characters to get around it. I was thinking validate the model on ng-change and just remove invalid characters from the model by replacing them with "" but I don't know how to do a string replace on every char not included in the regex set...
  • Alex Cross
    Alex Cross over 9 years
    As I understand you don't want to pass capital letters so i suppose that is plnk what you want.
  • Kyle V.
    Kyle V. over 9 years
    No I want to remove all characters that AREN'T in the list in the OP.
  • Dave Alperovich
    Dave Alperovich over 9 years
    @StickFigs, cut and paste has long been a "sticky" problem with model binding and validation. The paste event is NOT an event that AngularJS naturally listens to. Adjust Alex's answer to validate when the input loses focus. That is not ideal, but is probably as close as you will come to validating after a cut-paste.
  • Dave Alperovich
    Dave Alperovich over 9 years
    @AlexCross, a suggestion for future answers. Plunkers are Great, but include the vital snippets of code in your answer too. This makes a higher quality answer and one that is NOT dependent on this Plunker or the site as continuing. The best answers are a combination of 1) explanation, 2) code snippets, 3) Plnker / Fiddle
  • Dave Alperovich
    Dave Alperovich over 9 years
    hmmmm... this would definitely work, but is most likely to cause issues with Angular Model being updated. There are problems with JQuery and Angular playing nice together
  • Dave Alperovich
    Dave Alperovich over 9 years
    @StickFigs, I tried Alex's Plunkr and was not able to paste any caps. Please take me through your work. The downside is it DOES NOT ALLOW any characters that are lower case to be pasted either. Can both of you confirm what I see? If so, LMK if this works for OP or not.
  • Viktor Bahtev
    Viktor Bahtev over 9 years
    @DaveA I am not very familiar with AngularJS and the OP asked for Angular or jQuery solution so I went for jQuery. If you think that this solution can be adapted to Angular feel free to update my answer, to give suggestions or to post another answer :)
  • Dave Alperovich
    Dave Alperovich over 9 years
    Viktor, I agree that you have a good implementation. It may in fact be usable with Angular. I will think this over!
  • Dave Alperovich
    Dave Alperovich over 9 years
    Victor, take a look at my Plunker. I added your regex to Alex's and I think I have a proof of your concept!plnkr.co/edit/PiW7yFotqTylDTY3d8Ae?p=preview
  • Dave Alperovich
    Dave Alperovich over 9 years
    +1 it works. Only citicism, your regex allows single quotes and spaces
  • Kyle V.
    Kyle V. over 9 years
    I came across a solution on my own that is identical to this one but used ng-keyup, ng-paste, ng-change, etc. so I will mark this as the correct answer. Thanks!
  • Casimir et Hippolyte
    Casimir et Hippolyte over 9 years
    is it really necessary to write this monstruous character class when you can simply write (if you take a look at the ascii table): [ -`{-~]
  • Casimir et Hippolyte
    Casimir et Hippolyte over 9 years
    An other thing: your pattern can match an empty string since you use the * quantifier. Is it really what you want? (note that with this pattern you will replace nothing with nothing (that is useless) on each positions in the string until you encounter forbidden characters.)
  • Beyers
    Beyers over 9 years
    @CasimiretHippolyte I think you are missing the point of my answer. The answer is more about how to filter input text using the recommended AngularJs approach (i.e. using a directive with $parsers), than on the actual RegEx pattern. I thought that was clear given the fact that I clearly indicated I used the pattern from the already accepted answer. Just a suggestion, why dont you post an answer with your super-lean pattern?
  • Casimir et Hippolyte
    Casimir et Hippolyte over 9 years
    Oh, you have only copy/paste the pattern of the accepted answer! Please excuse me, I am so sorry.
  • Casimir et Hippolyte
    Casimir et Hippolyte over 9 years
    About "why dont you post an answer with your super-lean pattern?". It's simple, I always comment an answer on a question when I haven't posted an answer myself. When I post an answer I don't comment other answers (or in very rare situations).
  • Dave Alperovich
    Dave Alperovich over 9 years
    @CasimiretHippolyte, I think you miss the significance. Beyers was applying the regex (all it really is) with an AngularJS Directive. This may not seem important until you have done significant work with AngularJS, but performance is better and applications much cleaner if you avoid the use of JQuery and create a purely AngularJS Answer. I don't believe the answer merits a down-vote, and while OP was happy with the hybrid answer, I find this useful and worthy of a vote-up.
  • Beyers
    Beyers over 9 years
    @DaveA Thank you Dave, you obviously grasped the intent of my answer.