ASP.NET MVC: Редактируем Html в CKEditor через Knockout
Сайтостроение | создано: 04.02.2013 | опубликовано: 04.02.2013 | обновлено: 13.01.2024 | просмотров: 13280 | всего комментариев: 11
В этой статье при помощи Knockout будем редактировать Html-код в WYSIWYG редакторе CKeditor.
Введение
Редактирование html-кода достаточно тривиальная задача. Если учесть, что количество WYSIWYG-редакторов в сети огромное количество, то единственным объяснением, почему именно взят именно CKeditor это то, что я с ним работаю уже более 6 лет, а еще этот редактор может в загружать файлы (например, картинок или флэш) на сервер (правда для этого используется модуль CKFinder).
Тестовый проект
Сразу к делу. Создаю новый проект. Mvc будет 4-ый, .Net пусть будет 4.5, потому как это не принципиально для Knockout. После того как проект создала Visual Studio запускаю команду обновления всех nuget-пакетов. Теперь когда проект почти готов, надо бы загрузить и установить сам редактор CKEditor. Иду по ссылки и качаю последнюю версию (на момент написания статьи версия 4.0.1). Распаковываю архив и (для экономии времени) закидываю всю папку ckeditor в корень проекта. По идеи можно было бы "почистить" немного папку, удалив файлы примера и ненужные плагины, а еще файлы справки (всё равно их никто не читает).

Классы и формы
Создаю новый класс, который будет использоваться… Не будет у меня никаких классов, ибо цель данной статьи показать другое. Ставим новый nuget-пакет, который называется JsSite:
PM> install-package jssite Successfully installed 'JsSite 0.3.0'. Successfully added 'JsSite 0.3.0' to CKEditor_Knockout. PM>
После установки этого пакета, в папке Scripts появилась еще одна папка App. В этот папке набор полезных файликов (немного описания можно найти в статье), которые я сделал, чтобы ускорить разработку проектов такого типа как этот. Просто и быстро. Далее...
Для начала удаляю содержание представления (view) в Home/Index.cshtml, потому что буду писать новую разметку под Knockout. А теперь немного кода на JavaScript.
/// <reference path="../knockout-2.2.1.debug.js" />
/// <reference path="site.services.js" />
/// <reference path="site.controls.js" />
/// <reference path="site.core.js" />
$(function () {
"use strict";
site.vm.homeIndex = function () {
var meta = new site.fw.Metadata("Демонстрация редактирования HTML",
"Данный CKEditor используется в связке с Knockout. Дополнительные инструкции в статье.",
"http://www.calabonga.net/blog/post/107"),
title = ko.observable("Этот текст редактируется!"),
html = title,
save = function () {
alert("Сохранилось! " + html());
};
return {
title: title,
save: save,
meta: meta,
html: html
};
}();
ko.applyBindings(site.vm.homeIndex);
});
В строке 6 создается viewModel для страницы, а в строке 24 осуществляется привязка.
В строке 10 создаем поле, которое я и собираюсь редактировать. А 11-ой я создаю временную переменную, дальше будет понятно.
Теперь немного много разметки. Вся форма (представление Index) целиком:
@{
ViewBag.Title = "Home Page";
}
<h1 data-bind="text: meta.title"></h1>
<p>
<span data-bind="text: meta.description"></span>
<a data-bind="attr: {href: meta.helplink}">
<img style="border: 0" src="~/Images/help.png" alt="" /></a>
</p>
<p><span data-bind="html: title"></span></p>
<p>
<input id="html" name="html" data-bind="ckeditor: html" type="text" value=" " />
</p>
<p>
<button data-bind="click: save">сохранить</button>
</p>
@section scripts
{
<script src="~/ckeditor/ckeditor.js"></script>
<script src="~/Scripts/knockout-2.2.1.js"></script>
<script src="~/Scripts/app/site.bindingHandlers.js"></script>
<script src="~/Scripts/app/site.core.js"></script>
<script src="~/Scripts/app/site.controls.js"></script>
<script src="~/Scripts/app/site.services.js"></script>
<script src="~/Scripts/app/site.vm.homeIndex.js"></script>
}
Ключевыми строчками кода является 13 (прекрасный номер для главной строки, не правда ли?) и строчка номер 23. В 13-ой посмотрите на атрибут data-bind и, соответственно, на ckeditor (код может скачать и посмотреть). В 24 строке как раз, тот самый custom binding.
Запускаем

Отобразился редактор – уже хорошо! Попробуем чего-нибудь написать в редакторе:

Работает!
Заключение
В качестве заключения хочется сказать следующее. У меня задача была редактировать только свойства типа ko.observable(), поэтому я сделал свойство html в моделе и делаю привязку к нему.
Редактировать простой текст простого свойства из Literal Object не получится.
Комментарии к статье (11)
Салабонга, подскажи пожайлуста как увязать CKEidtitor c моделью.
EF для вьюхи генерирует код эдитора
@Html.EditorFor(model => model.Field)
как сделать, чтоб из
<input id="html" name="html" data-bind="ckeditor: html" type="text" value=" " />
текст отправлялся в model.Field
Всё очень просто, камаз:
добавляешь ссылки на скрипты:
<script src="@Url.Content("~/ckeditor/ckeditor.js")"></script>
в разметке ставишь так:
<div class="editor-field">
@Html.TextAreaFor(model => model.Content, new { @class = "editor", id = "editorSmall" })<br />
@Html.ValidationMessageFor(model => model.Content)
</div>
и скрипты для подключения к разметке:
var editorel = $('#editorSmall').val();
if (editorel != undefined) {
var editor = CKEDITOR.replace('editorSmall', { toolbar: [['Bold', 'Italic', 'Underline', 'Strike']] });
$('form').submit(function (e) {
for (instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
var content = unescape(CKEDITOR.instances.editorSmall.getData());
if ($('form').valid() && content.length > 0) {
return true;
} else {
alert('Кажется, есть ошибки при заполнении формы!');
return false;
}
});
}
почему-то у меня не получилось, сделал всё как вы написали только остались непонятки со скриптом для подключения к разметке.. засовывал его и ваши .js файлы в папке app и подключал отдельным файлом, и даже вешал на страницу внутри тега <script>
я всё понял, спасибо огромнейшее))
кстати нашёл совсем тривилаьное решение
<script type="text/javascript">
CKEDITOR.replace('Content');
CKEDITOR.editorConfig = function (config) {
ignoreEmptyParagraph = true;
};
</script>
CKEDITOR.replace('Content');
прекрасно работает, вот только если потребуется "поиграться" с валидацией, придется воспользоваться тем методом, что предложил я... :)
Добрый день!
Ваш код не работает, если на странице много ckeditor'ов. Подскажите пожалуйста как исправить код ko.bindingHandlers.ckeditor ?
Я вообще не очень понимаю, что означает эта строчка:
options.editor = CKEDITOR.replace(element, options);
точнее зачем она нужна, зачем так сохранять эдитор?
rhot, Честно говоря, я не ставил перед собой задачу отображать на странице более одного редактора. Я запустил, проверил, действительно, не работает! На данный момент, к сожалению не располагаю достаточным количеством времени, чтобы заняться проблемой. Но как только, так сразу.
rhot, CKEDITOR.replace(element, options); - он заменяет textarea с id = element на ckeditor, то есть елы ты хошеь много ckeditorов, тебе нужно написать заменитель на каждый id контролла.
rhot
Вы всё правильно сделали поправив код. Дело в том что я хранил настройки в одном месте для всех инстансов, у меня не стояло задачи "иметь много на странице". Убрав этот функционал, вы "разрешили" использовать несколько инстансов на одной странице. Всё правильно.
Спасибо за пост.
Хочу добавить, чтобы бинд был "двустороний" и сразу подтягивался текст в редактор, то в bindingHandlers в update нужно дописать:
CKEDITOR.instances[element.id].setData(value);