Search code examples
knockout.jscustom-bindingbindinghandlersmedium-editor

medium-editor custom binding


ko.bindingHandlers.editor = {
        init: function (element, valueAccessor, allBindingsAccessor) {

            var initialValue = ko.unwrap(valueAccessor);
            var options = allBindingsAccessor().editoroptions || {};
            var editorElement = '#' + $(element).attr('id');

            $(editorElement).html(initialValue);

            ko.utils.registerEventHandler(element, "keyup", function () {
                var newvalue = $(editorElement).html().toString()
                valueAccessor(newvalue);
            });

            $(editorElement).on('input', function () {
                $(element).trigger("keyup");
            });

            var editor = new MediumEditor(editorElement, {
                buttons: ['bold', 'italic', 'underline', 'quote', 'anchor', 'superscript', 'subscript', 'orderedlist', 'unorderedlist', 'pre', 'outdent', 'indent'],
                buttonLabels: 'fontawesome',
                placeholder: options.initialText
            });

            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                editor.deactivate();
            });
        }
    };

medium-editor.js

this is how i declared my custom binding, but the problem is then underling observable gets updated when i type in editor. here is how i use it in markup.

    <div class="editor" id="ruleseditor" data-bind="editor: RuleText(), editoroptions:{initialText: 'No rules defined please enter new rules for application'}"></div>

and here is my VM bounded to my html.

define(["require", "exports", 'knockout'], function(require, exports, ko) {
var rules = (function () {
    function rules() {
        var _this = this;
        this.IsLoading = ko.observable();
        this.RuleText = ko.observable("thet with some optiona markup <h3> displayd in cool editor<?h3>");
        this.RuleText.subscribe(function (newvalue) {
            console.log("new value for rule ....... ", newvalue);
        });
        this.RuleOriginal = ko.computed(function () {
            return _this.RuleText();
        });
        this.activate = this._activateCallback;
    }
    rules.prototype._activateCallback = function () {
        this._getAppRules();
    };

    rules.prototype._getAppRules = function () {
    };
    return rules;
})();

return rules;
});

initial custom binding works as expected because I can see editor script activated and value gets passed to the editor my problem is that i can not update my observable after updating my html in editor.


Solution

  • The way you access valueAccessor is wrong, it is not what you think. Please read about valueAccessor in http://knockoutjs.com/documentation/custom-bindings.html

    Try:

    (1) change your binding from editor: RuleText() to editor: RuleText as RuleText is the observable you want, not the underneath value.

    (2) fix your binding Handler

    ko.bindingHandlers.editor = {
        init: function (element, valueAccessor, allBindingsAccessor) {
    
            // you have to call valueAccessor() to get back the observable you passed in your binding.
            var value = valueAccessor(); 
            var options = allBindingsAccessor().editoroptions || {};
            var editorElement = '#' + $(element).attr('id');
    
            // unwrap observable value
            $(editorElement).html(ko.unwrap(value)); 
    
            ko.utils.registerEventHandler(element, "keyup", function () {
                var newvalue = $(editorElement).html().toString();
    
                // 'value' (not valueAccessor) is the observable you passed in.
                value(newvalue);
            });
    
            $(editorElement).on('input', function () {
                $(element).trigger("keyup");
            });
    
            var editor = new MediumEditor(editorElement, {
                buttons: ['bold', 'italic', 'underline', 'quote', 'anchor', 'superscript', 'subscript', 'orderedlist', 'unorderedlist', 'pre', 'outdent', 'indent'],
                buttonLabels: 'fontawesome',
                placeholder: options.initialText
            });
    
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                editor.deactivate();
            });
        }
    };