Skip to main content

DevExpress v24.2 Update — Your Feedback Matters

Our What's New in v24.2 webpage includes product-specific surveys. Your response to our survey questions will help us measure product satisfaction for features released in this major update and help us refine our plans for our next major release.

Take the survey Not interested

Add RichEdit to an Express.js Application

  • 4 minutes to read

This topic describes how to add the client-side RichEdit control to an Express application.

View Example: RichEdit jQuery Application

#Prerequisites

#Requirements

  • To use the RichEdit control in an Express application, you need to have a Universal, DXperience, or ASP.NET subscription.
  • Versions of the devexpress npm packages should be identical (their major and minor versions should be the same).

#How to Create an Express Application with RichEdit

#Install a RichEdit Package

The devexpress-richedit npm package references devextreme-dist as peerDependencies. The peerDependencies packages should be installed manually. This allows developers to control a version of the peerDependencies packages and guarantees that the package is installed once.

Install a RichEdit package with required peerDependencies:

  1. If the package.json file does not exist, create an npm configuration file as follows: npm init -y
  2. Install a RichEdit package with required peerDependencies:

    console
    npm i devextreme devextreme-dist devexpress-richedit --save
    

You can find all the libraries in the node_modules folder after the installation is completed.

#Create a RichEdit Bundle

Create a RichEdit Bundle.

console
cd node_modules/devexpress-richedit
npm i --save-dev
npm run build-custom
cd ../..

#Install the Express Package

console
npm i --save express body-parser

#Create the Express Server

Create the main.js file and populate it with the following content.

const path = require('path');
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

app.use(express.static('public'));
app.use(express.static(path.join('node_modules', 'devexpress-richedit', 'dist', 'custom')));
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies

app.post('/api/SaveDocument', function (req, res) {
    var fileAsBase64 = req.body.base64;
    var fileName = req.body.fileName;
    var format = req.body.format;
    var reason = req.body.reason;
    fs.writeFile(`${fileName}.${getDocumentExtension(format)}`, fileAsBase64, 'base64', (err) => { });
    res.sendStatus(200);
});

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`));

function getDocumentExtension(format) {
    switch (format) {
        case '4': return "docx";
        case '2': return "rtf";
        case '1': return "txt";
    }
    return "docx";
}

#Create the Main Page

Create the public/index.html file and populate it with the following content.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>RichEdit Express App</title>

    <link rel="stylesheet" type="text/css" href="dx.richedit.css" />
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="dx.richedit.js"></script>
    <script src="richedit-creator.js"></script>

    <script>
        $(document).ready(function () {
            createRichEdit('api/SaveDocument');
        });
    </script>
</head>
<body>
    <div id='rich-container' style="width: 100%; height: 900px"></div>
</body>
</html>

#Create RichEdit Object

Create the public/richedit-creator.js file and populate it with the following content.

/// <reference path="../node_modules/devexpress-richedit/dist/dx.richedit.d.ts" />
function createRichEdit(exportUrl) {
    const options = DevExpress.RichEdit.createOptions();

    options.bookmarks.visibility = true;
    options.bookmarks.color = '#ff0000';

    options.confirmOnLosingChanges.enabled = true;
    options.confirmOnLosingChanges.message = 'Are you sure you want to perform the action? All unsaved document data will be lost.';

    options.fields.updateFieldsBeforePrint = true;
    options.fields.updateFieldsOnPaste = true;

    options.mailMerge.activeRecord = 2;
    options.mailMerge.viewMergedData = true;
    options.mailMerge.dataSource = [
        { Name: 'Indy', age: 32 },
        { Name: 'Andy', age: 28 },
    ];

    // events
    options.events.activeSubDocumentChanged = () => { };
    options.events.autoCorrect = () => { };
    options.events.calculateDocumentVariable = () => { };
    options.events.characterPropertiesChanged = () => { };
    options.events.contentInserted = () => { };
    options.events.contentRemoved = () => { };
    options.events.documentChanged = () => { };
    options.events.documentFormatted = () => { };
    options.events.documentLoaded = () => { };
    options.events.gotFocus = () => { };
    options.events.hyperlinkClick = () => { };
    options.events.keyDown = () => { };
    options.events.keyUp = () => { };
    options.events.paragraphPropertiesChanged = () => { };
    options.events.lostFocus = () => { };
    options.events.pointerDown = () => { };
    options.events.pointerUp = () => { };
    options.events.saving = () => { };
    options.events.saved = () => { };
    options.events.selectionChanged = () => { };    
    options.events.customCommandExecuted = (s, e) => {
        switch (e.commandName) {
        case 'insertEmailSignature':
            s.document.insertParagraph(s.document.length);
            s.document.insertText(s.document.length, '_________');
            s.document.insertParagraph(s.document.length);
            s.document.insertText(s.document.length, 'Best regards,');
            s.document.insertParagraph(s.document.length);
            s.document.insertText(s.document.length, 'John Smith');
            s.document.insertParagraph(s.document.length);
            s.document.insertText(s.document.length, 'john@example.com');
            s.document.insertParagraph(s.document.length);
            s.document.insertText(s.document.length, '+1 (818) 844-0000');
            break;
        }
    };

    options.view.viewType = DevExpress.RichEdit.ViewType.PrintLayout;
    options.view.simpleViewSettings.paddings = {
        left: 15,
        top: 15,
        right: 15,
        bottom: 15,
    };

    options.autoCorrect = {
        correctTwoInitialCapitals: true,
        detectUrls: true,
        enableAutomaticNumbering: true,
        replaceTextAsYouType: true,
        caseSensitiveReplacement: false,
        replaceInfoCollection: [
            { replace: "wnwd", with: "well-nourished, well-developed" },
            { replace: "(c)", with: "©" }
        ],
    };
    // capitalize the first letter at the beginning of a new sentence/line
    options.events.autoCorrect = function (s, e) {
        if (e.text.length == 1 && /\w/.test(e.text)) {
            var prevText = s.document.getText(new DevExpress.RichEdit.Interval(e.interval.start - 2, 2));
            if (prevText.length == 0 || /^(\. |\? |\! )$/.test(prevText) || prevText.charCodeAt(1) == 13) {
                var newText = e.text.toUpperCase();
                if (newText != e.text) {
                    s.beginUpdate();
                    s.history.beginTransaction();
                    s.document.deleteText(e.interval);
                    s.document.insertText(e.interval.start, newText);
                    s.history.endTransaction();
                    s.endUpdate();
                    e.handled = true;
                }
            }
        }
    };  

    options.exportUrl = exportUrl;

    options.readOnly = false;
    options.width = '1400px';
    options.height = '900px';

    var richElement = document.getElementById("rich-container");

    var richEdit = DevExpress.RichEdit.create(richElement, options);

    var documentAsBase64 = "e1xydGYxXGRlZmYwe1xmb250dGJse1xmMCBDYWxpYnJpO319e1xjb2xvcnRibCA7XHJlZDB"
        + "cZ3JlZW4wXGJsdWUyNTUgO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NSA7fXtcKlxkZWZjaHAgXGZzMjJ9e1xzdHl"
        + "sZXNoZWV0IHtccWxcZnMyMiBOb3JtYWw7fXtcKlxjczFcZnMyMiBEZWZhdWx0IFBhcmFncmFwaCBGb250O317XCp"
        + "cY3MyXGZzMjJcY2YxIEh5cGVybGluazt9e1wqXHRzM1x0c3Jvd2RcZnMyMlxxbFx0c3ZlcnRhbHRcdHNjZWxsY2J"
        + "wYXQyXHRzY2VsbHBjdDBcY2x0eGxydGIgTm9ybWFsIFRhYmxlO319e1wqXGxpc3RvdmVycmlkZXRhYmxlfXtcaW5"
        + "mb31cbm91aWNvbXBhdFxzcGx5dHduaW5lXGh0bWF1dHNwXGV4cHNocnRuXHNwbHRwZ3BhclxkZWZ0YWI3MjBcc2V"
        + "jdGRcbWFyZ2xzeG4xNDQwXG1hcmdyc3huMTQ0MFxtYXJndHN4bjE0NDBcbWFyZ2JzeG4xNDQwXGhlYWRlcnk3MjB"
        + "cZm9vdGVyeTcyMFxwZ3dzeG4xMjI0MFxwZ2hzeG4xNTg0MFxjb2xzMVxjb2xzeDcyMFxwYXJkXHBsYWluXHFse1x"
        + "mczIyXGNmMFxjczEgRG9jdW1lbnQgdGV4dH1cZnMyMlxjZjBccGFyfQ==";
    richEdit.openDocument(documentAsBase64, 'DocumentName', DevExpress.RichEdit.DocumentFormat.Rtf);
}

#Launch the Application

Execute the node main.js command to launch the application.