Building a Rich Text Editor: Day 4

Basic Writing Experience

Shikhar Vaish
Geek Culture

--

Editing Events

Index

It might look like some complex electronics circuit but this simplifies a lot of coding.

The arrows between blocks tell us that the source is going to trigger the destination block. In this case, the block is going to trigger pageOverflow the handling mechanism through checkAndHandleOverflow the method of parent Page.

The text over the connecting arrows represents the “Events” when this is going to happen.

This is going to make it easier to handle and predict edge cases. Why? Insert Character(s) can take place due to a simple keypress or maybe when a user pastes content from the clipboard.

Problems with Tight Coupling

Consider the following scenario.

There are 3 menus that can cause data formatting. One menu is a generic header that contains a list of tools, the second is a popup that lets users quickly choose a set of fonts and the third is simply the “last text format” chosen by the user. All 3 menus or widgets trigger the same behavior on the block: formatData. Now, let's say I want to add another feature that lets users see a list of the last 50 data formats that have been executed. And clicking on any of these will trigger the same formatting.

It would become tedious to make the block track each widget that is going to cause data formatting. Instead, it will watch for the event DataFormatEvent and execute it whenever it's done. So adding the history-tracker is easy as I could simply cause it to subscribe to this event without any data duplicates from multiple blocks.

How to implement Publisher Subscriber in Angular?

My implementation is a slightly modified version of this gist to handle referencing problem. I’ll build that existing solution and modify it that will help you understand the problem.

After creating a PubSub.ts file in util directory, I started with a subscribe-unsubscribe mechanism:

Simple Publisher Subscriber

In the subscribe method, the given callback function fn is inserted into the eventLog: events object. This keeps track of functions that have subscribed to an event.

In the unsubscribe method, all the fn methods are iterated to check for the matching function and remove it from the list.

Lastly, the emit method will simply iterate through all the fn methods and call them. Note that the functions are called through this PubSub object. This means that the functions being called are associated with this PubSub object.

Testing the PubSub

A test class

Let's create a demo class:

Here, the object subscribes to or enters a chat room called foo and every time a message is sent to the room, it executes its method increaseData.

The increaseData will simply increase the value of its data by 5. A simple operation and easy to monitor through console logs.

Generate a temporary angular component, write the following code and monitor the logs.

You’ll see the following error message:

Cannot read property data of undefined.

What’s happening here?

Further debugging this, if you print the this object through console.log(this), it’ll show up that it's a PubSub object. Since it was called through PubSub at its source, it got associated with it.

To fix this, I added another attribute to keep track of the true source.

I created a quick wrapper model in the file util/PubSub/PubSubReceiver.ts:

And, update the util/PubSub/PubSub.ts:

In this case, the receiver function will be able to get the current object through event.subOb.

I’ll be following this guideline template to organize the code. Every Model file will have the following sections:

  1. Object Data: getter setter for object’s attributes. (keep in mind all attributes will be private or read-only)
  2. DOM Tools: directly interact with the webpage
  3. User Operations: high-level implementations of complex tasks
  4. Event Emitters: simply broadcast data to relevant events
  5. Event Handlers: handle external changes
  6. Utilities: quick handy tools

Developing Block Model

Now that we have a mind map ready, let's start with defining the events.

config/modeEvents.ts

Create a config/modelEvents.ts file and add the following content:

Moving on to Block Mode, start with the simple stuff: handling Object Data and DOM manipulation sections. Note that Block does not subscribe to any event according to the diagram. So, the constructor would remain empty here.

Block.ts

These tools would provide easy-to-use getter/setters to interact with the block. I’ve added abroadcastEvent method that I’ll build next:

Block.ts

This simple method would broadcast to all models that are connected with it.

pageRoom is the parent page’s private room.

I’ll define a simple method: characterInput() to represent basic text addition:

In the Event Handlers section of models/Block.ts file, add:

This would set up the basic Model to handle character input.

Developing Page Model

Before writing, prepare the 6 sections in the file through comments and add the following methods:

Initialization:

Page.ts

Note that the constructor has subscribed the current object to the events.

  1. Object Data Section

2. DOM Tools

This page overflow handling mechanism is purely arbitrary. The decision to split block at the middle is randomly decided. You may find better mechanisms to handle this. Although this is a rare scenario where there is a single block exists, you might want to split the block at 75% height or consider caret position and split at the current caret position.

Today, I’ll be skipping User Operations Section and move on to Events.

4. Event Emitters

This pretty similar to Block’s Event Emitter section.

5. Event Handlers

6. Utilities

I had to create an extra trigger for utility here to account for referencing problem in PubSub.

Developing Doc Model

Hopefully, you’ll get an idea of how I built these models piece by piece. I’ll share the Doc Model straightaway now, it should be easy to understand it through its sections:

The Easy Part Now :’)

Create a component: Document using angular cli and create a Document attribute:

In the corresponding.html file, add:

TADAAA

This would give you the following structure and play around typing on page until it overflows:

I’ve set the page width and height to A4 paper size here.

This should give you an idea how to go about developing the a software with so many data manipulations and edge cases. The idea of using pub/sub here came from robotics.

In swarm robotics, the bots have to constantly adjust to the sudden changes in surroundings that amount to extremely high number of edge cases. Robust algorithms are designed through events so each bot handles itself well and it becomes easy to add features to these bots.

I’ll be ending this series now. Hopefully, this helped you in some way.

Byee.

--

--