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>
我有需要与 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>