Primefaces inputTextArea new lines counter problem

Goal

To count the new line as 2 characters instead of 1 (as it should) in a Primefaces inputTextArea

Description

Recently, I had implemented a inputTextArea with a counter to display the number of remaining characters. Everything was well until we tested the implementation in which, for a textarea with a size of 10 (lets assume it is only 10 for testing purposes – in the reality, it was much more than 10), we introduced the following text (which is, actually, 10 characters long if the new line is counted as one character only, as it was in the interface: [1-8]\n9):

12345678

9

On the other hand, if we submitted the form as it was and the corresponding database field was 10 characters long, we would get a database exception telling us that the field was truncated (actually, the new line counts for two characters! Additionally, we tested the introduction of a JSF validator associated to the textArea field and the form no longer could be submitted because the validator would count the new line as 2 characters thus leading the textArea to be 11-characters long). In this recipe, I will tell you the “hack” I implemented to go through this issue and still use the PrimeFaces counter.

How to

The recipe consists on:

  1. Implement a Javascript function that counts the number of occurrences of a given string within a target string (the new line character in the full text area content)
  2. Extend the PrimeFaces inputTextArea widget Javascript code so that the previous function is taken into account

1 – The function that counts the number of occurrences of a given string within a target string has been adapted from stackoverflow and included within the PrimeFaces inputTextArea widget code. The relevant code is shown next:

/**
 * PrimeFaces InputTextarea Widget
 */
PrimeFaces.widget.InputTextarea = PrimeFaces.widget.BaseWidget
  .extend({

    ...

    /**
     * From http://stackoverflow.com/questions/4009756/how-to-count-string-occurrence-in-string
     * 
     * Function count the occurrences of substring in a string;
     * 
     * @param {String}
     *            string Required. The string;
     * @param {String}
     *            subString Required. The string to search for;
     * @param {Boolean}
     *            allowOverlapping Optional. Default: false;
     */
    occurrences : function(string, subString, allowOverlapping) {
      string += "";
      subString += "";
      if (subString.length <= 0) {
        return string.length + 1;
      }

      var n = 0, pos = 0;
      var step = (allowOverlapping) ? (1) : (subString.length);

      while (true) {
        pos = string.indexOf(subString, pos);
        if (pos >= 0) {
          n++;
          pos += step;
        } else {
          break;
        }
      } 
      return (n);
    },
    ...
  });

2 – Invoke the previous Javascript function in the Primefaces inputTextArea widget code that is responsible for checking the remaining characters for the input text area component (only the relevant parts are shown here and, in bold, we show the changes we added to Primefaces’ original code):

/**
 * PrimeFaces InputTextarea Widget
 */
PrimeFaces.widget.InputTextarea = PrimeFaces.widget.InputTextarea.extend({
 
    ...

    applyMaxlength : function() {
      var _self = this;

      this.jq.keyup(function(e) {
      var value = _self.jq.val(),
      
      // the length of the value when it has empty
      // lines, counts them as only 1 char. In order to fix
      // problems with data truncation on the database, we simply
      // add the number of occurrences of the empty
      // line so that each empty line counts as 2 characters
      // instead of 1
      newLineOccurrences = _self.occurrences(value, '\n'),
      length = value.length + newLineOccurrences;

      if (length > _self.cfg.maxlength) {
        _self.jq.val(value.substr(0, _self.cfg.maxlength - newLineOccurrences));
      }

      if (_self.counter) {
        _self.updateCounter();
      }
    });
  },

    updateCounter : function() {
      var value = this.jq.val(),
      // the length of the value when it has empty lines,
      // counts them as only 1 char. In order to fix
      // problems with data truncation on the database, we simply add
      // the number of occurrences of the empty
      // line so that each empty line counts as 2 characters instead
      // of 1
      length = value.length + this.occurrences(value, '\n');

      if (this.counter) {
	var remaining = this.cfg.maxlength - length, remainingText = this.cfg.counterTemplate
	.replace('{0}', remaining);

	this.counter.html(remainingText);
      }
    },

    ...

  });

Explanations

This recipe is more like a hack although a hack that worked quite well in my case. If you have an interface which uses Primefaces’ input text area counter to check the maximum size available for your input text area fields, then you may use this recipe to guarantee that the new line character will not result in an exception being thrown in your application due to data truncation.

4 comments

  1. The correct occorrences method is:

    occurrences : function(string, subString, allowOverlapping) {
    string += “”;
    subString += “”;

    if (subString.length = 0) {
    ++n;
    pos += step;
    } else break;
    }
    return (n);
    },

    1. Alexandre,

      Thank you for your comment but if you look at both methods, they are no different. The increment to n being prefixed or suffixed does not cause any behavioural differences.

      When you say something like the “correct method is the following”, it seems that the original version (which is semantically totally equivalent to your version) is not correct, which does not seem to be true.

    1. Hello, Sergio.

      Thank you for your compliment 🙂

      I never really thought about it, actually.. But I’m almost sure that the latest version of PF has this fixed..

      Nevertheless, I can think about that or you can propose it as well.. I don’t mind 🙂

      Regards,
      PZ

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s