3

I am trying to write a custom binding to replace "/n" with "<br />" within a "<p>" element.

I understand the concept more or less, but I'm stuggling to get it going. Can anyone suggest where I'm going wrong. I don't really want to use a computed observable, as I want to keep the real value using "/n" rather than "<br />".

ko.bindingHandlers.nl2br = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var field = valueAccessor();
        field.replace(/\n/g, '<br />');
        $(element).val(field)
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var field = valueAccessor();
        field.replace(/\n/g, '<br />');
        $(element).val(field)
    }
};
Jeroen
  • 60,696
  • 40
  • 206
  • 339
Wellso
  • 119
  • 3
  • 12
  • For anyone who just needs to ***visually*** get new lines where a \n character is in the text: this is **possible with one css-property** - see the following question: https://stackoverflow.com/questions/17422895/knockout-js-carraige-return-in-paragraph-text – David Rettenbacher Oct 16 '15 at 08:27

1 Answers1

6

TL;DR Answer

ko.bindingHandlers.nl2br = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var field = ko.utils.unwrapObservable(valueAccessor());
        field = field.replace(/\n/g, '<br />');
        ko.bindingHandlers.html.update(element, function() { return field; });
    }
};

Full Answer

First things first, the replace call should probably be:

field = field.replace(/\n/g, '<br />');

Otherwise the new string gets discarded.

Apart from that, I wouldn't recommend setting the element value like that directly. Rely on the existing handlers (that presumably are well tested in various browsers) to do the heavy lifting. See R.P. Niemeyer's blog post on the subject (specifically item 3).

You can use either the the text binding which will literally render "<br />" or (if you trust the input!) the html binding if you want to render a newline for the <br />. The latter looks like this:

ko.bindingHandlers.nl2br = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var field = ko.utils.unwrapObservable(valueAccessor());
        field = field.replace(/\n/g, '<br />');
        ko.bindingHandlers.html.update(element, function() { return field; });
    }
};

ko.applyBindings({ description: ko.observable("This is\nSparta!") });
p, pre { background-color: #dde; margin: 4px 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<strong>Text version in &lt;p&gt;:</strong>
<p data-bind="text: description"></p>
<hr />
<strong>Text version &lt;pre&gt;:</strong>
<pre data-bind="text: description"></pre>
<hr />
<strong>NL2BR version in &lt;p&gt;:</strong>
<p data-bind="nl2br: description"></p>
Jeroen
  • 60,696
  • 40
  • 206
  • 339
  • Forgot to mention my value was an observable, needed to call itself as a function to return the value. Thanks for all the help! – Wellso Aug 08 '13 at 13:32
  • @Wellso It's better in that case to use `unwrapObservable`, see [this advice](http://stackoverflow.com/questions/9624401/when-to-use-ko-utils-unwrapobservable). My answer was lacking in that regard, I've updated it accordingly. – Jeroen Jun 30 '15 at 21:48