slate-angualr officially open source

pubuzhixing
10 min readJun 25, 2021

Introduction

slate-angular is an editor view layer based on Angular and Slate.js, which helps developers use Angular + Slate.js to build a web rich text editor.

Slate.js

Slate.js is a particularly excellent rich text editor framework, with clean code, good structure, and strong scalability. Currently, there are countless rich text editors and products based on Slate.js on the market. And Slate.js is magical enough, from the beginning of the rich text editor development to about two years now, I feel that Slate.js has always been a good teacher for my growth, from the initial technical research to the development of the first version the editor, then the upgrade of the view layer architecture, and then the optimization of performance and stability, I can learn a lot from Slate.js at each stage, and of course I have a deeper and deeper understanding of it. I think Slate.js is an editor framework that is constantly iterating and improving itself, focusing on the view layer and the model layer. The architecture implementation has been closely following the development of the mainstream technology in the community, and the implementation mechanism of content editing is constantly changing to the current standards. It has been open source for about 5 years, the community is still very active, and it is constantly expanding more applicable scenarios, which is a very powerful existence.

Self-developed Angular view layer

Our front-end technology stack is Angular. There are very few rich text editor frameworks based on Angular in the open source community. The PingCode Wiki product we are going to do has extremely high requirements for the scalability of the rich text editor. The Slate editor framework is very In line with our needs, the core model layer does not rely on any front-end framework, is extensible, and has complete test coverage. Therefore, we try to develop the Angular-based view layer by ourselves. It can be understood that the core driver of the self-developed view layer comes from the product.

I personally feel that developing an Angular-based view layer is a very meaningful process, and the implementation of the Slate editor has not been developed using Angular in the current open source, so we want to open source our implementation to give back to the Slate and Angular communities and make more Many developers can develop rich text editors based on Angular + Slate.

The open source road

Currently slate-angular has supported the enterprise-level knowledge base product PingCode Wiki which has been running stably for more than 1 year. The initial version is based on Slate@0.47.0 (JavaScript version Slate), and the second version is based on the latest Slate implementation (TypeScript). It is the third edition, which has been prepared since the beginning of the year, including publicizing the Github repository, unifying the underlying implementation and API style, building an online demo, supplementing unit tests, upgrading the latest Slate, etc.

Live demo example :

github:https://github.com/worktile/slate-angular

demo :http://slate-angular.ngnice.com/

slate-angular may not be an out of the box rich text editor. It belongs to a view layer of the Slate framework and an intermediate layer that connects the functions of Slate Core and the rich text editor. It only provides basic rich text editing capabilities and functional expansion slots, but this is also the advantage of slate-angualr. It only focuses on the implementation of the basic layer: compatible browsers, mobile terminals, proxy input events, proxy cursors, support for customization Element/Text/Leaf node rendering, processing basic interaction, etc. Based on it, rich text editors like Quill and Prosemirror can be quickly developed, and its scalability is higher. After a certain period of time It is possible to build rich text editors with powerful functions like Confluence, Notion, and PingCode Wiki.

Features

  1. Support element front and rear cursor scheme

Support to extend the cursor before and after rendering the specified element. It is convenient for users to insert paragraphs before and after block elements (the basic interaction of block cursors has been implemented in the table Demo)

2. Support custom component/template rendering Element

Supports custom rendering of multi-level element content, which can meet the needs of complex scenes such as tables, while maintaining the correct dependency injection chain between custom components. For example, cell components can obtain table component services through constructor injection

3. Support custom component/template to render Text

Supports the content rendering of custom Text nodes. This is not provided in slate-react, but it is proposed separately in slate-angular, mainly used to achieve the requirements of bold, italic, underline, color, background color, etc.

4. Support custom component/template rendering Leaf

Supports the rendering of custom Leaf nodes. Leaf is the splitting of Text. Each Text node corresponds to a Leaf by default. The basis for splitting Leaf is Decoration, which is mainly used to decorate the text. Realize search highlighting, word comment and other requirements together with custom Leaf components

5. Support decorate decoration

Provide dynamic decoration of text content, which is driven by external data to locate and decorate text content. Its characteristic is to realize the decoration of the text without changing the original data, which is a way to deal with dynamic demands.

6. Support void element

Void means non-editable, and all content belongs to a whole. slate-angular supports the expansion of Void elements. Through Void elements, arbitrarily complex Angular components can be embedded into the editor, such as pictures, code editors, Gantt charts, etc. Embedded in the editor content area.

Compatible browser

Chrome、Edge、Safari、Firefox、QQ Browser

Solved common Slate.js compatibility issues

  1. Chinese input duplication problem
  2. Chinese input crash
  3. Safari browser input Chinese focus jumps
  4. \n cause content confusion
  5. <a> tag causes content confusion

These issues have been resolved in slate-angular, so I won’t explain more here. If you have any questions, please submit Issues to slate-angular.

Technical route

Next, let’s talk about technology-related content. In fact, I have always wanted to analyze the architecture design and internal mechanism of Slate.js, so I will use this open source to talk to you about the technical routes of slate.js and slate-angular. Incoluding important mechanisms in slate-angular implementation.

Let’s start with the Slate architecture:

The core of the Slate framework mainly includes a model layer and a view layer. The model layer defines the basic data structure describing rich text content (a node tree that supports nesting) and basic operations on the data. The view layer interfaces with the front-end framework to process basic input behaviors, selection proxy, content rendering, plugin extensions, etc.

It is worth mentioning that Slate’s data model definitions are all implemented with reference to the DOM standard, which is friendly to novices. For example, the concepts of data model Block, Inline-block, Text, etc. are consistent with the meaning in the DOM, and the selection area also has Selection, Anchor, Focus, Collapsed and other concepts.

Slate rich text editor architecture overview:

Develop rich text editor based on Angular

As an independent view layer, slate-angular is a bridge between Slate Core and the editor function. Its core function is to apply the characteristics of the front-end framework to the editor development, and better organize the development of the editor function.

The development of rich text editor based on slate-angular can be said to be the original Angular style. Whether it is basic function modification or extension of new functions, you can use Angular components or services to organize code instead of using Angular components to JavaScript The library is simply packaged:

You can customize the rendering of plugins using Angular components or templates.

You can encapsulate complex interactions based on Angular components.

You can reuse the Angular component library.

You can also use services to share data between parent and child node components, which maintain the correct dependency injection chain.

In short, you can use all the features of Angular.

Basic principles of content editing

Under the current technical framework, there are probably two implementation ideas for controlling input content. One is event proxy, and the other is monitoring content changes. Slate mainly uses event proxy, which is to monitor a series of content input DOM events. , And then judge the data operation corresponding to the input through the event type and other contexts, and finally convert it into a series of operations for the data model.

The way to monitor content changes is also useful in Slate, to support the Android browser, probably because the input events of some scenes under the Android browser cannot be captured correctly, and thus cannot respond to user operations, so use MutationObserver to monitor content changes In order to correctly respond to the user’s input behavior.

Because the implementation of the event proxy in the view layer is mainly to deal with input events, the implementation of input events in each browser is not completely unified, and it is necessary to distinguish between English input and Chinese IME input, so it is necessary to do a lot of compatibility processing for different browsers. Said to be a set of combined implementations, a general overview:

  1. Ideally: use the beforeinput event to complete the basic input proxy, because beforeinput is semantically clear and can be used as a basis for judging input behavior.
  2. Non-ideal situation: the browser does not support the beforeinput event, use React’s synthetic event onBeforeInput to process English input (you need to implement it yourself in Angular), and use keydown event processing for other input interactions such as break and delete.
  3. IME input processing uses the event compositionstart and compositionend processing, these three events are very reliable, without any browser compatibility issues.
  4. In addition, undo/redo, focus movement, etc. are handled in the keydown event. The logic of copying, cutting, etc. can use the native copy and cut events, and the logic of pasting, dragging and dropping depends on the beforeinput event as the basic input. Browsers that do not support beforeinput events are handled in events such as paste and drop.

Event proxy process schematic:

Selection sync

Like the selection of the browser, Slate’s data model also requires selection, which identifies the location of the data modification when the data changes. And this location needs to be consistent with the browser’s native selection. Whether the selection of the browser has changed or the selection of Slate has changed, it needs to be synchronized with each other.

The following describes the two-way synchronization mechanism of the selection in the slate-angular view layer:

一、DOM Selection -> Slate Selection:

Monitor the selectionchange event of the native Document object, query the corresponding Slate Selection when the DOM Selection changes, and modify the Slate Selection to be consistent with the DOM Selection.

Interactive behavior -> DOM Selection change -> selectionchange -> sync Slate Selection

Interactive behaviors include mouse Click, arrow keys, etc.

二、Slate Selection -> DOM Selection:

The Slate data Change causes the Slate Selection to change, which needs to be handled in the Change event. According to the latest Slate Selection, query the corresponding DOM Selection, and modify the DOM Selection to be consistent with the Slate Selection.

Interactive behavior -> trigger data update -> new Slate Selection -> view refresh -> sync DOM Selection

Plugin extension

Slate uses plugins to extend editor functions, and plugins are first-class (slate-angular is also a basic plug-in), and any advanced interaction can be achieved through plugins.

一、Overridable method

The bottom layer of Slate abstracts one by one overridable methods (deleteBackward, insertBreak, insertText, apply, etc.) for external extension. For example, if I want to recognize the Markdown data format when pasting, I can override the insertText implementation, and the special processing of carriage return can override insertBreak. Compared with directly exposing basic events, providing an overridable method is a very advanced implementation. In the slate-angular view layer, several overridable methods are also provided separately: insertData (processing paste data), isBlockCard (block card), onError (error handling), onKeydown (basic event).

二、Custom rendering

The UI part of the view layer is mainly composed of three layers of rendering, corresponding to three levels of data: Element, Text, and Leaf. The data of each level supports custom component/template rendering, mainly through renderElement, renderText, and renderLeaf.

Overview of the process of custom rendering components in the view layer:

This part mainly introduces several parts related to slate-angular: component development editor, event proxy, selection synchronization, plugin extension, etc. The core is still hope that you can learn more about slate.js and slate-angular, technology The content ends, and those who are interested can read the source code or other technical information.

The end

The rich text editor is a very complex field in the front-end. The road ahead is still very long. We also hope that more developers can become a contributors to discover and resolve issues such as browser compatibility, mobile compatibility, IME input, and standards Interaction issues, optimizing the mechanism of input proxy, optimizing the architecture, exploring collaboration solutions based on Slate, and so on.

If you have any questions, you are welcome to submit Issues or PRs to slate-angular!

--

--