Bestin John Bestin John - 23 days ago 8
HTML Question

Limit the number of rows and number of characters per row using knockoutJS

I am creating an application using KnockoutJS where I need to limit the number of lines(rows) in a

<textarea>
to 8 and the number of characters per line to 25. I am facing the following issues:


  1. Not able to limit the number of lines (rows) to 8.

  2. Able to limit the characters per line to 25 but if a key is pressed continuously this functionality also does not work.



Here is my code:

HTML



<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
<!-- This is a *view* - HTML markup that defines the appearance of your UI -->
<textarea data-limit-rows="true" cols="30" rows="9" data-bind="event: {keyup:check}"></textarea>

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-debug.js"></script>
<script src="js/script.js" charset="utf-8"></script>
</body>

</html>


JS

function AppViewModel() {
this.check= function(d,e) {
var maxLength = 25;
var text = $(e.currentTarget).val(),
lines = text.split(/(\r\n|\n|\r)/gm),
//maxRows = parseInt($(e.currentTarget).attr("rows"));
maxRows=8;
for (var i = 0; i < lines.length; i++) {
if (lines[i].length > maxLength) {
lines[i] = lines[i].substring(0, maxLength);
}
}

};
}

// Activates knockout.js
ko.applyBindings(new AppViewModel());


Thanks in Advance

Answer

I'd advice you to use a ko.computed with a read and write function. Data-bind this observable using the textInput binding.

The write stores the text input in an internal backing field. The read transforms and limits the string to meet your requirements.

Note that the notify: always extension is important here. When new raw input generates the same computed input, knockout will not trigger an update and your textarea will not be cleared. By using notify: always, you can tell knockout to reset the textarea every time, even if it's to the same value.

function AppViewModel() {
  var MAX_LINES = 4;
  var MAX_LENGTH = 5;
  var rawValue = ko.observable("");
  this.value = ko.computed({
    read: function() {
      return rawValue()
        .split("\n")
        .reduce(function(lines, line) {
          if (line.length === 0) {
            lines.push("");
          } 
          
          while(line.length) {
            lines.push(line.slice(0, MAX_LENGTH));
            line = line.slice(MAX_LENGTH, line.length); 
          }

          return lines;
        }, [])
        .slice(0, MAX_LINES)
        .join("\n");
    },
    write: rawValue
  }).extend({"notify": "always"});
}

// Activates knockout.js
ko.applyBindings(new AppViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<textarea cols="25" rows="9" data-bind="textInput: value"
          style="font-family: monospace"></textarea>