TinyMce исчезает в SPA — привязка Knockout оценивается дважды, что приводит к сбою редактора

Я пытаюсь внедрить TinyMCE в шаблон HotSpa Джона Папы.

Сейчас я использую Knockout 3.1.0 и Durandal 2.0.

По какой-то неизвестной причине мой собственный метод инициализации привязки нокаута запускается только один раз при первой загрузке и дважды при обновлении представления.

Под обновлением я подразумеваю использование маршрутизатора Durandal, я снова перешел к тому же представлению (но с другими параметрами). Он еще прошел весь жизненный цикл дурандаля, крюки отсоединяемого и присоединяемого вызываются снова.

У меня был код отладки, где второй вызов вернул .editors.length = 1, что означает, что редактор уже был инициализирован при первом вызове.

Примечание. Внутри метода инициализации у меня есть метод ko.utils.domNodeDisposal.addDisposeCallback, и по какой-то причине он вызывается после второго вызова инициализации. Я закомментировал это, но tinyMCE по-прежнему исчезает. Я просмотрел редакторы и заметил, что он считается скрытым. isHdidden = true.

<textarea id="mytinymce" class="tinymce" 
          data-bind="attr: {id: FieldId}, tinymce: FieldValue”></textarea>

Страница изначально правильно загружается с помощью элемента управления TinyMCE, однако после обновления экрана происходит сбой.

Я ухожу от представления и обратно.

ko.bindingHandlers.tinymce = {
        init: function (element, valueAccessor, allBindingsAccessor, 
                        context, arg1, arg2) {
            var options = allBindingsAccessor().tinymceOptions || {};
            var modelValue = valueAccessor();
            var value = ko.utils.unwrapObservable(valueAccessor());

            var el = $(element);
            var id = el.attr('id');

            //options.theme = "advanced";
            options.theme = "modern";

            options.menubar = false;
            options.plugins = [
                "advlist autolink autosave link image lists charmap print preview hr anchor pagebreak spellchecker",
                "searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking",
                "table contextmenu directionality template textcolor paste fullpage textcolor"
            ];
            //tinymce buttons
            //http://www.tinymce.com/tryit/classic.php
            options.toolbar_items_size = 'small';
            options.toolbar1 =
            "bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | forecolor backcolor | hr removeformat | subscript superscript  ";

            ////handle edits made in the editor. Updates after an undo point is reached.
            options.setup = function (editor) {
                editor.on('change', function (e) {
                    if (ko.isWriteableObservable(modelValue)) {
                        var content = editor.getContent({ format: 'raw' });
                        modelValue(content);
                    }
                });

            };

            el.html(value);

            $(element).tinymce(options);

            //handle disposal (if KO removes by the template binding)
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                var tinymceInstance = tinyMCE.get(id);
                if (tinymceInstance != undefined) {
                    // if instance already exist, then get rid of it... we don't want it
                    tinymceInstance.remove();
                }
            });              
        },
        update: function (element, valueAccessor, allBindingsAccessor, 
                          context) {

            var el = $(element);
            var value = ko.utils.unwrapObservable(valueAccessor());
            var id = el.attr('id');

            //handle programmatic updates to the observable
            // also makes sure it doesn't update it if it's the same. 
            // otherwise, it will reload the instance, causing the cursor to jump.
            if (id !== undefined) {
                //$(el).tinymce();

                var tinymceInstance = tinyMCE.get(id);
                if (!tinymceInstance)
                    return;
                var content = tinymceInstance.getContent({ format: 'raw' });
                if (content !== value) {
                    //tinymceInstance.setContent(value);
                    valueAccessor(content);
                    //$(el).html(value);
                }
            }
        }
    };

К сожалению, я не вижу никаких ошибок в консоли, но редактор (включая текстовую область) скрыт и не виден.


person Michael Pang    schedule 09.06.2014    source источник
comment
Есть ли у них какие-либо родительские элементы, которые имеют привязки if, foreach или template? Они могут привести к повторной инициализации дочерних элементов.   -  person Robert Slaney    schedule 10.06.2014
comment
Да, у нас есть некоторый код, который говорит, что шаблон представления должен создаваться только в том случае, если showTinyMce = true, и мы не можем избавиться от этого.   -  person Michael Pang    schedule 10.06.2014
comment
Когда вызывается метод инициализации, вы можете следить за трассировкой стека, чтобы выяснить, какой наблюдаемый объект вызвал повторную инициализацию. если привязки обычно являются виновниками, но привязки шаблонов также могут запускать сборку DOM. Еще одна вещь, на которую следует обратить внимание, это то, что компаратор равенства observable сравнивает только экземпляры объектов или собственные типы, он не будет отслеживать граф объектов и сравнивать значения. Это поймало меня, прежде чем вызвать неожиданные обновления.   -  person Robert Slaney    schedule 11.06.2014
comment
Пробовали ли вы установить переменную в функции init при первом ее вводе и проверить, настроена ли она для запуска кода только тогда, когда это не так?   -  person GôTô    schedule 12.06.2014
comment
В инициализации я проверил, что если tinyMce уже активен, просто вернитесь. Но tinyMCE по-прежнему скрыт.   -  person Michael Pang    schedule 15.06.2014


Ответы (2)


Я изменил html на:

<textarea data-bind="tinymce: FieldValue"></textarea>

то есть удалена привязка атрибута id.

И теперь это работает.

Я заметил, что он по-прежнему дважды вызывает init, но он работает.

Вы должны позволить TinyMCE управлять идентификатором.

person Michael Pang    schedule 17.06.2014
comment
Вот что мне нужно было знать. Поле ID боя. попытка установить при использовании Tiny действительно работала в SPA при перезагрузке.. ой. Теперь это работает ;-) Спасибо, Майкл Пэнг. - person phil soady; 17.06.2014
comment
Мне нравится твоя повязка. Как бы вы сделали его настраиваемым для опций и плагинов? - person Asle G; 12.09.2014

Вы говорите, что вводите функцию init дважды.

Вы пробовали простой код, чтобы предотвратить запуск его кода во второй раз? (Я бы попробовал, если бы у вас был работающий jsFiddle)

Что-то типа:

ko.bindingHandlers.tinymce = {
    init: function (element, valueAccessor, allBindingsAccessor, 
                    context, arg1, arg2) {
        if (!$(element).data("tinymce-hasinitstarted")) {
            $(element).data("tinymce-hasinitstarted", true);
            //your code here
        }
    }
}
person GôTô    schedule 12.06.2014
comment
Хотя это решение можно использовать и, вероятно, оно сработает, это гамбиарра, уродливый хак и всего лишь обходной путь для его проблемы, на самом деле он не решает проблему, он только скрывает ее и не должен даваться как решение его проблемы. - person Buzinas; 14.06.2014
comment
Ну, если у вас есть решение, которое не является уродливым, не стесняйтесь публиковать его. Без скрипки я не могу предложить лучшего способа, и я думаю, что самое главное - иметь его. - person GôTô; 15.06.2014