ASP.NET MVC HTML Editor With Quill

2023-02-26

說明如何使用 Quill 作為 Html Edtior,提供使用者在後台介面輸出資料、維護內容的便捷方式。相較於老牌的 CKEditor 以及 TinyMCE,本次所要介紹的新世代的 Html Editor Quill

logo

說明

GitHub

View (Html Partial)

在需要使用的 View 使用相關的 css 以及 js,示範使用 cdn 的方式,也可以使用 Client Side Library Manager (LibMan) 的方式管理與使用。

由於使用會需要以 js 註冊 Editor 以及將 Html 元素賦值對照的關係,因此以 HTML Partial 的方式方便反覆使用。

_QuillHtmlEditor.cshtml

<!-- Include the Quill Styles -->
<link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet" />

<!-- Include the Quill library -->
<script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quill-image-resize-module@3.0.0/image-resize.min.js"></script>

<!-- Initialize Quill editor -->
<script>
    var toolbarOptions = [
        ['bold', 'italic', 'underline', 'strike'],
        ['blockquote', 'code-block'],
        [{ 'color': [] }, { 'background': [] }],
        [{ 'header': 1 }, { 'header': 2 }],
        [{ 'list': 'ordered' }, { 'list': 'bullet' }],
        [{ 'script': 'sub' }, { 'script': 'super' }],
        [{ 'indent': '-1' }, { 'indent': '+1' }],
        [{ 'direction': 'rtl' }],
        [{ 'size': ['small', false, 'large', 'huge'] }],
        [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
        [{ 'align': [] }],
        ['image'],
        ['clean']
    ];

    // 註冊 Quill 在 #editor Element
    var quill = new Quill('#editor', {
        modules: {
            toolbar: toolbarOptions,
            imageResize: {
                displaySize: true
            },
        },
        placeholder: 'Enter some text here...',
        theme: 'snow'
    });

    // 加入 Listener 處理 Quill 編輯的結果至 Form Input
    document.addEventListener("DOMContentLoaded", function () {
        document.querySelector('form').addEventListener('submit', function (event) {
            document.querySelector('#Content').value = quill.root.innerHTML;
        });
    });
</script>

Create.cshtml

在 Create 的 View 當中,需要加入 #editor 的元素,讓 Quill 渲染為 Html Editor,原本應該用於提供使用者輸入的 Input Content 則刪除,並透先前的註冊,在 Form Submit 時做對照的賦值,提供後端進行使用。

@model NorthwindShop2.Web.Models.CMS.Article

@{
    ViewBag.Title = "Create";
}

<h2>New Article</h2>
<hr />

@using (Html.BeginForm("Create", "Article", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group mb-3">
            @Html.LabelFor(model => model.Content, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                <div id="editor" class="fs-6">
                </div>
                @Html.HiddenFor(model => model.Content)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Attachments, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                <input type="file" name="attachments" multiple class="form-control" />
                @Html.ValidationMessageFor(model => model.Attachments, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-outline-primary me-3" />
                @Html.ActionLink("Back to List", "Index", null, new { @class = "btn btn-outline-secondary me-3" })
            </div>
        </div>
    </div>
}

@Html.Partial("_QuillHtmlEditor")

Edit 在 View 的差別是將已經存在的 Content 使用 Html.Raw 的方式加入倒 #editor 的 innerHtml。

Edit.cshtml

<div class="form-group mb-3">
    @Html.LabelFor(model => model.Content, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        <div id="editor">
            @Html.Raw(Model.Content)
        </div>
        @Html.HiddenFor(model => model.Content)
    </div>
</div>

Controller

ArticleController.cs

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
  [Bind(Include = "Title,Content")] Article article,
  IEnumerable<HttpPostedFileBase> attachments)
{
    article.DatePublished = DateTime.Now;
    if (ModelState.IsValid)
    {
        db.Articles.Add(article);
        db.SaveChanges();

        foreach (var file in attachments)
        {
            if (file != null && file.ContentLength > 0)
            {
                var attachment = new Attachment
                {
                    FileName = Path.GetFileName(file.FileName),
                    FileType = file.ContentType,
                    ArticleId = article.Id
                };
                using (var reader = new BinaryReader(file.InputStream))
                {
                    attachment.FileData = reader.ReadBytes(file.ContentLength);
                }
                db.Attachments.Add(attachment);
            }
        }
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(article);
}

Model

Html Editor 會製作出 Html Tag 的內容,因此 Model 需要設定 AllowHtml

Article.cs

public class Article
{
    public int Id { get; set; }
    public string Title { get; set; }
    [AllowHtml]
    public string Content { get; set; }
    public DateTime DatePublished { get; set; }
    public virtual List<Attachment> Attachments { get; set; }
}

後續行動

相對於 TinyMCE 的持續更新,目前 Quill 最新的更新是在 2019,也考慮到 TinyMCE 的普及以及社群的討論熱度,未來打算在驗證使用 TinyMCE 結合 ASP.NET MVC 的方式。

TinyMCE Quill
Functions 支援多種文本格式、表格、媒體等元素的編輯和管理 支援常用的文本格式和樣式,也可擴展功能
Extensionality 提供了豐富的 API 和事件處理函數,可以方便地進行自定義和擴展 使用模組化架構,也可進行自定義和擴展
Usage 使用簡單直觀,支援多種語言和鍵盤快捷鍵 使用簡單直觀,支援多種語言和快捷鍵
Stability 歷史久、瀏覽器兼容性佳 使用 Virtual DOM 達到渲染快速的效能
License MIT License BSD 3-Clause "New" or "Revised" License

Comparison with Other Rich Text Editors | Quill

Quill vs TinyMCE | TinyMCE

參考連結

深入 slate.js x 一起打造專屬的富文字編輯器吧!| iThome

是佛心還是惡霸條例,了解GPL開源(Open Source)許可證的風險與感染性