16

KnockoutJS と CKEditor が関係する状況があります。

基本的に、サイトの一部は「シングル ページ」アプリ スタイルです。現在は 2 ページしか含まれていませんが、時間の経過とともに拡大する可能性があります。現在は、「リスト」ページとアイテムの「管理」ページにすぎません。リスト。

管理ページ自体には、ある種のリッチ テキスト エディターが必要です。会社全体のソリューションとして CKEditor を使用しました。

これらの 2 つのページは「単一ページ」スタイルであるため、明らかに CKEditor は管理要素に対して登録できません。これは、ページの読み込み時に存在しないためです。修正するのに十分な単純な問題です。サンプルとして、クリック イベントに CKEditor をアタッチしました。次の問題は、セットアップされた Knockout オブザーバブルが更新されないことでした。これは、CKEditor が実際に添付されているテキスト領域を変更せず、実際に編集するこれらすべての div/html 要素を作成するためです。

少しグーグルで調べた後、TinyMCEでこれを行っている人の例を見つけました-http ://jsfiddle.net/rniemeyer/GwkRQ/なので、これに似たものをCKEditorに適応できると思いました。

現在、私は実用的な解決策にかなり近づいています。この手法を使用して正しいオブザーバブルを初期化および更新し(コードを最後に投稿します)、サーバーに正しく投稿することさえできました-素晴らしいです。

私が現在経験している問題は、'Single Page' アプリ部分と CKEditor の再初期化にあります。

基本的に何が起こるかは、リストからクリックして管理し、保存して(リストページに戻る)、別の「管理」に移動すると、CKEditorが初期化されますが、値がありません。チェックしました更新コード (以下) と「値」には間違いなく正しい値がありますが、CKEditor 自体にプッシュされていません。

おそらく、CKEditor のフロー/初期化プロセスに関する理解の欠如、またはノックアウト バインディングに関する理解の欠如、または単一ページ アプリ用にセットアップされたフレームワークの問題である可能性があります。よくわかりません。

コードは次のとおりです。

//Test one for ckeditor
ko.bindingHandlers.ckeditor = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        var options = allBindingsAccessor().ckeditorOptions || {};
        var modelValue = valueAccessor();

        $(element).ckeditor();

        var editor = $(element).ckeditorGet();

        //handle edits made in the editor
        editor.on('blur', function (e) {
            var self = this;
            if (ko.isWriteableObservable(self)) {
                self($(e.listenerData).val());
            }
        }, modelValue, element);


        //handle destroying an editor (based on what jQuery plugin does)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            var existingEditor = CKEDITOR.instances[element.name];
            existingEditor.destroy(true);
        });
    },
    update: function (element, valueAccessor, allBindingsAccessor, context) {
        //handle programmatic updates to the observable
        var value = ko.utils.unwrapObservable(valueAccessor());
        $(element).html(value);
    }
};

したがって、HTML では、ViewModel の初期化時にバインディングを適用するのは、かなり標準的なノックアウト 'data-bind: ckeditor' です。

デバッガーを入れました。フローを確認するためのコードでは、最初に読み込むときに init を呼び出してから更新し、2 回目に行って ko.utils.domNodeDisposal にヒットして要素を破棄するように見えます。

私はそれを破壊しないようにしましたが、CKEditor はその名前で何かが既に存在すると不平を言います。私はそれを破棄せず、存在するかどうかを確認し、存在しない場合は初期化しようとしました.1回目は機能しますが、2回目はCKEditorがありません。

それを機能させるために欠けているものが1つだけあると思いますが、すべてのオプションを使い果たしました。

これら3つのことを統合することについて、私を助けることができる知識を持っている人はいますか?

私を助けてくれるノックアウトの専門家はいますか?

どんな助けでも大歓迎です。

MD

4

7 に答える 7

11

興味のある人のために私はそれを分類しました:

実行の基本的な順序だけでした。初期化する前に、値をtextareahtmlに設定する必要がありました。

これは、jqueryアダプター拡張機能を使用して要素に対して.ckeditor()を実行することに注意してください。

「ぼかし」の部分を行うためのより良い方法もおそらくあります。

この拡張機能も現時点ではオプションでは機能しませんが、比較すると非常に単純なはずです。

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

        $(element).html(value);
        $(element).ckeditor();

        var editor = $(element).ckeditorGet();

        //handle edits made in the editor

        editor.on('blur', function (e) {
            var self = this;
            if (ko.isWriteableObservable(self)) {
                self($(e.listenerData).val());
            }
        }, modelValue, element);
    }
};
于 2012-06-07T04:58:15.673 に答える
8

私はこれをしばらく使用してきましたが、.on("blur") アプローチでいくつかの問題を再度実行しました。つまり、人々がリッチ テキストをクリックしてテキストを入力し、フォームの [保存] ボタンに直接スクロールしたときに、オブザーバブルが十分な速さで更新されませんでした。遅延を処理する方法はたくさんありますが、もっと公式なものが欲しかったのです。CKEditor のドキュメントを調べたところ、この宝石が見つかりました: focusManager

これは、フォーカスとぼかしのすべてのインスタンスを処理し、真のぼかしイベントをコントロールに接続できる組み込み機能です。

これがリッチテキスト用の私の bindingHandler です。

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

       var txtBoxID = $(element).attr("id");
       var instance = CKEDITOR.instances[txtBoxID];

       var options = allBindingsAccessor().richTextOptions || {};
       options.toolbar_Full = [
            ['Source', '-', 'Format', 'Font', 'FontSize', 'TextColor', 'BGColor', '-', 'Bold', 'Italic', 'Underline', 'SpellChecker'],
            ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'],
            ['Link', 'Unlink', 'Image', 'Table']
       ];

       //handle disposal (if KO removes by the template binding)
       ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
          if (CKEDITOR.instances[txtBoxID]) { CKEDITOR.remove(CKEDITOR.instances[txtBoxID]); };
       });

       $(element).ckeditor(options);

       //wire up the blur event to ensure our observable is properly updated
       CKEDITOR.instances[txtBoxID].focusManager.blur = function () {
          var observable = valueAccessor();
          observable($(element).val());
       };
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {

       var val = ko.utils.unwrapObservable(valueAccessor());
       $(element).val(val);

    }
}
于 2013-02-04T14:03:01.863 に答える
5

初投稿なので何か間違っていたら教えてください

私のプロジェクトでは、保存されていない変更があるかどうかについて視覚的なフィードバックを提供したため、 で監視対象を更新する必要がありましたkeyupclickツールバーのボタンがクリックされたときもオンになります。これは、私の属性での使用とも一致していました。valueUpdate:['afterkeydown','propertychange','input']data-bind

また、パフォーマンスのために、.ckeditor(callback,options)ではなくのコールバック メソッド パラメータを使用しました.on(eventName,handler)

これは私が思いついたカスタム バインディングです。

ko.bindingHandlers.ckeditor = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        // get observable
        var modelValue = valueAccessor();;

        $(element).ckeditor(function(textarea) {
            // <span> element that contains the CKEditor markup
            var $ckeContainer = $(this.container.$);
            // <body> element within the iframe (<html> is contentEditable)
            var $editorBody =
                    $ckeContainer.find('iframe').contents().find('body');
            // sets the initial value
            $editorBody.html( modelValue() );
            // handle edits made in the editor - by typing
            $editorBody.keyup(function() {
                modelValue( $(this).html() );
            });
            // handle edits made in the editor - by clicking in the toolbar
            $ckeContainer.find('table.cke_editor').click(function() {
                modelValue( $editorBody.html() );
            });
        });


        // when ko disposes of <textarea>, destory the ckeditor instance
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).ckeditorGet().destroy(true);
        });
    },
    update: function (element, valueAccessor, allBindingsAccessor, context) {
        // handle programmatic updates to the observable
        var newValue = ko.utils.unwrapObservable(valueAccessor());
        var $ckeContainer = $(element).ckeditorGet().container;
        if( $ckeContainer ) {
            // <span> element that contains the CKEditor markup
            $ckeContainer = $($ckeContainer.$);
            // <body> element within the iframe (<html> is contentEditable)
            var $editorBody =
                    $ckeContainer.find('iframe').contents().find('body');
            // if new value != existing value, replace it in the editor
            if( $editorBody.html() != newValue )
                $editorBody.html( newValue );
        }
    }
};

理由:

コンテンツ内でandを検索するこのかなりハックな方法の代わりに、おそらく.getData()andを使用する必要があることはわかっています。.setData(html)<body><table class="cke_editor">iframe

理由は、update:次の条件の場合です。

if( $(element).ckeditorGet().getData() != newValue )
    $(element).ckeditorGet().setData( newValue )

CKEditor が行う HTML フォーマットのため、最初は true でした。そのため、ダーティ レコードではないにもかかわらず、ダーティ レコードについてユーザーに通知しました。私にとって非常に具体的なので、理由を知りたい場合に備えて知っておくべきだと思いました。

于 2012-09-27T13:36:25.763 に答える
0

これを書き直して、ぼかしの代わりに、キーアップごとにオブザーバブルを更新しました。これはオブザーバブルへのさらに多くの更新ですが、ボタンで保存している限り、これは今のところうまく機能しているようです!

//handle edits made in the editor
CKEDITOR.instances.thread_message.on('contentDom', function() {
  CKEDITOR.instances.thread_message.document.on('keyup', function(e) {

    var self = this;
    if (ko.isWriteableObservable(self)) {
      var ckValue = CKEDITOR.instances.element_id.getData();
      self(ckValue);
      //console.log("value: " + ckValue);
    }
  }, modelValue, element);
});
于 2013-06-04T18:00:14.353 に答える
0

この手法を CKEditor 4 で使用して、既存の (一方向) "html" バインディングを双方向バインディングで上書きしました。完全/静的エディターとは異なる動作をする可能性があるインライン CKEditor を使用しています (不明)。「値」バインディングから始めて、innerHTML代わりに動作するように微調整しました。

ko.bindingHandlers.html = {
    'init': function (element, valueAccessor, allBindingsAccessor) {
        var eventsToCatch = ["blur"];
        var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
        var valueUpdateHandler = null;

        if (requestedEventsToCatch) {
            if (typeof requestedEventsToCatch == "string")
                requestedEventsToCatch = [requestedEventsToCatch];

            ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
            eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
        }

        valueUpdateHandler = function () {
            var modelValue = valueAccessor();
            var oldValue = ko.utils.unwrapObservable(modelValue);
            var elementValue = element.innerHTML;
            var valueHasChanged = (oldValue !== elementValue);

            if (valueHasChanged)
                modelValue(elementValue);
        }

        ko.utils.arrayForEach(eventsToCatch, function (eventName) {
            var handler = valueUpdateHandler;

            if (eventName.indexOf("after") == 0) {
                handler = function () {
                    setTimeout(valueUpdateHandler, 0)
                };

                eventName = eventName.substring("after".length);
            }

            ko.utils.registerEventHandler(element, eventName, handler);
        });
    },
    'update': function (element, valueAccessor) {
        var newValue = ko.utils.unwrapObservable(valueAccessor());
        var elementValue = element.innerHTML;
        var valueHasChanged = (newValue !== elementValue);

        if (valueHasChanged)
            element.innerHTML = newValue;
    }
};

警告: これはおそらく、CKEditor 独自のchangeイベントを使用するように更新する必要があります。

于 2013-04-25T14:36:01.577 に答える
-1

「ぼかし」部分については、以下のコードを試してみましたが、うまくいくようです

              editor.on('blur', function (e) {
                var self = this;
                if (ko.isWriteableObservable(self)) {
                    var ckValue = e.editor.getData();
                    self(ckValue);
                }
            }, modelValue, element);

他の場所からオブザーバブルを「更新」する場合、「更新」部分がまだ必要だと思います(これは「ぼかし」によって処理されるため、編集ではありません)

于 2012-08-26T23:41:08.600 に答える