Comments
Before a document is approved, reviewers discuss it with notes they place on a page and with comments on annotations. In the Document Viewer, the comments sidebar lists them, and your code reads the comments from the exported annotations.
The examples change the view of the ASP.NET Core tutorial, which already loads a note with a comment. Annotations introduces the annotation types.
How Comments Work
- Notes are annotations that users insert at any place of a page to leave a textual note, like sticky notes.
- Comments can be added to every annotation, not only to notes: to a text highlight, a stamp or a drawing, for example.
- Several comments can belong to one annotation, so that reviewers answer each other. Each comment stores the user who wrote it and the time.
- A status can be set for an annotation with a drop-down list in the comments sidebar, which offers values such as Accepted, Rejected and Closed. The exported JSON stores the status in lowercase, for example
accepted. - The comments sidebar lists the annotations with their comments. A click on an entry leads to its annotation, also on other pages.
Show and Read Comments
The following view opens the annotation toolbar and the comments sidebar, loads a note with two comments and lists all comments of the document when users click List Comments. Replace the content of Views/Home/Index.cshtml:
@using TXTextControl.Web.MVC.DocumentViewer
@using System.Text
<div style="width: 800px; height: 600px;">
@Html.TXTextControl().DocumentViewer(settings => {
settings.DocumentData = Convert.ToBase64String(
Encoding.ASCII.GetBytes("<strong>Sample Content</strong>"));
settings.Dock = DocumentViewerSettings.DockStyle.Fill;
}).Render()
</div>
<input type="button" id="listComments" value="List Comments" />
<pre id="commentList"></pre>
<script>
var jsonAnnotations = '[[{"pen":{"type":"comment","objectWidth":32,"objectHeight":32},"user":"Unknown User","location":{"x":127,"y":50},"time":1658908087097,"comments":[{"comment":"Welcome to Text Control","user":"Unknown User","date":1658908100339,"id":"c8d818ce-ff1e-4c0c-b78c-6ba95a98dede","status":"none"},{"comment":"Thanks, looks good.","user":"Second Reviewer","date":1658908200339,"id":"0b6a3f2e-6c1d-4a51-9a52-3d3b1f7f2c10","status":"none"}],"id":"feed80ff-1c94-4abf-9838-3e833faa4092","status":"accepted"}]]';
window.addEventListener("documentViewerLoaded", function () {
TXDocumentViewer.annotations.showToolbar(true);
TXDocumentViewer.annotations.showSidebar(true);
TXDocumentViewer.annotations.load(jsonAnnotations);
});
document.getElementById("listComments").addEventListener("click", function () {
// One array for each page, with the annotations of that page
var pages = JSON.parse(TXDocumentViewer.annotations.export());
var lines = [];
pages.forEach(function (annotations, pageIndex) {
annotations.forEach(function (annotation) {
annotation.comments.forEach(function (comment) {
lines.push("Page " + (pageIndex + 1) + " | " + comment.user + " | " +
new Date(comment.date).toLocaleString() + " | " + comment.comment);
});
});
});
document.getElementById("commentList").textContent = lines.join("\n");
});
</script>
The code works as follows:
- showSidebar opens the comments sidebar, and showToolbar the annotation toolbar with which users add notes and comments.
- load loads the note from the JSON string. Its pen has the type
comment, and itscommentsarray contains two comments of two users. - export returns all annotations as a JSON string. The outer array has an entry for each page, so the index plus 1 is the page number.
Run the application: the sidebar shows the note with the number of its comments. A click on the entry, or on the note in the page, opens the thread with both comments, a drop-down list for the status of the annotation and a box for a new comment:

The picture shows the same note on a letter instead of "Sample Content". Add a comment to an annotation and click List Comments to see it in the list.
The Structure of a Comment
In the exported JSON, each annotation has a comments array. Each comment is an object with these properties:
| Property | Contains |
|---|---|
comment | The text of the comment |
user | The name of the user who wrote it |
date | The time it was written, in milliseconds since January 1, 1970, as JavaScript's Date uses it |
id | A unique identifier |
status | The status of the comment, none in the examples |
The annotation itself also has a user, a time, an id and, when it is set, a status. In the examples of this page, the first user is Unknown User. Import and Export describes the complete format.
Load the Comments of a Document
Documents can contain comments of their own, which are part of the document rather than annotations. DocumentLoadSettings.LoadEmbeddedComments sets whether the viewer loads them as annotations, so that they appear in the comments sidebar:
@Html.TXTextControl().DocumentViewer(settings => {
settings.DocumentPath = "App_Data/template.docx";
settings.Dock = DocumentViewerSettings.DockStyle.Fill;
settings.DocumentLoadSettings.LoadEmbeddedComments = true;
}).Render()
When your code loads a document with loadDocument, pass the setting in the load settings, the fourth parameter:
TXDocumentViewer.loadDocument(documentData, "review.docx", null, { loadEmbeddedComments: true });
loadEmbeddedComments is the JavaScript name of the setting. The comments of documents in the Document Editor are described in Comments of the Document Editor.
Review Processes
In a review process, several users add annotations and comments to the same document, and each user's name is stored with their comments. Keep the document in an editable format, such as DOCX or the TX Text Control format, if it needs to be changed after the review. The blog articles DocumentViewer Collaboration Features: Annotations and Using Document Viewer Collaboration Features in Angular describe such processes.
Next Steps
- Import and Export: store the annotations and comments on your server.
- Highlights: comment on ranges of text.
- Events: react when annotations change.