Knockoutjs + Ckeditor 需要帮助解决问题

Knockoutjs + Ckeditor Need help figuring something out

我有需要与 ckeditor 连接的代码:

基本上我想做的是通过以下方式将其与已经存在的数据绑定连接:

textarea autocomplete="off" class="form-control" data-bind="rev_ckeditor, value: app.models.userReview.body" id="editor" maxlength="50000" name="body" cols="50" 行="10"

上面的textarea既有值data-bind又有ckeditor的data-dind。但是出现了 ckwditor 但没有显示值的数据绑定,有什么问题吗?

代码:

(function($) {
'use strict'

app.viewModels.reviews = {

    /**
     * All reviews.
     *
     * @type ko.observable(Array)
     */
    sourceItems: ko.observableArray([]),

    /**
     * Sorting type and order.
     *
     * @type ko.observable(String)
     */
    currentSort: ko.observable(),

    /**
     * Holds average score of all critic reviews.
     *
     * @type ko.observable(String)
     */
    criticAverage: ko.observable(),

    /**
     * Holds count of all critic reviews.
     *
     * @type ko.observable(String)
     */
    criticCount: ko.observable(),

    /**
     * Holds average score of all user reviews.
     *
     * @type ko.observable(String)
     */
    userAverage: ko.observable(),

    /**
     * Holds count of all user reviews.
     *
     * @type ko.observable(String)
     */
    userCount: ko.observable(),

    /**
     * Whether to who user, critic or all reviews.
     *
     * @type ko.observable(String)
     */
    currentType: ko.observable(),

    /**
     * Send request to server to create a new
     * user review.
     *
     * @return void
     */
    create: function(form) {
        var self = this;

        var params = {
            data: ko.toJSON(app.models.userReview),
            success: function(response) {
                var exists = false;

                $.each(self.sourceItems(), function(i,v) {

                    //if user has already written a review for this game we'll just replace
                    //it with this one as that's what backend is doing as well
                    if (v.type == 'user' && v.user_id == parseInt(vars.userId)) {

                        self.sourceItems()[i] = ko.toJS(app.models.userReview);
                        self.sourceItems.notifySubscribers();
                        exists = true;
                        return false;
                    }
                });

                if ( ! exists) {
                    self.sourceItems.push(ko.toJS(app.models.userReview));
                }

                $('#review-modal').modal('hide');
                app.utils.noty(response, 'success');
            },

            /**
             * Append any validation errors returned to new review form.
             *
             * @param  jq
             * @return void
             */
            error: function(jq) {
                $('.alert').remove();
                app.utils.appendError(jq);
            },
            url: form.action,
        };

        app.utils.ajax(params);
    },

    /**
     * Handle user click on review edit button.
     *
     * @param  app.models.review review
     * @return void
     */
    edit: function(review) {
        app.models.userReview.id(review.id);
        app.models.userReview.title(review.title);
        app.models.userReview.body(review.body);
        app.models.userReview.score(review.score);
        app.models.userReview.story_rev(review.story_rev);
        app.models.userReview.animation_rev(review.animation_rev);
        app.models.userReview.sound_rev(review.sound_rev);
        app.models.userReview.characters_rev(review.characters_rev);
        app.models.userReview.enjoyment_rev(review.enjoyment_rev);

        $('#review-modal').modal('show');
    },

    /**
     * Handle user click on delete button.
     *
     * @param  Object review
     * @return void
     */
    delete: function(review) {
        var self = app.viewModels.reviews;

        app.utils.ajax({
            url: vars.urls.baseUrl+'/movies/'+vars.titleId+'/reviews/'+review.id,
            type: 'DELETE',
            data: ko.toJSON(vars.token),
            success: function() {
                self.sourceItems.remove(review);
            }
        })
    }
};

/**
 * Calculate average critic/user review score as well as review counts.
 *
 * @return void
 */
 app.viewModels.reviews.calculateMeta = ko.computed(function() {
     var self     = this, score = 0,
         crCount  = 0,    crAvg  = 0,
         uCount   = 0,    uAvg   = 0;

     $.each(self.sourceItems(), function(ind, val) {
         if (val.type == 'critic') {
             crCount++;
             crAvg += parseFloat(val.score);
         } else if (val.type == 'user') {
             uCount++;
             uAvg += parseFloat(val.score);
         }
     });

     //set average to flash if there's no reviews otherwise calculate an avarage
     crCount ? self.criticAverage(crAvg / crCount) : self.criticAverage('/');
     uCount ? self.userAverage(uAvg / uCount) : self.userAverage('/');

     self.userCount(uCount);
     self.criticCount(crCount);

 }, app.viewModels.reviews, {deferEvaluation: true});

/**
 * Filters critic reviews on platform dropdown change,
 * if no reviews found fires an ajax request to query
 * review data provider.
 *
 * @return array
 */
app.viewModels.reviews.filteredReviews = ko.computed(function() {
    var self = this, filtered;

    //filter by user or critic reviews if user select either
    if (self.currentType() === 'all') {
        filtered = self.sourceItems();
    } else {
        filtered = ko.utils.arrayFilter(self.sourceItems(), function(rev) {
            return rev.type === self.currentType();
        });
    }

    //split current sort by camelCase into type and order params
    var sort = self.currentSort().match(/([A-Z]?[^A-Z]*)/g).slice(0,-1);

    if (sort.length === 2) {
        filtered.sort(app.utils.sort[sort[0]](sort[1]));
    }

    return filtered ? filtered : [];

}, app.viewModels.reviews, {deferEvaluation: true});

/**
 * New review form model.
 *
 * @type Object
 */
app.models.userReview = {
    id: ko.observable(),
    author: app.username,
    title: ko.observable(),
    source: 'Test',
    body: ko.observable(),
    story_rev: ko.observable(),
    animation_rev: ko.observable(),
    characters_rev: ko.observable(),
    sound_rev: ko.observable(),
    enjoyment_rev: ko.observable(),
    score: ko.observable(),
    type: 'user',
    _token: vars.token,
    user_id: app.user_id,
};


/**
* Renders CKeditor on textarea.
*
* @type {Object}
*/
ko.bindingHandlers.rev_ckeditor = {
        init: function (element, valueAccessor, allBindingsAccessor, context, review) {
                var $element = $(element);
                var value = ko.utils.unwrapObservable(valueAccessor());

                $element.html(value);
                var editor = CKEDITOR.replace('editor');

                /**
                * Resize CKeditor according to textarea col and rows attributes.
                *
                * @return void
                */
                jQuery.fn.cke_resize = function() {
                    return this.each(function() {
                            var $this = $(this);
                            var rows = $this.attr('rows');
                            var height = rows * 20;
                            $this.next("div.cke").find(".cke_contents").css("height", height);
                    });
                };

                CKEDITOR.on('instanceReady', function(){ $element.cke_resize(); });

                //Update body observable on ckeditor blur event
                editor.on('blur', function (e) {

                       var obs = app.models.userReview.body(review.body);

                        if (ko.isWriteableObservable(obs)) {
                                obs(e.editor.getData());
                        }
                });
        }
};

})(jQuery);

我对 CKEditor 不熟悉,所以我在这里可能是错的,但是许多所见即所得的编辑器不会仅仅因为你更改了底层 <textarea> 中的数据而自行更新,这就是你的 [=13] =] 绑定就可以了。我建议完全删除 value 绑定,并让编辑器绑定到所有工作。

现在,您的绑定似乎只处理编辑器对可观察对象的更改。我们还需要处理相反的方向并将可观察到的更改写入编辑器。根据 CKEditor 文档,这是通过 Editor#setData:

完成的
ko.bindingHandlers.rev_ckeditor = {
    init: function (element, valueAccessor, allBindingsAccessor, context, review) {
        var $element   = $(element),
            observable = valueAccessor();

        var editor = CKEDITOR.replace('editor');

        ko.computed(function() {
          editor.setData( observable() );
        }, { disposeWhenNodeIsRemoved: element });

        jQuery.fn.cke_resize = function () {
            return this.each(function () {
                var $this = $(this);
                var rows = $this.attr('rows');
                var height = rows * 20;
                $this.next("div.cke").find(".cke_contents").css("height", height);
            });
        };

        CKEDITOR.on('instanceReady', function () {
            $element.cke_resize();
        });

        editor.on('blur', function (e) {
            if (ko.isWriteableObservable(observable)) {
                observable(e.editor.getData());
            }
        });
    }
};

通常,人们会在绑定 update 处理程序中将对可观察对象的更改写回编辑器。在这种情况下,在 init 处理程序中进行更方便,因为我们已经可以访问那里的编辑器实例。

如果您授予编辑器对 observable 的绑定访问权限,一切都应该按预期工作:

<textarea data-bind="rev_ckeditor: app.models.userReview.body"></textarea>