Pull to refresh

Rich text editors from backend perspective

Website development *.NET *HTML *Angular *Data visualization
Welcome everyone, in this article I’m going to overview the most popular types of rich text editors, tradeoffs of their use from a backend perspective. By that I mean:

  • Streaming of content from the rich text editor to other infrastructure tools like full-text search, warehouses, etc.
  • Retrieving of content to clients: mobile, web, desktop.
  • Storing of content in some kind of storage (SQL database in my case)
  • Analyzing of content, which includes point 1, but also analyzing it from the perspective of our application

Probably you have already got the point, there is no “best” choice, it’s just a trade-off between your needs, operability, and budget of the project.

Why did I investigate it and explore this topic? I would like to build some kind of CMS for my needs and learn those possible choices, trade-offs, etc. So after building a little prototype application I can analyze potential problems related to rich text editors using and possible solutions to those problems.

Basically, there are 3 types of rich text editors for frontend, maybe I named them incorrectly, but you should get the meaning:

  1. Html rich text editor. And in my example, I’ll use CKEditor for Angular.
  2. Markdown rich text editors. In my example, I’ll use ngx-markdown-editor.
  3. JSON based rich text editors. I haven’t found a lot of them, but for the sake of example, I’ll consider contentful editor.
  4. My hybrid solution

Html Rich Text Editors. CKEditor


These editors allow you to type, edit your messages. Functionality varies depending on the particular editor, but they have a common type of data processing.

You don’t need to type anything that doesn’t relate to the text itself as for example with markdown. You are working with the content itself. And from the frontend the content is converted to raw HTML, therefore, probably in case of the absence of some kind of parser on the frontend side — your backend will receive raw HTML.

image
image

Let's consider the pros and cons of that.

1. Multiple platforms.

Basically, we’ll have problems with multi-platform usage. For example, I participated in a project which had rich text functionality and all content was sent to the backend in raw HTML form. But then, the product owner decided to add mobile to the project, and that was the point when problems arose.
Most of the problems were for mobile engineers because they had to figure out how to convert HTML to mobile markup. Yes — we have different libs and frames for that, but as I remember they also had to write custom parser behavior for different cases. For sure, the same problem would be for desktop, if the product owner decided to add a desktop to the project. Also, we should point, that implementation of different HTML rich text editors differs so much, and there is a problem with browser compatibility.

2. Other infrastructure processing.

Let’s consider a full-text search for that purpose. First of all, we are not sure that it will work correctly because it’ll parse HTML markup with content and we don’t know all the consequences. Also in the case of some warehouse — the same problem will arise, what if your analytics don’t want to see that raw HTML, they need raw text? For downloading purposes, if it is acceptable you can return HTML content in the file, or you’ll need to parse it on the backend as well as clients do.

3. Simplicity and budget.

But it has an advantage because the efforts you need to perform in order to implement it are minimal. Frontend needs to install it from repo by “npm install” and everything starts working. The backend doesn’t need to do anything, they just start saving and retrieving HTML content instead of raw content.

This is acceptable probably in the case when you don’t have any other infrastructure that works with this HTML content, and when you don’t need other platforms, for example, your mobile — is just another web version of the project.

Markdown rich text editor. ngx-markdown-editor


If you are a software engineer you have probably already used it, because stackoverflow used this rich text editor. You also have an ability to edit and type your text, but now, you should have deal with special characters, such as “*, #”.

image

image

Let's consider the pros and cons of that.

1. Multiple platforms.

Probably the same problem as it was with an HTML rich text editor. But in that case, it will be simpler to solve. All platforms are aware of markdown rules and can appropriately handle them, they probably anticipate all cases better than in the case of HTML rich text editors. Also, markdown is similar among all markdown editors, and in case of exchanging one editor with another — it shouldn’t impact you much.

2. Other infrastructure processing.

There is still a problem with streaming the data to other infrastructure items like full-text search, but in markdown cases, it’s easier to parse it. For downloading purposes, if it is acceptable you can return markdown content, or you’ll need to parse it on the backend as well as clients do.

3. Simplicity and budget.

It is also pretty simple to implement. I’m not sure regarding the libraries for clients (mobile, desktop), but even in the case of custom parsing, it isn’t so hard to implement. Markdown editor is the best for web-only applications without the additional infrastructure in the backend. I need to point, that the markdown editor doesn’t have some features that other editors have, the functionality is pretty poor.

JSON based rich text editor. Contentful


In my opinion, this is the cleanest approach. But on the other hand, it is either the most expensive or you’ll end up being too coupled to some library or provider.

image

Actually, contentful is just a platform that provides content management. I have not found a single text editor, but even if this type of editor exists it has some problems.

1. Multiple platforms.

A JSON based rich text editor has the same problems with portability as others, but now, editors provide clients with JSON, and clients can parse it as they want. Also, usually with this kind of editor clients will know every possible node and can implement full parsing and sleep well after the working day.

2. Other infrastructure processing.

For other infrastructure in the backend — now you have a lot of space for implementing what you want. You can implement custom streaming, by selecting what to stream where, etc. It costs, right, but now you can be sure about your implementation. For downloading purposes, if it is acceptable you can return JSON content, or since you have JSON and you know the structure — you can write behavior that parses this JSON.

3. Simplicity and budget

This type of rich text editor is the most expensive because you need to parse JSON on all clients including the backend (in case of streaming this data to some other providers like full-text search). But in my opinion, it’s the cleanest way to do it. You have still coupled to this editor parsing rules and node namings, as well as you, were with markdown and HTML, but now, you have complete documentation (at least you should have it) of some syntax, and it won’t differ depending on browser or something else. But if the text editor provider decides to drop it, then you won’t have updates for it, and maybe it will become not a workable solution.

My hybrid solution


As I don’t find any Json based free editors, and there was not enough markdown functionality, I decided to make a hybrid solution.

What is my solution? I use a rich text editor, but I implement parsers on the backend side so that I have my custom JSON structure inside the backend as well as raw HTML structure.

With that approach, I can retrieve the clients the format they want: HTML, JSON, even XML if needed. I don’t depend on other text editor providers (I mean in the case of JSON rich text editor provider), because I’m parsing it to my custom structure. Also, I have enough flexibility, because even if text editor implementation will change — it won’t affect guys who use JSON content type from the backend, because it remains the same, but yes, I would need to update the parsers.

As a trade-off — it costs because you need to implement parsing from HTML to JSON and vice versa along with tests. Let me show you an example:

Imagine we have some HTML structure:

image

and we pass it to our backend, and it will parse it to something like that:

image

Yes, sure JSON costs more space in your storage, but as I said, the choice of editor is about trade-offs.

Simple code that does this conversion using .net core.

public class HtmlToDomainRichTextParser
    {
        public (RootItem item, string plainContent) ParseFromHtml(string html)
        {
            var document = new HtmlDocument();
            document.LoadHtml(html);

            var root = new RootItem();

            foreach (var node in document.DocumentNode.ChildNodes)
            {
                var item = ParseHtmlNode(node);
                root.Children.Add(item);
            }

            return (root, document.DocumentNode.InnerText);
        }

        private ItemBase ParseHtmlNode(HtmlNode node)
        {
            var item = DefineItem(node);

            if (!node.HasChildNodes)
                return item;

            foreach (var child in node.ChildNodes)
            {
                var childItem = ParseHtmlNode(child);
                item.Children.Add(childItem);
            }

            return item;
        }

        private ItemBase DefineItem(HtmlNode node)
        {
            switch (node.Name)
            {
                case "#text": return new Text(HttpUtility.HtmlDecode(node.InnerText));
                case "a":
                {
                    var href = node.Attributes["href"].Value;
                    return new Link(href);
                }
                case "h2": return new H2();
                case "h3": return new H3();
                case "h4": return new H4();
                case "p": return new Paragraph();
                case "br":
                {
                    var dataLanguage = node.Attributes["data-cke-filler"].Value;
                    return new NewLine(dataLanguage);
                }
                case "ol": return new NumberList();
                case "ul": return new NotNumberList();
                case "li": return new ListItem();
                case "strong": return new Strong();
                case "i": return new Italic();
                case "blockquote": return new BlockQuote();
                case "pre":
                {
                    var dataLanguage = node.Attributes["data-language"]?.Value;
                    var spellCheck = node.Attributes["spellcheck"]?.Value;
                    return new Pre(dataLanguage, spellCheck);
                }
                case "code":
                {
                    var classAttribute = node.Attributes["class"].Value;
                    return new Code(classAttribute);
                }
                case "figure":
                {
                    var classAttribute = node.Attributes["class"].Value;
                    var contentEditableAttribute = node.Attributes["contenteditable"]?.Value;
                    return new Figure(classAttribute, contentEditableAttribute);
                }
                case "figcaption":
                {
                    var classAttribute = node.Attributes["class"].Value;
                    var dataPlaceHolderAttribute = node.Attributes["data-placeholder"].Value;
                    var contentEditableAttribute = node.Attributes["contenteditable"].Value;
                    return new FigCaption(classAttribute, dataPlaceHolderAttribute, contentEditableAttribute);
                }
                case "img":
                {
                    var src = node.Attributes["src"].Value;
                    return new Img(src);
                }
            }

            throw new Exception($"Cannot determine {node.Name} while parsing html.");
        }

and piece of the test as well as:

image

So as you see, there are trade-offs, and you need to pick one that fits your needs the best. Thank you for your attention. I would appreciate it if you leave your opinion and solution in the comments below.
Tags:
Hubs:
Total votes 1: ↑0 and ↓1 -1
Views 1.5K
Comments Comments 1