An Angular Single Page Application (SPA) dynamically updates the web page without reloading. It loads all necessary resources once and uses client-side routing to provide a seamless user experience.
The Problem
In this context, because Angular SPAs handle routing on the client side without full page reloads, changing the route doesn't reload the app, but it can break the WebSocket connection that the TX Text Control Document Editor relies on, because the editor's connection is tied to the initial context and is not automatically reestablished across route changes.
The Solution
The solution is to treat the TX Text Control Document Editor as a persistent, long-lived component by keeping it in a stable part of the DOM. Instead of destroying and recreating the editor on every route change (which would break the WebSocket connection), the editor instance is physically moved around the DOM: When you navigate away from it, it's moved back into a hidden persistent container, and when you navigate to a new route that needs it, it's moved into the new view. This way, the same editor instance (and its WebSocket connection) is reused across routes without interruption.
The Implementation
The sample includes an Angular Single Page Application (SPA) that demonstrates how to reuse persistent document editor components. The project follows the standard Angular application structure. Below is an overview of its file and directory layout.
app/ | |
??? about/ # Component for the About page | |
??? guards/ # Route guards (e.g., to protect navigation) | |
??? home/ # Component for the Home page | |
??? persistent/ # Components/services related to persistent editor handling | |
??? app-routing.module.ts # Angular routing module | |
??? app.component.css # Styles for the root component | |
??? app.component.html # Template for the root component | |
??? app.component.spec.ts # Unit tests for the root component | |
??? app.component.ts # Root component class | |
??? app.module.ts # Main application module |
The persistent/ folder contains components related to the persistent document editor functionality.
persistent/ | |
??? persistent.component.html # HTML template for the persistent editor UI | |
??? persistent.component.ts # TypeScript logic |
The persistent.component.html file contains the Document Editor component, which is moved around the DOM as needed:
<div id="persistent-editor"> | |
<div id="moveable-editor"> | |
<tx-document-editor | |
width="1000px" | |
height="500px" | |
webSocketURL="wss://backend.textcontrol.com/TXWebSocket?access-token=yourtoken"> | |
</tx-document-editor> | |
</div> | |
</div> |
Guards
The guards/ folder contains logic to control navigation within the Angular app. It includes:
guards/ | |
??? can-component-deactivate.ts # Interface for components that can be deactivated | |
??? can-deactivate.guard.ts # Angular route guard implementation |
These files work together to implement a CanDeactivate guard. This guard prevents users from navigating away from a route without copying the editor instance back to the persistent DOM section.
Routing
Let's take a look at app.component.html, which contains the typical router-outlet and the app-persistent section. The router outlet section contains the dynamic Home and About components, while the persistent section always contains a placeholder for the editor.
The implementation of Home and About is the same and contains a placeholder for the editor:
<h2>Home</h2> | |
<p>This is the home view.</p> | |
<div id="editor-placeholder"></div> |
The Home component implements CanComponentDeactivate. This interface requires a canDeactivate() method that is called when the user tries to leave this path.
import { Component } from '@angular/core'; | |
import { CanComponentDeactivate } from '../guards/can-component-deactivate'; | |
declare const TXTextControl: any; | |
@Component({ | |
selector: 'app-home', | |
templateUrl: './home.component.html' | |
}) | |
export class HomeComponent implements CanComponentDeactivate { | |
async canDeactivate(): Promise<boolean> { | |
const success = await this.unloadEditor(); | |
return success; | |
} | |
async loadEditor(): Promise<boolean> { | |
const editor: HTMLElement | null = document.getElementById('moveable-editor'); | |
const placeholder: HTMLElement | null = document.getElementById('editor-placeholder'); | |
if (editor && placeholder) { | |
placeholder.appendChild(editor); | |
return true; | |
} | |
return false; | |
} | |
async unloadEditor(): Promise<boolean> { | |
const editor: HTMLElement | null = document.getElementById('moveable-editor'); | |
const placeholder: HTMLElement | null = document.getElementById('persistent-editor'); | |
if (editor && placeholder) { | |
placeholder.appendChild(editor); | |
// you should save your content here and the reset | |
TXTextControl.resetContents(); | |
return true; | |
} | |
return false; | |
} | |
ngOnInit() { | |
this.loadEditor(); | |
} | |
} |
When the component is loaded, the editor is moved in the DOM from the persistent placeholder to the placeholder of the Home component. And when the user navigates away from this component, the canDeactivate() method moves the editor back to the persistent section.
Demo
The following screencast shows the implementation of the persistent document editor in an Angular Single Page Application (SPA). The editor is moved around the DOM as needed, and the WebSocket connection remains intact through route changes.
The demo is available on GitHub and can be run locally.