# JavaScript

> Add the Document Editor and Document Viewer to pages without a framework: scripts, a backend on another origin, fetch, both on one page and dynamic pages.

- **Product:** TX Text Control .NET Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET Server > Integration Guide > JavaScript
- **4501 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/javascript/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/javascript/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/javascript/llms-full.txt

---

Any web page can show the Document Editor and the Document Viewer, whether a web server delivers it as a static file or another server technology renders it. The page needs no framework and no npm package: it loads the scripts of both components from an ASP.NET Core backend and talks to them through their JavaScript APIs.

[Document Editor: JavaScript](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/document-editor/javascript/llms-full.txt) and [Document Viewer: JavaScript](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/document-viewer/javascript/llms-full.txt) start with one component per page and describe the initialization settings of the editor and the settings of the viewer element. What follows connects such pages to a backend on another origin, exchanges documents with it, combines both components and creates and removes the editor and the viewer at run time.

### Where the Scripts Come From

| Component | Script | Served by |
|---|---|---|
| Document Editor | `https://<backend>/api/TXWebSocket/GetResource?name=tx-document-editor.min.js` | The WebSocket middleware of the backend |
| Document Viewer | `https://<backend>/TextControl/GetResource?Resource=minimized.tx-viewer.min.js` and `https://<backend>/TextControl/GetResource?Resource=minimized.tx-viewer-component.min.js` | The `TextControl` controller of the viewer package |

The editor script provides the global [TXTextControl](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/txtextcontrol/llms-full.txt) object with [init](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/txtextcontrol/init-method/llms-full.txt), which creates the editor in a container element. The viewer scripts provide the `tx-document-viewer` web component and the [TXDocumentViewer](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript-document-viewer/txdocumentviewer/llms-full.txt) API.

### Prepare the Backend

Set up the backend as described in [Configure Program.cs for Separate Frontends](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/aspnet-core/#configure-program-cs-for-separate-frontends) and add the controller of [Add a Document Controller](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/aspnet-core/#add-a-document-controller). Place a *template.docx* in the *Documents* folder of the backend.

- **CORS.** A page on another origin calls the viewer and your controllers with HTTP requests, so the CORS policy of the backend must allow the origin of the page, for example `http://localhost:8080`. The Document Editor needs no CORS policy.
- **Addresses.** The examples assume that the backend runs on `https://localhost:7118`. Replace the port in every URL with the HTTPS port of your backend, and use `ws` instead of `wss` when the backend runs on HTTP.

### Load and Save Documents From a Page

The following page loads the template from the backend when the editor is ready and saves the document back as *output.docx*:

```html

<html>
<head>
  <meta charset="utf-8" />
  <title>Document Editor With a Separate Backend</title>
  <script src="https://localhost:7118/api/TXWebSocket/GetResource?name=tx-document-editor.min.js"></script>
  <style>
    #txDocumentEditor { width: 1000px; height: 600px; }
  </style>
</head>
<body>
  <div id="txDocumentEditor"></div>
  <input type="button" onclick="saveToServer()" value="Save Document" />
  <input type="button" onclick="loadFromServer('output.docx')" value="Load Saved Document" />

  <script>
    const backendUrl = "https://localhost:7118";

    TXTextControl.init({
      containerID: "txDocumentEditor",
      webSocketURL: "wss://localhost:7118/api/TXWebSocket"
    });

    TXTextControl.addEventListener("textControlLoaded", function () {
      loadFromServer("template.docx");
    });

    function loadFromServer(name) {
      fetch(backendUrl + "/Document/Load?name=" + encodeURIComponent(name))
        .then(response => response.json())
        .then(stored => {
          TXTextControl.loadDocument(TXTextControl.StreamType.WordprocessingML, stored.data);
        });
    }

    function saveToServer() {
      TXTextControl.saveDocument(TXTextControl.StreamType.WordprocessingML, function (result) {
        fetch(backendUrl + "/Document/Save", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ name: "output.docx", data: result.data })
        });
      });
    }
  </script>
</body>
</html>
```

The code works as follows:

- The `script` element loads the editor from the backend, and `init` creates the editor in the `div` with the ID `txDocumentEditor`.
- [addEventListener](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/txtextcontrol/addeventlistener-method/llms-full.txt) waits for the `textControlLoaded` event before the page loads the template.
- `loadFromServer` requests a document from the controller and passes the base64-encoded data to [loadDocument](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/txtextcontrol/loaddocument-method/llms-full.txt).
- `saveToServer` saves the document with [saveDocument](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/txtextcontrol/savedocument-method/llms-full.txt) and posts [SaveDocumentResult.data](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/savedocumentresult/data-property/llms-full.txt) to the controller.

Open the page from a web server whose origin the CORS policy allows. [Loading and Saving Documents](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-editor/load-save/llms-full.txt) describes the formats and callbacks.

### Show the Editor and the Viewer on One Page

Both components can share a page, for example to preview a template in the viewer while it's edited:

```html

<html>
<head>
  <meta charset="utf-8" />
  <title>Document Editor and Document Viewer</title>
  <script src="https://localhost:7118/api/TXWebSocket/GetResource?name=tx-document-editor.min.js"></script>
  <script src="https://localhost:7118/TextControl/GetResource?Resource=minimized.tx-viewer.min.js"></script>
  <script src="https://localhost:7118/TextControl/GetResource?Resource=minimized.tx-viewer-component.min.js"></script>
  <style>
    #txDocumentEditor, #viewerContainer { width: 800px; height: 600px; }
  </style>
</head>
<body>
  <div id="txDocumentEditor"></div>
  <input type="button" onclick="showPreview()" value="Preview" />

  <div id="viewerContainer">
    <tx-document-viewer id="viewer1" settings='{"dock":1, "basePath":"https://localhost:7118"}'>
    </tx-document-viewer>
  </div>

  <script>
    TXTextControl.init({
      containerID: "txDocumentEditor",
      webSocketURL: "wss://localhost:7118/api/TXWebSocket"
    });

    function showPreview() {
      TXTextControl.saveDocument(TXTextControl.StreamType.InternalUnicodeFormat, function (result) {
        document.getElementById("viewer1").loadDocument(result.data, "preview.tx");
      });
    }
  </script>
</body>
</html>
```

The code works as follows:

- The page loads the script of the editor and the two scripts of the viewer from the same backend.
- `showPreview` saves the document of the editor in the internal format of TX Text Control and loads it into the viewer.
- The page calls [loadDocument](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript-document-viewer/documentviewercontroller/loaddocument-method/llms-full.txt) on the `tx-document-viewer` element. Each viewer element has its own instance, so a page with several viewers addresses each one by its element; the global `TXDocumentViewer` refers to the viewer initialized last.

A server endpoint can merge the template with data before the preview, as [Show the Editor and the Viewer in One View](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/aspnet-core/#show-the-editor-and-the-viewer-in-one-view) shows with a controller and `MailMerge`.

### Create and Remove the Editor at Run Time

On pages that build their content dynamically, add the editor only when it's needed and remove it cleanly when it's no longer shown. The page loads the script at run time, creates the editor in its `onload` handler and removes both again:

```html
<div id="editorHost" style="width: 800px; height: 600px;"></div>
<button onclick="createEditor()">Add Editor</button>
<button onclick="removeEditor()">Remove Editor</button>

<script>
  const scriptUrl = "https://localhost:7118/api/TXWebSocket/GetResource?name=tx-document-editor.min.js";

  function createEditor() {
    const script = document.createElement("script");
    script.src = scriptUrl;
    script.onload = function () {
      TXTextControl.init({
        containerID: "editorHost",
        webSocketURL: "wss://localhost:7118/api/TXWebSocket"
      });
    };
    document.head.appendChild(script);
  }

  function removeEditor() {
    const script = document.querySelector("script[src='" + scriptUrl + "']");
    if (script) script.remove();

    if (typeof TXTextControl !== "undefined") TXTextControl.removeFromDom();
  }
</script>
```

The code works as follows:

- `containerID` names the container element and is required, also when [containerElement](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/componentsettings/containerelement-property/llms-full.txt) passes the element itself. Of the two, `containerElement` takes precedence.
- [removeFromDom](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/txtextcontrol/removefromdom-method/llms-full.txt) closes the WebSocket connection gracefully and removes the editor, its event handlers and its CSS references. Save the document before, because unsaved content is lost.
- Removing the `script` element as well lets `createEditor` load the script and create the editor again.

Instead of creating the editor again, a page can also keep one editor and move its container between places, for example between tabs. It saves the document of one place, resets or loads the content for the next one and fires a `resize` event after the move. The blog article [Reuse Document Editor Instances by Dynamically Moving them in the DOM](https://www.textcontrol.com/blog/2023/10/02/reuse-document-editor-instances-by-dynamically-moving-them-in-the-dom/) implements this approach.

### Remove and Recreate the Document Viewer

The Document Viewer has no method that closes it. Remove its `tx-document-viewer` element from the page to take the viewer away, and add a new element to show a viewer again:

```javascript
function removeViewer() {
  document.getElementById("viewer1").remove();
}

function createViewer() {
  const viewer = document.createElement("tx-document-viewer");
  viewer.id = "viewer1";
  viewer.setAttribute("settings", JSON.stringify({ dock: 1, basePath: "https://localhost:7118" }));
  document.getElementById("viewerContainer").appendChild(viewer);
}
```

A new element initializes a new viewer as soon as it's added to the page and fires the `documentViewerLoaded` event again, after which `loadDocument` on the element loads a document. The element reads its `settings` only once, so set them before you add the element. Moving an existing element to another place in the page also initializes its viewer again.

### Pass Access Tokens

When the backend protects its endpoints with access tokens, the page gets a token from the backend first. What it can do with it differs between the two components:

- **Document Editor.** [DocumentEditorSettings.webSocketProtocols](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/documenteditorsettings/websocketprotocols-property/llms-full.txt) sends the token as an additional WebSocket sub-protocol instead of in the URL, as [Pass Tokens Without the URL](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/websockets/#host-the-connection) shows.
- **Document Viewer.** The settings of the `tx-document-viewer` element have no entry for a token, and the JavaScript API of the viewer documents none. Serve the page from the origin of the backend and protect the viewer with the sign-in cookie of the user instead, as [Frontends on Another Origin](https://docs.textcontrol.com/tx-text-control-dotnet-server/security/authentication/#frontends-on-another-origin) describes. A token in the query string of `BasePath` is documented for the HTML helper of an ASP.NET Core view, which [Protect the Endpoints](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-viewer/architecture/#protect-the-endpoints) shows.

Tokens in URLs can appear in the logs of web servers and proxies. [Access Tokens](https://docs.textcontrol.com/tx-text-control-dotnet-server/security/access-tokens/llms-full.txt) covers the server side.

### Node.js Applications

A Node.js application can render the editor on the server with the `@txtextcontrol/tx-document-editor` package. Its `render` method writes the same `script` element and `init` call that a static page contains, and the `@txtextcontrol/tx-websocket-server` package or an ASP.NET Core backend with `TXTextControl.Web` provides the WebSocket endpoint. [npm Packages: Document Editor](https://docs.textcontrol.com/tx-text-control-dotnet-server/installation/npm/document-editor/#the-backend-for-node-js) describes the packages and their backend.

### Next Steps

- [WebSocket Architecture](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/websockets/llms-full.txt): the connection between the editor and the backend in detail.
- [JavaScript API](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-editor/javascript-api/llms-full.txt): the API of the Document Editor.
- [JavaScript API](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-viewer/javascript-api/llms-full.txt): the API of the Document Viewer.
- [REST Services](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/rest-services/llms-full.txt): call document services from the page.

---

## Nearby

- **Previous:** [React](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/react/llms-full.txt)
- **Next:** [Blazor](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/blazor/llms-full.txt)
